diff --git a/.circleci/config.yml b/.circleci/config.yml index 3155dcb7586..84ac475ac90 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -75,7 +75,7 @@ jobs: generate-config: docker: - image: aztecprotocol/alpine-build-image - resource_class: xlarge + resource_class: large steps: - *checkout - *setup_env @@ -435,67 +435,52 @@ jobs: command: build noir-projects aztec_manifest_key: noir-projects - boxes-files: - machine: - image: default - resource_class: medium + yarn-project-pre-join: + docker: + - image: cimg/base:2023.09 + resource_class: small steps: - - *checkout - - *setup_env - run: - name: "Build" - command: build boxes-files - aztec_manifest_key: boxes-files + name: "Noop" + command: echo Noop - yarn-project-base: - machine: - image: default - resource_class: large + yarn-project-x86_64: + docker: + - image: aztecprotocol/alpine-build-image + resource_class: small steps: - *checkout - *setup_env - run: name: "Build" - command: build yarn-project-base | add_timestamps - aztec_manifest_key: yarn-project-base + command: cond_spot_run_build yarn-project 64 + aztec_manifest_key: yarn-project - yarn-project: - machine: - image: default - resource_class: large + yarn-project-arm64: + docker: + - image: aztecprotocol/alpine-build-image + resource_class: small steps: - *checkout - *setup_env - run: - name: Build - command: build yarn-project | add_timestamps + name: "Build" + command: cond_spot_run_build yarn-project 64 arm64 aztec_manifest_key: yarn-project - yarn-project-prod: + yarn-project-ecr-manifest: machine: image: default - resource_class: large - steps: - - *checkout - - *setup_env - - run: - name: Build - command: build yarn-project-prod | add_timestamps - aztec_manifest_key: yarn-project-prod - - yarn-project-formatting: - docker: - - image: aztecprotocol/alpine-build-image - resource_class: small + resource_class: medium steps: - *checkout - *setup_env - run: - name: Check Formatting - command: cond_spot_run_container yarn-project 8 formatting | add_timestamps + name: "Create ECR manifest" + command: create_ecr_manifest yarn-project x86_64,arm64 aztec_manifest_key: yarn-project - yarn-project-tests: + yarn-project-test: docker: - image: aztecprotocol/alpine-build-image resource_class: small @@ -503,9 +488,9 @@ jobs: - *checkout - *setup_env - run: - name: Test - command: cond_spot_run_container yarn-project 64 test | add_timestamps - aztec_manifest_key: yarn-project + name: "Build and test" + command: cond_spot_run_build yarn-project-test 64 + aztec_manifest_key: yarn-project-test aztec-package: machine: @@ -985,7 +970,7 @@ jobs: - *setup_env - run: name: "Test" - command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose-browser.yml TEST=e2e_aztec_js_browser.test.ts + command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_aztec_js_browser.test.ts aztec_manifest_key: end-to-end e2e-card-game: @@ -1151,9 +1136,6 @@ jobs: steps: - *checkout - *setup_env - - run: - name: "Copy docs dockerignore" - command: cp docs/.dockerignore . - run: name: "Build docs" command: | @@ -1317,14 +1299,14 @@ defaults: &defaults event: fail branch_pattern: "master" -defaults_yarn_project: &defaults_yarn_project +defaults_yarn_project_pre_join: &defaults_yarn_project_pre_join requires: - - yarn-project + - yarn-project-pre-join <<: *defaults -defaults_yarn_project_prod: &defaults_yarn_project_prod +defaults_yarn_project: &defaults_yarn_project requires: - - yarn-project-prod + - yarn-project-ecr-manifest <<: *defaults defaults_deploy: &defaults_deploy @@ -1418,8 +1400,6 @@ workflows: - yellow-paper: *defaults - - boxes-files: *defaults - - noir-projects: requires: - avm-transpiler @@ -1427,28 +1407,28 @@ workflows: <<: *defaults # Yarn Project - - yarn-project-base: + - yarn-project-pre-join: requires: - bb-js - noir-packages - <<: *defaults - - yarn-project: - requires: - - yarn-project-base - l1-contracts - noir-projects - - boxes-files <<: *defaults - - yarn-project-prod: *defaults_yarn_project - - yarn-project-formatting: *defaults_yarn_project - - yarn-project-tests: *defaults_yarn_project - - end-to-end: *defaults_yarn_project - - build-docs: *defaults_yarn_project + - end-to-end: *defaults_yarn_project_pre_join + - aztec-faucet: *defaults_yarn_project_pre_join + - build-docs: *defaults_yarn_project_pre_join + - yarn-project-test: *defaults_yarn_project_pre_join + - yarn-project-x86_64: *defaults_yarn_project_pre_join + - yarn-project-arm64: *defaults_yarn_project_pre_join + - yarn-project-ecr-manifest: + requires: + - yarn-project-x86_64 + - yarn-project-arm64 + <<: *defaults # Artifacts - - aztec-package: *defaults_yarn_project_prod - - cli: *defaults_yarn_project_prod - - aztec-faucet: *defaults_yarn_project_prod + - aztec-package: *defaults_yarn_project + - cli: *defaults_yarn_project # Boxes. - boxes: @@ -1519,6 +1499,21 @@ workflows: # Everything that must complete before deployment. - end: requires: + - barretenberg-x86_64-linux-gcc + - barretenberg-x86_64-linux-clang + - barretenberg-x86_64-linux-clang-assert + - barretenberg-x86_64-linux-clang-fuzzing + - barretenberg-wasm-linux-clang + - barretenberg-x86_64-linux-clang-sol + - barretenberg-bench + - barretenberg-proof-system-tests + - barretenberg-dsl-tests + - barretenberg-tests + - barretenberg-stdlib-tests + - barretenberg-stdlib-recursion-ultra-tests + - barretenberg-join-split-tests + - barretenberg-acir-tests-bb + - barretenberg-docs - mainnet-fork - e2e-2-pxes - e2e-note-getter diff --git a/.gitignore b/.gitignore index f24f1a84f27..e2fd5f4e513 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ cmake-build-debug .graphite* .DS_Store + +**/*.dockerignore \ No newline at end of file diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5a1dfbf60ca..07c5df16b15 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,5 +1,7 @@ { - ".": "0.24.0", - "barretenberg": "0.24.0", - "barretenberg/ts": "0.24.0" + ".": "0.25.0", + "yarn-project/cli": "0.25.0", + "yarn-project/aztec": "0.25.0", + "barretenberg": "0.25.0", + "barretenberg/ts": "0.25.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 78f957daf26..246e0a30ff4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,235 @@ # Changelog +## [0.25.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-packages-v0.24.0...aztec-packages-v0.25.0) (2024-03-05) + + +### ⚠ BREAKING CHANGES + +* nullifier read requests in private ([#4764](https://github.com/AztecProtocol/aztec-packages/issues/4764)) +* Use new deployment flow in ContractDeployer ([#4497](https://github.com/AztecProtocol/aztec-packages/issues/4497)) +* renamings of state var wrappers ([#4739](https://github.com/AztecProtocol/aztec-packages/issues/4739)) +* l1 to l2 message api takes sender as arg ([#4648](https://github.com/AztecProtocol/aztec-packages/issues/4648)) +* autogenerate compute_note_hash_and_nullifier ([#4610](https://github.com/AztecProtocol/aztec-packages/issues/4610)) + +### Features + +* Add aztec-nr private functions for initialization nullifier ([#4807](https://github.com/AztecProtocol/aztec-packages/issues/4807)) ([4feaea5](https://github.com/AztecProtocol/aztec-packages/commit/4feaea59267437a0841aa14f445cee7556a0c0b4)) +* Add deploy contract helper to aztec-nr ([#4775](https://github.com/AztecProtocol/aztec-packages/issues/4775)) ([6018fc6](https://github.com/AztecProtocol/aztec-packages/commit/6018fc66adfe76afbade0ffde3f1c83e97eba9c0)) +* Add tagged note structure ([#4843](https://github.com/AztecProtocol/aztec-packages/issues/4843)) ([553c2c6](https://github.com/AztecProtocol/aztec-packages/commit/553c2c602702d683c455928fc386f3b554f536ef)), closes [#4572](https://github.com/AztecProtocol/aztec-packages/issues/4572) +* Additional op count timing ([#4722](https://github.com/AztecProtocol/aztec-packages/issues/4722)) ([f0cc760](https://github.com/AztecProtocol/aztec-packages/commit/f0cc76040a2de5d0f827afdb662591232c4ee1ed)) +* Allow nullifier proofs in public ([#4892](https://github.com/AztecProtocol/aztec-packages/issues/4892)) ([f7a7243](https://github.com/AztecProtocol/aztec-packages/commit/f7a72436bb12e30d8a85c8cf9b3a460d5b380252)) +* Analyze % of time spent on field arithmetic ([#4501](https://github.com/AztecProtocol/aztec-packages/issues/4501)) ([5ddfa16](https://github.com/AztecProtocol/aztec-packages/commit/5ddfa16391f1017219a997c322b061ebe6f34db2)) +* AUTHWIT cancellations ([#4799](https://github.com/AztecProtocol/aztec-packages/issues/4799)) ([b7c2bc0](https://github.com/AztecProtocol/aztec-packages/commit/b7c2bc0e70faebb60e2051e0330e94937a1e3711)) +* AUTHWIT generator ([#4798](https://github.com/AztecProtocol/aztec-packages/issues/4798)) ([efd70f4](https://github.com/AztecProtocol/aztec-packages/commit/efd70f4b8bb284815c5345bd16d79018ed2dd812)) +* Autogenerate compute_note_hash_and_nullifier ([#4610](https://github.com/AztecProtocol/aztec-packages/issues/4610)) ([286e708](https://github.com/AztecProtocol/aztec-packages/commit/286e708c1016d60278060bb01f5d997f9a0bdfba)) +* **avm-simulator:** Add NULLIFIEREXISTS opcode to avm simulator, transpiler, noir test, TS tests ([#4747](https://github.com/AztecProtocol/aztec-packages/issues/4747)) ([707f572](https://github.com/AztecProtocol/aztec-packages/commit/707f572ae8802a9c92b9fe8cff2ec16dfea00b9d)) +* **avm-simulator:** Create cache for pending nullifiers and existence checks ([#4743](https://github.com/AztecProtocol/aztec-packages/issues/4743)) ([0f80579](https://github.com/AztecProtocol/aztec-packages/commit/0f80579823aa2de1271c8cdccc72e5f5ee935939)) +* **avm-simulator:** Implement AVM message opcodes (simulator/transpiler/noir-test) ([#4852](https://github.com/AztecProtocol/aztec-packages/issues/4852)) ([c98325d](https://github.com/AztecProtocol/aztec-packages/commit/c98325d23897d23c09faddc4355958406d44faa9)) +* **avm-simulator:** Implement NOTEHASHEXISTS ([#4882](https://github.com/AztecProtocol/aztec-packages/issues/4882)) ([d8c770b](https://github.com/AztecProtocol/aztec-packages/commit/d8c770bbf9e208adb31c6b0ea41e08f7c4f8818c)) +* **avm-transpiler:** Add emitnotehash and emitnullifier opcodes to avm transpiler and simulator tests ([#4746](https://github.com/AztecProtocol/aztec-packages/issues/4746)) ([d44d9f1](https://github.com/AztecProtocol/aztec-packages/commit/d44d9f11be2a2d2652b70b1d333322440c6ef06c)) +* **avm:** Enable main -> mem clk lookups ([#4591](https://github.com/AztecProtocol/aztec-packages/issues/4591)) ([0e503c1](https://github.com/AztecProtocol/aztec-packages/commit/0e503c14c0c20a93e162a90d8d049f094b64de7d)) +* **avm:** Hashing opcodes ([#4526](https://github.com/AztecProtocol/aztec-packages/issues/4526)) ([fe10c70](https://github.com/AztecProtocol/aztec-packages/commit/fe10c7049b3597a96f76a27a22e9233bc3b8ce82)) +* **avm:** Hashing to simulator ([#4527](https://github.com/AztecProtocol/aztec-packages/issues/4527)) ([9f67eec](https://github.com/AztecProtocol/aztec-packages/commit/9f67eec73c5d639df16e6b3bf45c4a1fc1c54bad)) +* **avm:** Propagate tag err to the main trace for op_return and internal_return ([#4615](https://github.com/AztecProtocol/aztec-packages/issues/4615)) ([427f1d8](https://github.com/AztecProtocol/aztec-packages/commit/427f1d8567a3f68c3093c29a2999096746927548)), closes [#4598](https://github.com/AztecProtocol/aztec-packages/issues/4598) +* Avoid requiring arith gates in sequence ([#4869](https://github.com/AztecProtocol/aztec-packages/issues/4869)) ([0ab0a94](https://github.com/AztecProtocol/aztec-packages/commit/0ab0a94842ce9b174ba82b430a93cba188fe75b0)) +* **bb:** Working msan preset ([#4618](https://github.com/AztecProtocol/aztec-packages/issues/4618)) ([0195ac8](https://github.com/AztecProtocol/aztec-packages/commit/0195ac89a13dc2a7b9caa5a8d8d29458a99c5f76)) +* Benchmark Protogalaxy rounds ([#4316](https://github.com/AztecProtocol/aztec-packages/issues/4316)) ([91af28d](https://github.com/AztecProtocol/aztec-packages/commit/91af28d6e03d85b5c749740c82cf9114379c823a)) +* Bitwise_not avm circuit ([#4548](https://github.com/AztecProtocol/aztec-packages/issues/4548)) ([3a7d31b](https://github.com/AztecProtocol/aztec-packages/commit/3a7d31b200e6e604eea06a40dcf5bf02b088ab79)) +* Boxes refactor pt2 ([#4612](https://github.com/AztecProtocol/aztec-packages/issues/4612)) ([aad45b3](https://github.com/AztecProtocol/aztec-packages/commit/aad45b3bc2be50dc7223ccc3faf1c336613dffea)) +* Boxes update ([#4498](https://github.com/AztecProtocol/aztec-packages/issues/4498)) ([382626c](https://github.com/AztecProtocol/aztec-packages/commit/382626cddaa175041695e2eb70ad3c350351ffe3)) +* Check initializer by default in private functions ([#4832](https://github.com/AztecProtocol/aztec-packages/issues/4832)) ([3ff9fe0](https://github.com/AztecProtocol/aztec-packages/commit/3ff9fe0ad9591caebc313acecd3a2144f8434ae2)) +* Define Aztec prelude ([#4929](https://github.com/AztecProtocol/aztec-packages/issues/4929)) ([8ffe5df](https://github.com/AztecProtocol/aztec-packages/commit/8ffe5df71b78ed5100f598f680fbb1fe49b546b3)) +* Delegate calls ([#4586](https://github.com/AztecProtocol/aztec-packages/issues/4586)) ([e6d65a7](https://github.com/AztecProtocol/aztec-packages/commit/e6d65a7fe9ebe855dcac389775aae2ccc3fa311f)) +* **devops:** Filter circleci config no-ops ([#4731](https://github.com/AztecProtocol/aztec-packages/issues/4731)) ([41984b4](https://github.com/AztecProtocol/aztec-packages/commit/41984b4e43fd3fd42522552ecb8ca1e54f32cdf1)) +* **docs:** Autogenerated Aztec-nr reference docs ([#3481](https://github.com/AztecProtocol/aztec-packages/issues/3481)) ([aebf762](https://github.com/AztecProtocol/aztec-packages/commit/aebf762d37dee9985740f3bf2578a0cf69818050)) +* **docs:** Docs meta doc ([#4767](https://github.com/AztecProtocol/aztec-packages/issues/4767)) ([0a645d3](https://github.com/AztecProtocol/aztec-packages/commit/0a645d3a5d3029501ccbba5e030146f7397301b0)) +* **docs:** Meta doc typo fixes ([#4779](https://github.com/AztecProtocol/aztec-packages/issues/4779)) ([44df132](https://github.com/AztecProtocol/aztec-packages/commit/44df1327fb7018187bf15a4ae4c76218160a2914)) +* **docs:** Note type IDs and compute_note_hash_and_nullifier page ([#4636](https://github.com/AztecProtocol/aztec-packages/issues/4636)) ([032874a](https://github.com/AztecProtocol/aztec-packages/commit/032874a031ce9a5dde7da20864fbd456061adc43)) +* Equality avm circuit ([#4595](https://github.com/AztecProtocol/aztec-packages/issues/4595)) ([aad7b45](https://github.com/AztecProtocol/aztec-packages/commit/aad7b45aa6d3a4c3df259ea41fdde48bf01139b1)) +* Execution Trace ([#4623](https://github.com/AztecProtocol/aztec-packages/issues/4623)) ([07ac589](https://github.com/AztecProtocol/aztec-packages/commit/07ac589d08964a44ea54a0d9fa0a21db73186aee)) +* Gate blocks ([#4741](https://github.com/AztecProtocol/aztec-packages/issues/4741)) ([61067a5](https://github.com/AztecProtocol/aztec-packages/commit/61067a5cdedfd10fbc32e381083b031bc80fc6d6)) +* Goblin documentation ([#4679](https://github.com/AztecProtocol/aztec-packages/issues/4679)) ([24d918f](https://github.com/AztecProtocol/aztec-packages/commit/24d918f7bd114f2641ae61bcf0da888e06f6520a)) +* Goblin Translator Fuzzer ([#4752](https://github.com/AztecProtocol/aztec-packages/issues/4752)) ([7402517](https://github.com/AztecProtocol/aztec-packages/commit/74025170288e39e1d7516f57df94f22bc30f663c)) +* GoblinUltra Bench ([#4671](https://github.com/AztecProtocol/aztec-packages/issues/4671)) ([319eea9](https://github.com/AztecProtocol/aztec-packages/commit/319eea9e4caf1d1ade00fedface5fab9bbf9db16)) +* Implementing IPA optimisation ([#4363](https://github.com/AztecProtocol/aztec-packages/issues/4363)) ([13647c2](https://github.com/AztecProtocol/aztec-packages/commit/13647c24487116f971c81dfaf4ee4664870522d5)) +* L1 to l2 message api takes sender as arg ([#4648](https://github.com/AztecProtocol/aztec-packages/issues/4648)) ([96f6b2a](https://github.com/AztecProtocol/aztec-packages/commit/96f6b2a6e5475d747191def24a122532eacd610d)), closes [#4559](https://github.com/AztecProtocol/aztec-packages/issues/4559) +* Login to ecr explicitly, faster bootstrap as we only do once. ([#4900](https://github.com/AztecProtocol/aztec-packages/issues/4900)) ([86d6749](https://github.com/AztecProtocol/aztec-packages/commit/86d6749615a533e0a9fbe0a1dca97b38fb14bb5f)) +* Macros for initializer checks ([#4830](https://github.com/AztecProtocol/aztec-packages/issues/4830)) ([c7c24b2](https://github.com/AztecProtocol/aztec-packages/commit/c7c24b2d1e71a95d3af7a9fe9e39b439ec319e3d)) +* Manual ClientIVC breakdown ([#4778](https://github.com/AztecProtocol/aztec-packages/issues/4778)) ([b4cfc89](https://github.com/AztecProtocol/aztec-packages/commit/b4cfc89c0d8286d2dfa3e04c58695d554951c920)) +* Moving the unbox option to npx command ([#4718](https://github.com/AztecProtocol/aztec-packages/issues/4718)) ([4c3bb92](https://github.com/AztecProtocol/aztec-packages/commit/4c3bb9294fc10ff4663275c952e277eaa7ecd647)) +* Native fee payment ([#4543](https://github.com/AztecProtocol/aztec-packages/issues/4543)) ([5d4702b](https://github.com/AztecProtocol/aztec-packages/commit/5d4702b7684393b54bef4cdca963077504b41a2a)) +* Non revertible effects and tx phases ([#4629](https://github.com/AztecProtocol/aztec-packages/issues/4629)) ([c04d72f](https://github.com/AztecProtocol/aztec-packages/commit/c04d72fd363b32743cf906bfe986f82c5d5901fc)) +* Nullifier read requests in private ([#4764](https://github.com/AztecProtocol/aztec-packages/issues/4764)) ([a049d1f](https://github.com/AztecProtocol/aztec-packages/commit/a049d1f571487f2cec25cb1bdeff5c177e25b91d)) +* Outgoing messages to any address ([#4512](https://github.com/AztecProtocol/aztec-packages/issues/4512)) ([4d0e8d3](https://github.com/AztecProtocol/aztec-packages/commit/4d0e8d30fb604e72bd4ef62f5cf8928e0eaa2009)) +* Parallel native/wasm bb builds. Better messaging around using ci cache. ([#4766](https://github.com/AztecProtocol/aztec-packages/issues/4766)) ([a924e55](https://github.com/AztecProtocol/aztec-packages/commit/a924e55393daa89fbba3a87cf019977286104b59)) +* Parallelise kernel and function circuit construction in client IVC ([#4841](https://github.com/AztecProtocol/aztec-packages/issues/4841)) ([9c689d8](https://github.com/AztecProtocol/aztec-packages/commit/9c689d8d5a7d330dabafaa7d10c0cfc5e4694921)) +* Public initializer check ([#4894](https://github.com/AztecProtocol/aztec-packages/issues/4894)) ([6b861bb](https://github.com/AztecProtocol/aztec-packages/commit/6b861bb06c7d0e51692953a946aba481bc78e2d1)) +* Public refunds via FPC ([#4750](https://github.com/AztecProtocol/aztec-packages/issues/4750)) ([30502c9](https://github.com/AztecProtocol/aztec-packages/commit/30502c96fc2aa2a86cdad0f7edaec9cac97e6cf5)) +* PublicImmutable impl ([#4758](https://github.com/AztecProtocol/aztec-packages/issues/4758)) ([87c976b](https://github.com/AztecProtocol/aztec-packages/commit/87c976bcf022300b2bd9dfa2a8c98f8fe7e45433)), closes [#4757](https://github.com/AztecProtocol/aztec-packages/issues/4757) +* Renamings of state var wrappers ([#4739](https://github.com/AztecProtocol/aztec-packages/issues/4739)) ([4667c27](https://github.com/AztecProtocol/aztec-packages/commit/4667c27695ad203f4d8fef73e13158ceed2cef7d)) +* Separate addition gate after final RAM gate ([#4851](https://github.com/AztecProtocol/aztec-packages/issues/4851)) ([f329db4](https://github.com/AztecProtocol/aztec-packages/commit/f329db4ec08f013bf8f53eb73b18d3d98d98e2e4)) +* Separate arithmetic gate in sort with edges ([#4866](https://github.com/AztecProtocol/aztec-packages/issues/4866)) ([40adc5c](https://github.com/AztecProtocol/aztec-packages/commit/40adc5cdc578c6ff6d6a9aa25c9a2f3506ec1677)) +* Simplify public input copy cycles ([#4753](https://github.com/AztecProtocol/aztec-packages/issues/4753)) ([a714ee0](https://github.com/AztecProtocol/aztec-packages/commit/a714ee027262dba3a083e17878862cd1144a86a6)) +* Static call support in aztec.nr and acir-simulator ([#4106](https://github.com/AztecProtocol/aztec-packages/issues/4106)) ([5f9546a](https://github.com/AztecProtocol/aztec-packages/commit/5f9546a50b72e29ec032e115a79ce5ceae2f26c0)) +* Update header to match message extension ([#4627](https://github.com/AztecProtocol/aztec-packages/issues/4627)) ([dc01e1d](https://github.com/AztecProtocol/aztec-packages/commit/dc01e1d573795f2199b6b9c6249fb1e816d5c594)) +* Update RAM/ROM memory records for new block structure ([#4806](https://github.com/AztecProtocol/aztec-packages/issues/4806)) ([65e4ab9](https://github.com/AztecProtocol/aztec-packages/commit/65e4ab93219118c8ac46a68bc6607ee9d11f6478)) +* Use new deployment flow in ContractDeployer ([#4497](https://github.com/AztecProtocol/aztec-packages/issues/4497)) ([0702dc6](https://github.com/AztecProtocol/aztec-packages/commit/0702dc6988149258124184b85d38db930effe0e7)) +* Use yarns topological build to get rid of explicit sequential steps, and let it solve. ([#4868](https://github.com/AztecProtocol/aztec-packages/issues/4868)) ([c909966](https://github.com/AztecProtocol/aztec-packages/commit/c909966ad6d0f1621d066f5861d38a128fe9c224)) +* **yp:** Add algolia search to the yellow paper ([#4771](https://github.com/AztecProtocol/aztec-packages/issues/4771)) ([48dd78e](https://github.com/AztecProtocol/aztec-packages/commit/48dd78e06a2dc9452bea1a3156721ffd68e046a4)) + + +### Bug Fixes + +* Add new oracle contract to devnet in CI ([#4687](https://github.com/AztecProtocol/aztec-packages/issues/4687)) ([920fa10](https://github.com/AztecProtocol/aztec-packages/commit/920fa10d4d5fb476cd6d868439310452f6e8dcc5)) +* Add registry contract to list ([#4694](https://github.com/AztecProtocol/aztec-packages/issues/4694)) ([3675e1d](https://github.com/AztecProtocol/aztec-packages/commit/3675e1d110eccf45986bbbcf35e29746474bb7aa)) +* Add TODO with issue for num_gates bug ([#4847](https://github.com/AztecProtocol/aztec-packages/issues/4847)) ([f6c558b](https://github.com/AztecProtocol/aztec-packages/commit/f6c558b41d3e003e1626a853aff0b58705847e84)) +* After noir move ([#4564](https://github.com/AztecProtocol/aztec-packages/issues/4564)) ([5f5bf16](https://github.com/AztecProtocol/aztec-packages/commit/5f5bf1604ce16a9d7c9f121ed79f9d287358510c)) +* Align block structs w/ yp [#3868](https://github.com/AztecProtocol/aztec-packages/issues/3868) ([#4541](https://github.com/AztecProtocol/aztec-packages/issues/4541)) ([081da3c](https://github.com/AztecProtocol/aztec-packages/commit/081da3cb0b9e83f817a82314bb4be116e32e054c)) +* Assembly benching ([#4640](https://github.com/AztecProtocol/aztec-packages/issues/4640)) ([f144745](https://github.com/AztecProtocol/aztec-packages/commit/f14474571210a46e7159cb9d2f0bc9374a837d3d)) +* AZTEC_PORT variable for devnet ([#4700](https://github.com/AztecProtocol/aztec-packages/issues/4700)) ([097a888](https://github.com/AztecProtocol/aztec-packages/commit/097a888b1f60d285595dbae6ebac5af32f9ace67)) +* Aztec-node terraform args ([#4669](https://github.com/AztecProtocol/aztec-packages/issues/4669)) ([4f37270](https://github.com/AztecProtocol/aztec-packages/commit/4f372703bcd2a13a7949cc3370356d0b376746ef)) +* **bb:** Initialize element::infinity() ([#4664](https://github.com/AztecProtocol/aztec-packages/issues/4664)) ([6813540](https://github.com/AztecProtocol/aztec-packages/commit/6813540731149db1f0d8932598335f95937ada03)) +* Boost the size of the non-revertible reads/writes ([#4688](https://github.com/AztecProtocol/aztec-packages/issues/4688)) ([9cb6daf](https://github.com/AztecProtocol/aztec-packages/commit/9cb6daff6330a5675a070334cc88773d6e0bae3a)) +* **build-system:** Login to dockerhub ([#4716](https://github.com/AztecProtocol/aztec-packages/issues/4716)) ([5eb0c57](https://github.com/AztecProtocol/aztec-packages/commit/5eb0c577f34df5f111d17ec25000fc03d09d5497)) +* Change function limit to private function limit ([#4785](https://github.com/AztecProtocol/aztec-packages/issues/4785)) ([2799f1f](https://github.com/AztecProtocol/aztec-packages/commit/2799f1fe1718fadd4bc0705449a8b4c79bc391b6)) +* Ci merge check ([#4921](https://github.com/AztecProtocol/aztec-packages/issues/4921)) ([46063da](https://github.com/AztecProtocol/aztec-packages/commit/46063da1b42f109e8b0c5c4b1a07c15401899b30)) +* **ci:** Bump puppeteer to fix yarn-project-base ([#4721](https://github.com/AztecProtocol/aztec-packages/issues/4721)) ([89af734](https://github.com/AztecProtocol/aztec-packages/commit/89af73421a83dfc79743e3e0287b246326d71b7d)) +* Cpp build ([#4918](https://github.com/AztecProtocol/aztec-packages/issues/4918)) ([15df3c0](https://github.com/AztecProtocol/aztec-packages/commit/15df3c08168611f7f65f5837a937031d81bb3566)) +* Dapp sub test ([#4938](https://github.com/AztecProtocol/aztec-packages/issues/4938)) ([827afd1](https://github.com/AztecProtocol/aztec-packages/commit/827afd10edfca8b2c8273742717f039981543194)) +* Debug build ([#4666](https://github.com/AztecProtocol/aztec-packages/issues/4666)) ([acc27b1](https://github.com/AztecProtocol/aztec-packages/commit/acc27b1bd2ec21c7b5c71f02974bd49d29b4caa5)) +* Depreciated ci image ([#4911](https://github.com/AztecProtocol/aztec-packages/issues/4911)) ([174fc10](https://github.com/AztecProtocol/aztec-packages/commit/174fc104d68e94b33d4d455f24e38b73a64b534a)) +* **docs:** Update 0.22 migration_notes.md w/ proper note interface ([#4701](https://github.com/AztecProtocol/aztec-packages/issues/4701)) ([a972dc8](https://github.com/AztecProtocol/aztec-packages/commit/a972dc8b0d62ba8e3fbbb9aed7f523ebd2b06f59)) +* **docs:** Update unconstrained function call image ([#4834](https://github.com/AztecProtocol/aztec-packages/issues/4834)) ([b0bc772](https://github.com/AztecProtocol/aztec-packages/commit/b0bc772017fd36671ce9250f52d6cc64b22f7386)) +* **dsl:** Add full recursive verification test ([#4658](https://github.com/AztecProtocol/aztec-packages/issues/4658)) ([9e09772](https://github.com/AztecProtocol/aztec-packages/commit/9e0977261aea723d6ea68750788f29a40730c404)) +* Expose port when running aztec img ([#4719](https://github.com/AztecProtocol/aztec-packages/issues/4719)) ([df40b15](https://github.com/AztecProtocol/aztec-packages/commit/df40b15524cee9799c5193c6adf2ad7a5ea92faf)) +* Fetch Headers and Bodies separately [#4167](https://github.com/AztecProtocol/aztec-packages/issues/4167) ([#4632](https://github.com/AztecProtocol/aztec-packages/issues/4632)) ([0681b3a](https://github.com/AztecProtocol/aztec-packages/commit/0681b3a6fe99667cdaa6cb3954accf15795c42ea)) +* Fix races in slab allocator and lookup tables and add prepending for op_queues ([#4754](https://github.com/AztecProtocol/aztec-packages/issues/4754)) ([0c99de7](https://github.com/AztecProtocol/aztec-packages/commit/0c99de7c4b9931989824f66dab83cc644578a75c)) +* Fix Translator composer test instability ([#4751](https://github.com/AztecProtocol/aztec-packages/issues/4751)) ([842ba7a](https://github.com/AztecProtocol/aztec-packages/commit/842ba7a720d075632ad2c4b948f874a12cfa3ecd)) +* G2.Serialize sporadic failure ([#4626](https://github.com/AztecProtocol/aztec-packages/issues/4626)) ([c9e6bb1](https://github.com/AztecProtocol/aztec-packages/commit/c9e6bb1391070b6551b313b85fe73742ff0966fc)) +* Get_wires for ultra ([#4605](https://github.com/AztecProtocol/aztec-packages/issues/4605)) ([512110e](https://github.com/AztecProtocol/aztec-packages/commit/512110e4bdc353b01ee92fb5b2ff5f6e6f875fbb)) +* Initializer checks across txs ([#4842](https://github.com/AztecProtocol/aztec-packages/issues/4842)) ([747fc33](https://github.com/AztecProtocol/aztec-packages/commit/747fc33590f9fe25ffcd3e538d7db49bfb98fae8)) +* Issue if commitment hints when the same commitment appears twice within the same tx ([#4702](https://github.com/AztecProtocol/aztec-packages/issues/4702)) ([9c3c880](https://github.com/AztecProtocol/aztec-packages/commit/9c3c88015965554dfdb6568bc239214cbbe85002)) +* L1 contract address config ([#4684](https://github.com/AztecProtocol/aztec-packages/issues/4684)) ([20e7605](https://github.com/AztecProtocol/aztec-packages/commit/20e76058e3de7d0d30d6c951fa74d6dd08a68d2c)) +* Master borked arithmetic tests ([#4606](https://github.com/AztecProtocol/aztec-packages/issues/4606)) ([472c54a](https://github.com/AztecProtocol/aztec-packages/commit/472c54a7e89001f5f752da670cc25ec1a537da87)) +* More robust noir sync ([#4734](https://github.com/AztecProtocol/aztec-packages/issues/4734)) ([f53946d](https://github.com/AztecProtocol/aztec-packages/commit/f53946df78d09e7634eb839d068c559fffa0e751)) +* Msan build ([#4646](https://github.com/AztecProtocol/aztec-packages/issues/4646)) ([886cc75](https://github.com/AztecProtocol/aztec-packages/commit/886cc7585f935f4f12257444af7862b51dc91584)) +* MSAN msgpack noise ([#4677](https://github.com/AztecProtocol/aztec-packages/issues/4677)) ([1abae28](https://github.com/AztecProtocol/aztec-packages/commit/1abae28580354f5ccc620dbd717bf079f39fb445)) +* Noir test incorrect reporting ([#4925](https://github.com/AztecProtocol/aztec-packages/issues/4925)) ([d98db3a](https://github.com/AztecProtocol/aztec-packages/commit/d98db3aa7cbfdaf5f698d4f4f0eaf4a788a02199)) +* P2p-bootstrap ECS command + /status route ([#4682](https://github.com/AztecProtocol/aztec-packages/issues/4682)) ([21ec23d](https://github.com/AztecProtocol/aztec-packages/commit/21ec23d54fa69c3515f0d9fa23cc7ea1168d7e6e)) +* PXE devnet connectivity ([#4759](https://github.com/AztecProtocol/aztec-packages/issues/4759)) ([c2027e3](https://github.com/AztecProtocol/aztec-packages/commit/c2027e3f58279fc9fa7c8e5c1b7fdcf832555d90)) +* Rebuilding on snap updates ([#4729](https://github.com/AztecProtocol/aztec-packages/issues/4729)) ([a2c0cae](https://github.com/AztecProtocol/aztec-packages/commit/a2c0caed4c48ce2d37d2370040ea059d80d93bfe)), closes [#4728](https://github.com/AztecProtocol/aztec-packages/issues/4728) +* Remove the `VerificationKey` from `ProverInstance` ([#4908](https://github.com/AztecProtocol/aztec-packages/issues/4908)) ([8619c08](https://github.com/AztecProtocol/aztec-packages/commit/8619c084cdfd061f284058b00a96f16fbbca65bf)) +* Revert boxes update ([#4602](https://github.com/AztecProtocol/aztec-packages/issues/4602)) ([f5592b8](https://github.com/AztecProtocol/aztec-packages/commit/f5592b82cab37072f0a1140b77e15cfa68220d74)) +* Temporarily skip failing deployment test ([e6ce08f](https://github.com/AztecProtocol/aztec-packages/commit/e6ce08f6d74db76a45e5dea69d5b7531ca99c769)) +* Use size hint for ivc circuits ([#4802](https://github.com/AztecProtocol/aztec-packages/issues/4802)) ([035cff4](https://github.com/AztecProtocol/aztec-packages/commit/035cff451ca2171e08279b9d36b23f38b840efea)) +* Use specific slither and slitherin versions ([#4621](https://github.com/AztecProtocol/aztec-packages/issues/4621)) ([9e7a451](https://github.com/AztecProtocol/aztec-packages/commit/9e7a4519ae6d5ded8b7369abf50eb2c46948abe7)) +* **yp:** Update search API key ([#4800](https://github.com/AztecProtocol/aztec-packages/issues/4800)) ([1cb6396](https://github.com/AztecProtocol/aztec-packages/commit/1cb639631dab59b8a301f1e256d2f76bd52addd2)) + + +### Miscellaneous + +* 1 struct per file ([#4693](https://github.com/AztecProtocol/aztec-packages/issues/4693)) ([19d2bbe](https://github.com/AztecProtocol/aztec-packages/commit/19d2bbea913506761e9706073d13513d5533fedb)), closes [#4410](https://github.com/AztecProtocol/aztec-packages/issues/4410) +* Add authwit to migration notes ([#4914](https://github.com/AztecProtocol/aztec-packages/issues/4914)) ([e775ead](https://github.com/AztecProtocol/aztec-packages/commit/e775ead27c975027022813902183c9eda44d64a4)) +* Add comments in kernel_prover.ts related to hints ([#4713](https://github.com/AztecProtocol/aztec-packages/issues/4713)) ([68162b6](https://github.com/AztecProtocol/aztec-packages/commit/68162b6799aef91f005539a5e613240698bc2a1c)) +* Add custom inspect for base types ([#4890](https://github.com/AztecProtocol/aztec-packages/issues/4890)) ([a1b3c01](https://github.com/AztecProtocol/aztec-packages/commit/a1b3c01fa088400188348b85ac1933e14bd9bdf6)) +* Add pow poly bench and link optimization issues ([#4725](https://github.com/AztecProtocol/aztec-packages/issues/4725)) ([faa9586](https://github.com/AztecProtocol/aztec-packages/commit/faa9586ef702e3f150e6aa8217dcbcd63611dea2)) +* Add struct for each bigint modulus ([#4422](https://github.com/AztecProtocol/aztec-packages/issues/4422)) ([a2942b7](https://github.com/AztecProtocol/aztec-packages/commit/a2942b791c55aab85e2266a0ec66ffb5a993c2a4)) +* Address comments ([#4772](https://github.com/AztecProtocol/aztec-packages/issues/4772)) ([10d90ab](https://github.com/AztecProtocol/aztec-packages/commit/10d90ab3a15de66f4b8a64464fe8e15f33a0589d)) +* Addressing flakiness of `e2e_public_cross_chain_messaging` ([#4853](https://github.com/AztecProtocol/aztec-packages/issues/4853)) ([99bbaee](https://github.com/AztecProtocol/aztec-packages/commit/99bbaee6282ec9d7e6d853e43653d43eb68bf408)) +* **avm-simulator:** Create a dedicated component just for tracing world state accesses ([#4733](https://github.com/AztecProtocol/aztec-packages/issues/4733)) ([0af89e6](https://github.com/AztecProtocol/aztec-packages/commit/0af89e6c1ff21a6079d42fe87d57d667a42cc491)) +* **avm-simulator:** Pull out public storage caching and merging from the state journal ([#4730](https://github.com/AztecProtocol/aztec-packages/issues/4730)) ([b075401](https://github.com/AztecProtocol/aztec-packages/commit/b075401e53a6dbe95c413608fc3c30bf19648103)) +* **avm-simulator:** Test cleanup using `expect.objectContaining()` ([#4863](https://github.com/AztecProtocol/aztec-packages/issues/4863)) ([c4ecfdd](https://github.com/AztecProtocol/aztec-packages/commit/c4ecfddeaa09b204977d31329aec7ba00f26e2d0)) +* **avm-transpiler:** Minor rust fixes ([#4889](https://github.com/AztecProtocol/aztec-packages/issues/4889)) ([46ee6a8](https://github.com/AztecProtocol/aztec-packages/commit/46ee6a88f4c8972bf7c8b60caf14030760590b96)) +* **avm-transpiler:** Prefix AVM opcode oracles with avmOpcode ([#4862](https://github.com/AztecProtocol/aztec-packages/issues/4862)) ([f07beee](https://github.com/AztecProtocol/aztec-packages/commit/f07beee3c220ccce892a984b1995e6f867c6895c)) +* **avm:** Nit fixes on message opcodes ([#4915](https://github.com/AztecProtocol/aztec-packages/issues/4915)) ([c48f5ce](https://github.com/AztecProtocol/aztec-packages/commit/c48f5cebf56e3a4545fcc72bb9d619b1127dc1ba)) +* **avm:** Remove some leftover files related to Avm-mini (replaced by Avm) ([#4715](https://github.com/AztecProtocol/aztec-packages/issues/4715)) ([8c697ce](https://github.com/AztecProtocol/aztec-packages/commit/8c697ce187b4bb1c66f1146ebbc39567a46f35f8)) +* **aztec-nr:** Clarify in comments that nullifier computation does not need to include siloed note-hash for protocol security ([#2667](https://github.com/AztecProtocol/aztec-packages/issues/2667)) ([426513e](https://github.com/AztecProtocol/aztec-packages/commit/426513e39e79579c53f6a4a16f26c8f5d9631026)), closes [#2666](https://github.com/AztecProtocol/aztec-packages/issues/2666) +* **bb:** Allow dynamic plookup tables ([#4667](https://github.com/AztecProtocol/aztec-packages/issues/4667)) ([5920012](https://github.com/AztecProtocol/aztec-packages/commit/592001255a999abb7167f885a5def7f8651d63a7)) +* **bb:** More namespaces under bb ([#4348](https://github.com/AztecProtocol/aztec-packages/issues/4348)) ([00ba983](https://github.com/AztecProtocol/aztec-packages/commit/00ba9837606f33ccbc5c0c40be22b11a736b1608)) +* **bb:** Small test improvements ([#4568](https://github.com/AztecProtocol/aztec-packages/issues/4568)) ([e23d048](https://github.com/AztecProtocol/aztec-packages/commit/e23d048e916fa12966fe01d1a8c0d3bfb50c2943)) +* **bb:** Use RefArray where possible ([#4686](https://github.com/AztecProtocol/aztec-packages/issues/4686)) ([5b4e1a6](https://github.com/AztecProtocol/aztec-packages/commit/5b4e1a61216655cebb58863d26d418b23881dd02)) +* Bootstrap improvements. ([#4711](https://github.com/AztecProtocol/aztec-packages/issues/4711)) ([1375233](https://github.com/AztecProtocol/aztec-packages/commit/13752339334be9c8cc0ae500d0e932f76d18a77d)) +* **boxes:** Adding frontend test to vanilla-js box ([cd1ca2e](https://github.com/AztecProtocol/aztec-packages/commit/cd1ca2e13c3b475e28f17ad74e09b439a1133de0)) +* **boxes:** Adding react frontend tests ([086e478](https://github.com/AztecProtocol/aztec-packages/commit/086e4789985d4e9b4712c0556811ab88be51e387)) +* Build nargo against Ubuntu 20 for better compatability ([#4710](https://github.com/AztecProtocol/aztec-packages/issues/4710)) ([e84759f](https://github.com/AztecProtocol/aztec-packages/commit/e84759f953b789f38624021814dc634e8dc1d5b7)) +* **ci:** Enforce formatting of noir rust code ([#4765](https://github.com/AztecProtocol/aztec-packages/issues/4765)) ([d9a1853](https://github.com/AztecProtocol/aztec-packages/commit/d9a1853cc0474050f40ef52b196568b711f7eb07)), closes [#4763](https://github.com/AztecProtocol/aztec-packages/issues/4763) +* **ci:** Test noir-projects in CI ([#4604](https://github.com/AztecProtocol/aztec-packages/issues/4604)) ([2ac428f](https://github.com/AztecProtocol/aztec-packages/commit/2ac428fd048aaadbdd28eb4ff7b7692a149e6468)) +* ContextInterface trait for private and public contexts ([#4808](https://github.com/AztecProtocol/aztec-packages/issues/4808)) ([237f870](https://github.com/AztecProtocol/aztec-packages/commit/237f870cfa9d83eb11530b0c64d3b3e5a6b0ad8d)) +* Decouple ypb ([#4749](https://github.com/AztecProtocol/aztec-packages/issues/4749)) ([f3c65ce](https://github.com/AztecProtocol/aztec-packages/commit/f3c65ce75637bd47aca849a08b567b06a69318b0)) +* Deploy docs to production only on releases ([#4928](https://github.com/AztecProtocol/aztec-packages/issues/4928)) ([c9eb856](https://github.com/AztecProtocol/aztec-packages/commit/c9eb856ab7307642c77a8bd808de49585449b1d3)) +* Do not download foundry during L1 contracts fast bootstrap ([#4865](https://github.com/AztecProtocol/aztec-packages/issues/4865)) ([c4357c8](https://github.com/AztecProtocol/aztec-packages/commit/c4357c8c4af5f763a81939ff4abe19b5e0e40029)) +* **docs:** Getting a bot to comment on docs PRs with docs previews ([#4600](https://github.com/AztecProtocol/aztec-packages/issues/4600)) ([8307dad](https://github.com/AztecProtocol/aztec-packages/commit/8307dadd853d5091841e169c841ab6b09c223efb)) +* **docs:** Passing nothing if pull request is unbounded ([#4794](https://github.com/AztecProtocol/aztec-packages/issues/4794)) ([db3f785](https://github.com/AztecProtocol/aztec-packages/commit/db3f785348f92a3255edc6ccaf59c3ecede082c6)) +* **docs:** Removing boxes page, will iterate later as part of DIP ([#4698](https://github.com/AztecProtocol/aztec-packages/issues/4698)) ([5c232af](https://github.com/AztecProtocol/aztec-packages/commit/5c232af1dfbbf3872fafc88fad41f6e64bc0d341)) +* **docs:** Simple e2e tests to use in docs ([#4596](https://github.com/AztecProtocol/aztec-packages/issues/4596)) ([6ec9f57](https://github.com/AztecProtocol/aztec-packages/commit/6ec9f577afe860ca2986b03a00b5ebe87d6600f4)) +* **docs:** Update aztecnr-getting-started.md CLI deploy command ([#4590](https://github.com/AztecProtocol/aztec-packages/issues/4590)) ([234ae3e](https://github.com/AztecProtocol/aztec-packages/commit/234ae3e773ace4097bfe9b9be9a563886dfaaffc)) +* **docs:** Update communication images ([#4744](https://github.com/AztecProtocol/aztec-packages/issues/4744)) ([8968e6e](https://github.com/AztecProtocol/aztec-packages/commit/8968e6e1709d7e257cfc264c76d9e52500ccd99f)) +* **docs:** Update getting started contract tutorial ([#4588](https://github.com/AztecProtocol/aztec-packages/issues/4588)) ([f417452](https://github.com/AztecProtocol/aztec-packages/commit/f4174527657db9e0c5168c98a896a93f1214e846)) +* Ecr login retry ([#4617](https://github.com/AztecProtocol/aztec-packages/issues/4617)) ([c3a784f](https://github.com/AztecProtocol/aztec-packages/commit/c3a784f7dfc7c11e4069c0a81dbc9c3303b0d3d5)) +* Fix docs ([#4923](https://github.com/AztecProtocol/aztec-packages/issues/4923)) ([edfba29](https://github.com/AztecProtocol/aztec-packages/commit/edfba29efea1faa10631dd76ea4e737f8d8bad79)) +* Get rid of Honk UltraComposer ([#4875](https://github.com/AztecProtocol/aztec-packages/issues/4875)) ([7e52c29](https://github.com/AztecProtocol/aztec-packages/commit/7e52c2971b91dfb0f07c178b2adb4427363acd1e)) +* Implement poseidon2 opcode ([#4446](https://github.com/AztecProtocol/aztec-packages/issues/4446)) ([491a8df](https://github.com/AztecProtocol/aztec-packages/commit/491a8dfe81a33a7552686f70833f6130da944142)) +* Improve noir-contracts.js codegen ([#4789](https://github.com/AztecProtocol/aztec-packages/issues/4789)) ([d367cc4](https://github.com/AztecProtocol/aztec-packages/commit/d367cc45c72a8d4a6c4e207a38047f3e63bee3b9)), closes [#4707](https://github.com/AztecProtocol/aztec-packages/issues/4707) +* Integration test of body publishing ([#4795](https://github.com/AztecProtocol/aztec-packages/issues/4795)) ([e414846](https://github.com/AztecProtocol/aztec-packages/commit/e414846db11479f91f332fd4d5edf62b3eeae905)) +* Make first iteration of protogalaxy more efficient ([#4630](https://github.com/AztecProtocol/aztec-packages/issues/4630)) ([4c7f24f](https://github.com/AztecProtocol/aztec-packages/commit/4c7f24f8ea8c21bc8114ead67d2082a06c9c5493)) +* Min noir build ([#4812](https://github.com/AztecProtocol/aztec-packages/issues/4812)) ([01dd0a9](https://github.com/AztecProtocol/aztec-packages/commit/01dd0a9318de6c69d60e15d56b0fb29d2ec51b28)) +* More interop tests ([#4699](https://github.com/AztecProtocol/aztec-packages/issues/4699)) ([a9971e1](https://github.com/AztecProtocol/aztec-packages/commit/a9971e10e7e9980946ebcbe7a7d4201c61d7bef0)), closes [#4412](https://github.com/AztecProtocol/aztec-packages/issues/4412) +* Move remaining data out of Honk UltraComposer ([#4848](https://github.com/AztecProtocol/aztec-packages/issues/4848)) ([823e071](https://github.com/AztecProtocol/aztec-packages/commit/823e071a0988cae906c13fa47e501fe9912788dc)) +* Move vk computation out of Honk Ultra composer ([#4811](https://github.com/AztecProtocol/aztec-packages/issues/4811)) ([f354e89](https://github.com/AztecProtocol/aztec-packages/commit/f354e899b4b35dd6d06699f0dbff48f7ea9ed9c3)) +* Moving hash functions to relevant classes ([#4551](https://github.com/AztecProtocol/aztec-packages/issues/4551)) ([731d7d0](https://github.com/AztecProtocol/aztec-packages/commit/731d7d012b1f5fb0f8ae3380f14683a37be0e65c)) +* Moving types consts to constants.nr ([#4919](https://github.com/AztecProtocol/aztec-packages/issues/4919)) ([ecfcb78](https://github.com/AztecProtocol/aztec-packages/commit/ecfcb7876e487c9f7a8a31ff5438c15e342ba31b)) +* **noir:** Extend 4681 bitsize refactor ([#4689](https://github.com/AztecProtocol/aztec-packages/issues/4689)) ([811d767](https://github.com/AztecProtocol/aztec-packages/commit/811d76771b472a2da0464c3038c15a489d49319c)) +* PedersenHash(...) TS func returns Fr ([#4704](https://github.com/AztecProtocol/aztec-packages/issues/4704)) ([c5eeb4c](https://github.com/AztecProtocol/aztec-packages/commit/c5eeb4c4ba4cec3be6b3c9fc60b7105ca2f54867)), closes [#4614](https://github.com/AztecProtocol/aztec-packages/issues/4614) +* Pull noir for u64 as array lengths ([#4787](https://github.com/AztecProtocol/aztec-packages/issues/4787)) ([e69b586](https://github.com/AztecProtocol/aztec-packages/commit/e69b58660ff843350e1e098d8f1a84f4ce3d3c34)) +* Purge SafeU120 ([#4819](https://github.com/AztecProtocol/aztec-packages/issues/4819)) ([9633b0f](https://github.com/AztecProtocol/aztec-packages/commit/9633b0fd4dfbdc80b3fc248b03486f2a73f37bed)) +* Reduce size for rollup benchmark ([cf8bd85](https://github.com/AztecProtocol/aztec-packages/commit/cf8bd85376169cdeb6fbda40e19ae2601bbb3370)) +* Remove import of `dep::aztec` from aztec_macros ([#4941](https://github.com/AztecProtocol/aztec-packages/issues/4941)) ([e696b1e](https://github.com/AztecProtocol/aztec-packages/commit/e696b1e7b4d6f5cc895c6dad7fb56f001ebbac6e)) +* Remove last impls of compute_note_hash_and_nullifier ([#4943](https://github.com/AztecProtocol/aztec-packages/issues/4943)) ([ff66bb8](https://github.com/AztecProtocol/aztec-packages/commit/ff66bb83a610ac5d6390c1b648245e31cc958189)) +* Remove legacy deployer ([#4777](https://github.com/AztecProtocol/aztec-packages/issues/4777)) ([20dc67b](https://github.com/AztecProtocol/aztec-packages/commit/20dc67b5b1de367787361e8406c09e670b12bac2)) +* Remove original return from aztec fns ([#4804](https://github.com/AztecProtocol/aztec-packages/issues/4804)) ([9e246c1](https://github.com/AztecProtocol/aztec-packages/commit/9e246c1289fa40c35c4b28d2f0081dfdc2aa9d19)) +* Remove TypeScript tooling from noir-projects. ([#4867](https://github.com/AztecProtocol/aztec-packages/issues/4867)) ([15c5399](https://github.com/AztecProtocol/aztec-packages/commit/15c5399a10719a8916ed82fe0ea510a8c6e8c6c7)) +* Remove unnecessary casts ([#4906](https://github.com/AztecProtocol/aztec-packages/issues/4906)) ([7a62c2f](https://github.com/AztecProtocol/aztec-packages/commit/7a62c2f9dfc35080a3051c518fa63c26f86977d7)) +* Remove VK computation Pg prover flow; improve benchmark to reflect possible optimization ([#4639](https://github.com/AztecProtocol/aztec-packages/issues/4639)) ([c1709b3](https://github.com/AztecProtocol/aztec-packages/commit/c1709b3d5fe615d980b2ebd9283fb841d9e6a85a)) +* Remove WASMTIME_ENV_HACK ([#4714](https://github.com/AztecProtocol/aztec-packages/issues/4714)) ([50f89f1](https://github.com/AztecProtocol/aztec-packages/commit/50f89f1832154d526908c55ab296aaf9bacf3608)) +* Removing msg-key ([#4856](https://github.com/AztecProtocol/aztec-packages/issues/4856)) ([2b6656d](https://github.com/AztecProtocol/aztec-packages/commit/2b6656dbbd3b16297ceb93df3403a7c7d80c9899)), closes [#4678](https://github.com/AztecProtocol/aztec-packages/issues/4678) +* Rename avm_mini to avm ([#4580](https://github.com/AztecProtocol/aztec-packages/issues/4580)) ([5896a92](https://github.com/AztecProtocol/aztec-packages/commit/5896a920bc4f5fd239d69795872567af6ccbe803)), closes [#4533](https://github.com/AztecProtocol/aztec-packages/issues/4533) +* Rename read request to note hash read request ([#4888](https://github.com/AztecProtocol/aztec-packages/issues/4888)) ([bd3f614](https://github.com/AztecProtocol/aztec-packages/commit/bd3f614009701ab6e7e0033be25c4f04def62ebf)) +* Replacing use of `L2Tx` with `TxEffect` ([#4876](https://github.com/AztecProtocol/aztec-packages/issues/4876)) ([d9acaa4](https://github.com/AztecProtocol/aztec-packages/commit/d9acaa43140974c7d5e4380aead467552c932496)) +* Specify rust-analyzer.linkedProjects after noir-repo move ([#4922](https://github.com/AztecProtocol/aztec-packages/issues/4922)) ([c22b8c6](https://github.com/AztecProtocol/aztec-packages/commit/c22b8c67483c5f28afd4e95b0a6b0f794224be79)) +* Squash yp ypb + other build improvements. ([#4901](https://github.com/AztecProtocol/aztec-packages/issues/4901)) ([be5855c](https://github.com/AztecProtocol/aztec-packages/commit/be5855cdbd1993155bd228afbeafee2c447b46a5)) +* Subscribe to a dapp with a token ([#4696](https://github.com/AztecProtocol/aztec-packages/issues/4696)) ([3bbe167](https://github.com/AztecProtocol/aztec-packages/commit/3bbe167b43f13dd87d0ebf0b3f5005ba7bb612e7)) +* Switch noir pull to master branch ([#4581](https://github.com/AztecProtocol/aztec-packages/issues/4581)) ([a7889f8](https://github.com/AztecProtocol/aztec-packages/commit/a7889f8d21684099306b72a87e0fb57b3bba0cb4)) +* **tests:** Add counter and private voting tests ([#4592](https://github.com/AztecProtocol/aztec-packages/issues/4592)) ([d3be5cc](https://github.com/AztecProtocol/aztec-packages/commit/d3be5cc5d2569f3c9c00f993d4c4df8118bf7e7b)) +* Toy avm snake case ([#4584](https://github.com/AztecProtocol/aztec-packages/issues/4584)) ([d071768](https://github.com/AztecProtocol/aztec-packages/commit/d07176863011382c34af5d5c80c596f737369703)) +* Updating encoding of TxEffects ([#4726](https://github.com/AztecProtocol/aztec-packages/issues/4726)) ([29b1ea3](https://github.com/AztecProtocol/aztec-packages/commit/29b1ea3db2fd86bb42b584f48d5933e53fa73978)) +* Updating viem ([#4783](https://github.com/AztecProtocol/aztec-packages/issues/4783)) ([23bc26a](https://github.com/AztecProtocol/aztec-packages/commit/23bc26a4859d9777c3e6dd49e351a4e6b13a989a)) +* Use shared immutable for slow tree ([#4831](https://github.com/AztecProtocol/aztec-packages/issues/4831)) ([821c25d](https://github.com/AztecProtocol/aztec-packages/commit/821c25dccf8b32c51cbca49842395755cf39037e)), closes [#4820](https://github.com/AztecProtocol/aztec-packages/issues/4820) +* Using Tuples in `TxEffect`s and renaming note commitments ([#4717](https://github.com/AztecProtocol/aztec-packages/issues/4717)) ([3dd3c46](https://github.com/AztecProtocol/aztec-packages/commit/3dd3c46591aac17f1d936c49aeb04a5f00e9ff0e)) +* Yellow paper typo fix ([#4663](https://github.com/AztecProtocol/aztec-packages/issues/4663)) ([315fcb1](https://github.com/AztecProtocol/aztec-packages/commit/315fcb1f6bf3dcffab51af793cf2745619bed4be)) +* **yellowpaper:** Fix notehashexists nullifierexists instructions ([#4625](https://github.com/AztecProtocol/aztec-packages/issues/4625)) ([5d38dc7](https://github.com/AztecProtocol/aztec-packages/commit/5d38dc79e44f6053d68228e061c9c65f117e072b)) +* **yellowpaper:** Minor cleanup ([#4622](https://github.com/AztecProtocol/aztec-packages/issues/4622)) ([2d16966](https://github.com/AztecProtocol/aztec-packages/commit/2d169665ee7191a710f9586db0f37fd8d409678e)) +* **yellowpaper:** Typos and other cleanup ([#4620](https://github.com/AztecProtocol/aztec-packages/issues/4620)) ([825c5c3](https://github.com/AztecProtocol/aztec-packages/commit/825c5c3446d8d5a31d886972551c0214158a2501)) + + +### Documentation + +* Add compression circuit outline ([#4599](https://github.com/AztecProtocol/aztec-packages/issues/4599)) ([2eca2aa](https://github.com/AztecProtocol/aztec-packages/commit/2eca2aa8796b7077e05f0bc1b71dd4d404ad36b3)) +* Add Notes page to build section ([#4690](https://github.com/AztecProtocol/aztec-packages/issues/4690)) ([6582b09](https://github.com/AztecProtocol/aztec-packages/commit/6582b09956d03b1749c5727053ca23f7c266e535)) +* Add prelude note to migration ([#4949](https://github.com/AztecProtocol/aztec-packages/issues/4949)) ([8342393](https://github.com/AztecProtocol/aztec-packages/commit/83423933f23e28ec7ca6e9a5c96c291ef40303df)) +* Add section on nodes and actors ([#3975](https://github.com/AztecProtocol/aztec-packages/issues/3975)) ([379ded4](https://github.com/AztecProtocol/aztec-packages/commit/379ded49162d4f0a9fd2877c1e22d11ad74126b6)) +* Address DA comments ([#4641](https://github.com/AztecProtocol/aztec-packages/issues/4641)) ([624ec4c](https://github.com/AztecProtocol/aztec-packages/commit/624ec4ce52479e3060f0d7e656426640407c0f43)) +* Incorrect comment ([#4846](https://github.com/AztecProtocol/aztec-packages/issues/4846)) ([4979e02](https://github.com/AztecProtocol/aztec-packages/commit/4979e02dd359238547df0573aab3fe14c81a3602)) +* Minor fixes state ([#4909](https://github.com/AztecProtocol/aztec-packages/issues/4909)) ([b027dbb](https://github.com/AztecProtocol/aztec-packages/commit/b027dbbc91298c9a159248e7792aaf0a12dbfcfd)) +* Pass by brunny-eth ([#4579](https://github.com/AztecProtocol/aztec-packages/issues/4579)) ([5285010](https://github.com/AztecProtocol/aztec-packages/commit/5285010219fca950991f30d557b8082922fff449)) +* Refactoring of private message delivery section of yellow paper ([#4628](https://github.com/AztecProtocol/aztec-packages/issues/4628)) ([5a2c534](https://github.com/AztecProtocol/aztec-packages/commit/5a2c534280fa45de8437b9cdac5600b6eb2eac67)) +* Update LSP instructions ([#4920](https://github.com/AztecProtocol/aztec-packages/issues/4920)) ([a5e26e7](https://github.com/AztecProtocol/aztec-packages/commit/a5e26e7c283fb54b4acbc485d227df0b07505401)) +* Updated bytecode section ([#4650](https://github.com/AztecProtocol/aztec-packages/issues/4650)) ([fa67330](https://github.com/AztecProtocol/aztec-packages/commit/fa67330ea466058d1613a2c7fa82351f81cf85de)) +* Updated fees spec in yellow paper ([#4624](https://github.com/AztecProtocol/aztec-packages/issues/4624)) ([cdf67ea](https://github.com/AztecProtocol/aztec-packages/commit/cdf67ea74aed4ba8f465a981b32f82766a32641a)) +* Updated yellow paper P2P network. ([#4652](https://github.com/AztecProtocol/aztec-packages/issues/4652)) ([d3ae287](https://github.com/AztecProtocol/aztec-packages/commit/d3ae28780ca33fe88166e7cceb3cc3c246926195)) +* Yellow paper - AVM circuit Chiplets section ([#4642](https://github.com/AztecProtocol/aztec-packages/issues/4642)) ([d717dde](https://github.com/AztecProtocol/aztec-packages/commit/d717dde4054e47dbe56f7903075ea9a007777e54)) +* **yellow-paper:** Changes to circuit sections ([#4616](https://github.com/AztecProtocol/aztec-packages/issues/4616)) ([3260081](https://github.com/AztecProtocol/aztec-packages/commit/3260081755bdb3bbd71aaedb2cb129c68110298a)) +* **yellowpaper:** AVM `call` instructions, split out sections, cleanup ([#4594](https://github.com/AztecProtocol/aztec-packages/issues/4594)) ([e63f022](https://github.com/AztecProtocol/aztec-packages/commit/e63f02265d3d2b3c2f3e2a9e35ed6201753512f5)) + ## [0.24.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-packages-v0.23.0...aztec-packages-v0.24.0) (2024-02-13) diff --git a/avm-transpiler/Dockerfile.dockerignore b/avm-transpiler/Dockerfile.dockerignore deleted file mode 100644 index 8edf9d12d62..00000000000 --- a/avm-transpiler/Dockerfile.dockerignore +++ /dev/null @@ -1,7 +0,0 @@ -** - -!avm-transpiler/ -!noir/ -**/target/ -**/node_modules/ -**/packages/ \ No newline at end of file diff --git a/barretenberg/.dockerignore b/barretenberg/.dockerignore deleted file mode 100644 index 2920dd00c6c..00000000000 --- a/barretenberg/.dockerignore +++ /dev/null @@ -1,11 +0,0 @@ -sol/broadcast -sol/cache -sol/out -sol/Dockerfile -sol/lib -sol/.foundry -sol/cache -sol/out -cpp/build -cpp/srs_db/ignition -.gitmodules \ No newline at end of file diff --git a/barretenberg/.gitrepo b/barretenberg/.gitrepo index bf195997490..7f0c2262312 100644 --- a/barretenberg/.gitrepo +++ b/barretenberg/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/AztecProtocol/barretenberg branch = master - commit = a3c713311e45192f775bef06080a6a13e7ab5603 - parent = e6ce08f6d74db76a45e5dea69d5b7531ca99c769 + commit = 026fa53e80e1850bd37137cc528bd366fc61be49 + parent = 8ffe5df71b78ed5100f598f680fbb1fe49b546b3 method = merge cmdver = 0.4.6 diff --git a/barretenberg/CHANGELOG.md b/barretenberg/CHANGELOG.md index e063075e289..177c8e12a22 100644 --- a/barretenberg/CHANGELOG.md +++ b/barretenberg/CHANGELOG.md @@ -1,5 +1,84 @@ # Changelog +## [0.25.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg-v0.24.0...barretenberg-v0.25.0) (2024-03-05) + + +### Features + +* Additional op count timing ([#4722](https://github.com/AztecProtocol/aztec-packages/issues/4722)) ([f0cc760](https://github.com/AztecProtocol/aztec-packages/commit/f0cc76040a2de5d0f827afdb662591232c4ee1ed)) +* Analyze % of time spent on field arithmetic ([#4501](https://github.com/AztecProtocol/aztec-packages/issues/4501)) ([5ddfa16](https://github.com/AztecProtocol/aztec-packages/commit/5ddfa16391f1017219a997c322b061ebe6f34db2)) +* **avm-simulator:** Implement AVM message opcodes (simulator/transpiler/noir-test) ([#4852](https://github.com/AztecProtocol/aztec-packages/issues/4852)) ([c98325d](https://github.com/AztecProtocol/aztec-packages/commit/c98325d23897d23c09faddc4355958406d44faa9)) +* **avm:** Enable main -> mem clk lookups ([#4591](https://github.com/AztecProtocol/aztec-packages/issues/4591)) ([0e503c1](https://github.com/AztecProtocol/aztec-packages/commit/0e503c14c0c20a93e162a90d8d049f094b64de7d)) +* **avm:** Hashing opcodes ([#4526](https://github.com/AztecProtocol/aztec-packages/issues/4526)) ([fe10c70](https://github.com/AztecProtocol/aztec-packages/commit/fe10c7049b3597a96f76a27a22e9233bc3b8ce82)) +* **avm:** Propagate tag err to the main trace for op_return and internal_return ([#4615](https://github.com/AztecProtocol/aztec-packages/issues/4615)) ([427f1d8](https://github.com/AztecProtocol/aztec-packages/commit/427f1d8567a3f68c3093c29a2999096746927548)), closes [#4598](https://github.com/AztecProtocol/aztec-packages/issues/4598) +* Avoid requiring arith gates in sequence ([#4869](https://github.com/AztecProtocol/aztec-packages/issues/4869)) ([0ab0a94](https://github.com/AztecProtocol/aztec-packages/commit/0ab0a94842ce9b174ba82b430a93cba188fe75b0)) +* **bb:** Working msan preset ([#4618](https://github.com/AztecProtocol/aztec-packages/issues/4618)) ([0195ac8](https://github.com/AztecProtocol/aztec-packages/commit/0195ac89a13dc2a7b9caa5a8d8d29458a99c5f76)) +* Benchmark Protogalaxy rounds ([#4316](https://github.com/AztecProtocol/aztec-packages/issues/4316)) ([91af28d](https://github.com/AztecProtocol/aztec-packages/commit/91af28d6e03d85b5c749740c82cf9114379c823a)) +* Bitwise_not avm circuit ([#4548](https://github.com/AztecProtocol/aztec-packages/issues/4548)) ([3a7d31b](https://github.com/AztecProtocol/aztec-packages/commit/3a7d31b200e6e604eea06a40dcf5bf02b088ab79)) +* Equality avm circuit ([#4595](https://github.com/AztecProtocol/aztec-packages/issues/4595)) ([aad7b45](https://github.com/AztecProtocol/aztec-packages/commit/aad7b45aa6d3a4c3df259ea41fdde48bf01139b1)) +* Execution Trace ([#4623](https://github.com/AztecProtocol/aztec-packages/issues/4623)) ([07ac589](https://github.com/AztecProtocol/aztec-packages/commit/07ac589d08964a44ea54a0d9fa0a21db73186aee)) +* Gate blocks ([#4741](https://github.com/AztecProtocol/aztec-packages/issues/4741)) ([61067a5](https://github.com/AztecProtocol/aztec-packages/commit/61067a5cdedfd10fbc32e381083b031bc80fc6d6)) +* Goblin documentation ([#4679](https://github.com/AztecProtocol/aztec-packages/issues/4679)) ([24d918f](https://github.com/AztecProtocol/aztec-packages/commit/24d918f7bd114f2641ae61bcf0da888e06f6520a)) +* Goblin Translator Fuzzer ([#4752](https://github.com/AztecProtocol/aztec-packages/issues/4752)) ([7402517](https://github.com/AztecProtocol/aztec-packages/commit/74025170288e39e1d7516f57df94f22bc30f663c)) +* GoblinUltra Bench ([#4671](https://github.com/AztecProtocol/aztec-packages/issues/4671)) ([319eea9](https://github.com/AztecProtocol/aztec-packages/commit/319eea9e4caf1d1ade00fedface5fab9bbf9db16)) +* Implementing IPA optimisation ([#4363](https://github.com/AztecProtocol/aztec-packages/issues/4363)) ([13647c2](https://github.com/AztecProtocol/aztec-packages/commit/13647c24487116f971c81dfaf4ee4664870522d5)) +* Login to ecr explicitly, faster bootstrap as we only do once. ([#4900](https://github.com/AztecProtocol/aztec-packages/issues/4900)) ([86d6749](https://github.com/AztecProtocol/aztec-packages/commit/86d6749615a533e0a9fbe0a1dca97b38fb14bb5f)) +* Manual ClientIVC breakdown ([#4778](https://github.com/AztecProtocol/aztec-packages/issues/4778)) ([b4cfc89](https://github.com/AztecProtocol/aztec-packages/commit/b4cfc89c0d8286d2dfa3e04c58695d554951c920)) +* Parallel native/wasm bb builds. Better messaging around using ci cache. ([#4766](https://github.com/AztecProtocol/aztec-packages/issues/4766)) ([a924e55](https://github.com/AztecProtocol/aztec-packages/commit/a924e55393daa89fbba3a87cf019977286104b59)) +* Parallelise kernel and function circuit construction in client IVC ([#4841](https://github.com/AztecProtocol/aztec-packages/issues/4841)) ([9c689d8](https://github.com/AztecProtocol/aztec-packages/commit/9c689d8d5a7d330dabafaa7d10c0cfc5e4694921)) +* Separate addition gate after final RAM gate ([#4851](https://github.com/AztecProtocol/aztec-packages/issues/4851)) ([f329db4](https://github.com/AztecProtocol/aztec-packages/commit/f329db4ec08f013bf8f53eb73b18d3d98d98e2e4)) +* Separate arithmetic gate in sort with edges ([#4866](https://github.com/AztecProtocol/aztec-packages/issues/4866)) ([40adc5c](https://github.com/AztecProtocol/aztec-packages/commit/40adc5cdc578c6ff6d6a9aa25c9a2f3506ec1677)) +* Simplify public input copy cycles ([#4753](https://github.com/AztecProtocol/aztec-packages/issues/4753)) ([a714ee0](https://github.com/AztecProtocol/aztec-packages/commit/a714ee027262dba3a083e17878862cd1144a86a6)) +* Update RAM/ROM memory records for new block structure ([#4806](https://github.com/AztecProtocol/aztec-packages/issues/4806)) ([65e4ab9](https://github.com/AztecProtocol/aztec-packages/commit/65e4ab93219118c8ac46a68bc6607ee9d11f6478)) + + +### Bug Fixes + +* Add TODO with issue for num_gates bug ([#4847](https://github.com/AztecProtocol/aztec-packages/issues/4847)) ([f6c558b](https://github.com/AztecProtocol/aztec-packages/commit/f6c558b41d3e003e1626a853aff0b58705847e84)) +* Assembly benching ([#4640](https://github.com/AztecProtocol/aztec-packages/issues/4640)) ([f144745](https://github.com/AztecProtocol/aztec-packages/commit/f14474571210a46e7159cb9d2f0bc9374a837d3d)) +* **bb:** Initialize element::infinity() ([#4664](https://github.com/AztecProtocol/aztec-packages/issues/4664)) ([6813540](https://github.com/AztecProtocol/aztec-packages/commit/6813540731149db1f0d8932598335f95937ada03)) +* Cpp build ([#4918](https://github.com/AztecProtocol/aztec-packages/issues/4918)) ([15df3c0](https://github.com/AztecProtocol/aztec-packages/commit/15df3c08168611f7f65f5837a937031d81bb3566)) +* Debug build ([#4666](https://github.com/AztecProtocol/aztec-packages/issues/4666)) ([acc27b1](https://github.com/AztecProtocol/aztec-packages/commit/acc27b1bd2ec21c7b5c71f02974bd49d29b4caa5)) +* **dsl:** Add full recursive verification test ([#4658](https://github.com/AztecProtocol/aztec-packages/issues/4658)) ([9e09772](https://github.com/AztecProtocol/aztec-packages/commit/9e0977261aea723d6ea68750788f29a40730c404)) +* Fix races in slab allocator and lookup tables and add prepending for op_queues ([#4754](https://github.com/AztecProtocol/aztec-packages/issues/4754)) ([0c99de7](https://github.com/AztecProtocol/aztec-packages/commit/0c99de7c4b9931989824f66dab83cc644578a75c)) +* Fix Translator composer test instability ([#4751](https://github.com/AztecProtocol/aztec-packages/issues/4751)) ([842ba7a](https://github.com/AztecProtocol/aztec-packages/commit/842ba7a720d075632ad2c4b948f874a12cfa3ecd)) +* G2.Serialize sporadic failure ([#4626](https://github.com/AztecProtocol/aztec-packages/issues/4626)) ([c9e6bb1](https://github.com/AztecProtocol/aztec-packages/commit/c9e6bb1391070b6551b313b85fe73742ff0966fc)) +* Get_wires for ultra ([#4605](https://github.com/AztecProtocol/aztec-packages/issues/4605)) ([512110e](https://github.com/AztecProtocol/aztec-packages/commit/512110e4bdc353b01ee92fb5b2ff5f6e6f875fbb)) +* Master borked arithmetic tests ([#4606](https://github.com/AztecProtocol/aztec-packages/issues/4606)) ([472c54a](https://github.com/AztecProtocol/aztec-packages/commit/472c54a7e89001f5f752da670cc25ec1a537da87)) +* Msan build ([#4646](https://github.com/AztecProtocol/aztec-packages/issues/4646)) ([886cc75](https://github.com/AztecProtocol/aztec-packages/commit/886cc7585f935f4f12257444af7862b51dc91584)) +* MSAN msgpack noise ([#4677](https://github.com/AztecProtocol/aztec-packages/issues/4677)) ([1abae28](https://github.com/AztecProtocol/aztec-packages/commit/1abae28580354f5ccc620dbd717bf079f39fb445)) +* Remove the `VerificationKey` from `ProverInstance` ([#4908](https://github.com/AztecProtocol/aztec-packages/issues/4908)) ([8619c08](https://github.com/AztecProtocol/aztec-packages/commit/8619c084cdfd061f284058b00a96f16fbbca65bf)) +* Use size hint for ivc circuits ([#4802](https://github.com/AztecProtocol/aztec-packages/issues/4802)) ([035cff4](https://github.com/AztecProtocol/aztec-packages/commit/035cff451ca2171e08279b9d36b23f38b840efea)) + + +### Miscellaneous + +* Add pow poly bench and link optimization issues ([#4725](https://github.com/AztecProtocol/aztec-packages/issues/4725)) ([faa9586](https://github.com/AztecProtocol/aztec-packages/commit/faa9586ef702e3f150e6aa8217dcbcd63611dea2)) +* Address comments ([#4772](https://github.com/AztecProtocol/aztec-packages/issues/4772)) ([10d90ab](https://github.com/AztecProtocol/aztec-packages/commit/10d90ab3a15de66f4b8a64464fe8e15f33a0589d)) +* **avm:** Remove some leftover files related to Avm-mini (replaced by Avm) ([#4715](https://github.com/AztecProtocol/aztec-packages/issues/4715)) ([8c697ce](https://github.com/AztecProtocol/aztec-packages/commit/8c697ce187b4bb1c66f1146ebbc39567a46f35f8)) +* **bb:** Allow dynamic plookup tables ([#4667](https://github.com/AztecProtocol/aztec-packages/issues/4667)) ([5920012](https://github.com/AztecProtocol/aztec-packages/commit/592001255a999abb7167f885a5def7f8651d63a7)) +* **bb:** More namespaces under bb ([#4348](https://github.com/AztecProtocol/aztec-packages/issues/4348)) ([00ba983](https://github.com/AztecProtocol/aztec-packages/commit/00ba9837606f33ccbc5c0c40be22b11a736b1608)) +* **bb:** Small test improvements ([#4568](https://github.com/AztecProtocol/aztec-packages/issues/4568)) ([e23d048](https://github.com/AztecProtocol/aztec-packages/commit/e23d048e916fa12966fe01d1a8c0d3bfb50c2943)) +* **bb:** Use RefArray where possible ([#4686](https://github.com/AztecProtocol/aztec-packages/issues/4686)) ([5b4e1a6](https://github.com/AztecProtocol/aztec-packages/commit/5b4e1a61216655cebb58863d26d418b23881dd02)) +* Bootstrap improvements. ([#4711](https://github.com/AztecProtocol/aztec-packages/issues/4711)) ([1375233](https://github.com/AztecProtocol/aztec-packages/commit/13752339334be9c8cc0ae500d0e932f76d18a77d)) +* Get rid of Honk UltraComposer ([#4875](https://github.com/AztecProtocol/aztec-packages/issues/4875)) ([7e52c29](https://github.com/AztecProtocol/aztec-packages/commit/7e52c2971b91dfb0f07c178b2adb4427363acd1e)) +* Implement poseidon2 opcode ([#4446](https://github.com/AztecProtocol/aztec-packages/issues/4446)) ([491a8df](https://github.com/AztecProtocol/aztec-packages/commit/491a8dfe81a33a7552686f70833f6130da944142)) +* Make first iteration of protogalaxy more efficient ([#4630](https://github.com/AztecProtocol/aztec-packages/issues/4630)) ([4c7f24f](https://github.com/AztecProtocol/aztec-packages/commit/4c7f24f8ea8c21bc8114ead67d2082a06c9c5493)) +* Min noir build ([#4812](https://github.com/AztecProtocol/aztec-packages/issues/4812)) ([01dd0a9](https://github.com/AztecProtocol/aztec-packages/commit/01dd0a9318de6c69d60e15d56b0fb29d2ec51b28)) +* Move remaining data out of Honk UltraComposer ([#4848](https://github.com/AztecProtocol/aztec-packages/issues/4848)) ([823e071](https://github.com/AztecProtocol/aztec-packages/commit/823e071a0988cae906c13fa47e501fe9912788dc)) +* Move vk computation out of Honk Ultra composer ([#4811](https://github.com/AztecProtocol/aztec-packages/issues/4811)) ([f354e89](https://github.com/AztecProtocol/aztec-packages/commit/f354e899b4b35dd6d06699f0dbff48f7ea9ed9c3)) +* Pull noir for u64 as array lengths ([#4787](https://github.com/AztecProtocol/aztec-packages/issues/4787)) ([e69b586](https://github.com/AztecProtocol/aztec-packages/commit/e69b58660ff843350e1e098d8f1a84f4ce3d3c34)) +* Remove VK computation Pg prover flow; improve benchmark to reflect possible optimization ([#4639](https://github.com/AztecProtocol/aztec-packages/issues/4639)) ([c1709b3](https://github.com/AztecProtocol/aztec-packages/commit/c1709b3d5fe615d980b2ebd9283fb841d9e6a85a)) +* Remove WASMTIME_ENV_HACK ([#4714](https://github.com/AztecProtocol/aztec-packages/issues/4714)) ([50f89f1](https://github.com/AztecProtocol/aztec-packages/commit/50f89f1832154d526908c55ab296aaf9bacf3608)) +* Rename avm_mini to avm ([#4580](https://github.com/AztecProtocol/aztec-packages/issues/4580)) ([5896a92](https://github.com/AztecProtocol/aztec-packages/commit/5896a920bc4f5fd239d69795872567af6ccbe803)), closes [#4533](https://github.com/AztecProtocol/aztec-packages/issues/4533) +* Squash yp ypb + other build improvements. ([#4901](https://github.com/AztecProtocol/aztec-packages/issues/4901)) ([be5855c](https://github.com/AztecProtocol/aztec-packages/commit/be5855cdbd1993155bd228afbeafee2c447b46a5)) +* Toy avm snake case ([#4584](https://github.com/AztecProtocol/aztec-packages/issues/4584)) ([d071768](https://github.com/AztecProtocol/aztec-packages/commit/d07176863011382c34af5d5c80c596f737369703)) + + +### Documentation + +* **yellowpaper:** AVM `call` instructions, split out sections, cleanup ([#4594](https://github.com/AztecProtocol/aztec-packages/issues/4594)) ([e63f022](https://github.com/AztecProtocol/aztec-packages/commit/e63f02265d3d2b3c2f3e2a9e35ed6201753512f5)) + ## [0.24.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg-v0.23.0...barretenberg-v0.24.0) (2024-02-13) diff --git a/barretenberg/acir_tests/.dockerignore b/barretenberg/acir_tests/.dockerignore deleted file mode 100644 index 9dcf5756d6d..00000000000 --- a/barretenberg/acir_tests/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -acir_tests* -**/node_modules -Dockerfile* \ No newline at end of file diff --git a/barretenberg/cpp/.dockerignore b/barretenberg/cpp/.dockerignore deleted file mode 100644 index e9b3e4636d3..00000000000 --- a/barretenberg/cpp/.dockerignore +++ /dev/null @@ -1,27 +0,0 @@ -# We want to explicitly define what's allowed into the context. -* - -# Important cmake files. -!CMakeLists.txt -!CMakePresets.json -!cmake - -# Important srs_db files. -!srs_db/download_ignition.sh -!srs_db/download_grumpkin.sh -!srs_db/ignition/checksums - -# Source code. -!src/CMakeLists.txt -!src/barretenberg -!src/msgpack-c - -# Needed scripts. -!scripts/install-wasi-sdk.sh -!scripts/strip-wasm.sh -!scripts/ci -!./.clang-format -!./format.sh - -# Doxygen stuff. -!docs diff --git a/barretenberg/cpp/CMakeLists.txt b/barretenberg/cpp/CMakeLists.txt index da5c17d85ff..f5f7b1bb947 100644 --- a/barretenberg/cpp/CMakeLists.txt +++ b/barretenberg/cpp/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.24 FATAL_ERROR) project( Barretenberg DESCRIPTION "BN254 elliptic curve library, and PLONK SNARK prover" - VERSION 0.24.0 # x-release-please-version + VERSION 0.25.0 # x-release-please-version LANGUAGES CXX C ) # Insert version into `bb` config file diff --git a/barretenberg/sol/Dockerfile b/barretenberg/sol/Dockerfile index bae96012f4c..df99298fb85 100644 --- a/barretenberg/sol/Dockerfile +++ b/barretenberg/sol/Dockerfile @@ -23,20 +23,14 @@ RUN apk update && apk add git curl build-base openmp-dev bash COPY --from=0 /usr/src/barretenberg/cpp/build/bin /usr/src/barretenberg/cpp/build/bin COPY --from=0 /usr/src/barretenberg/cpp/srs_db /usr/src/barretenberg/cpp/srs_db WORKDIR /usr/src/barretenberg/sol -RUN git init COPY ./sol . # Copy forge binary directly from foundry COPY --from=ghcr.io/foundry-rs/foundry:latest /usr/local/bin/forge /usr/local/bin/forge -RUN forge install --no-commit \ - https://github.com/foundry-rs/forge-std \ - https://github.com/openzeppelin/openzeppelin-contracts \ - https://github.com/Arachnid/solidity-stringutils - RUN cd ../cpp/srs_db && ./download_ignition.sh 3 && cd ../../sol RUN ./scripts/init.sh -# TestBase is excluded as it is just boilerplate +# TestBase is excluded as it is just boilerplate RUN forge test --no-match-contract TestBase \ No newline at end of file diff --git a/barretenberg/ts/.dockerignore b/barretenberg/ts/.dockerignore deleted file mode 100644 index ab18ce85c3d..00000000000 --- a/barretenberg/ts/.dockerignore +++ /dev/null @@ -1,7 +0,0 @@ -dest -node_modules -.dockerignore -Dockerfile -.yarn -!.yarn/releases -.tsbuildinfo* \ No newline at end of file diff --git a/barretenberg/ts/CHANGELOG.md b/barretenberg/ts/CHANGELOG.md index d4183d89809..6e9b3178a27 100644 --- a/barretenberg/ts/CHANGELOG.md +++ b/barretenberg/ts/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## [0.25.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg.js-v0.24.0...barretenberg.js-v0.25.0) (2024-03-05) + + +### Features + +* Parallel native/wasm bb builds. Better messaging around using ci cache. ([#4766](https://github.com/AztecProtocol/aztec-packages/issues/4766)) ([a924e55](https://github.com/AztecProtocol/aztec-packages/commit/a924e55393daa89fbba3a87cf019977286104b59)) + + +### Miscellaneous + +* Bootstrap improvements. ([#4711](https://github.com/AztecProtocol/aztec-packages/issues/4711)) ([1375233](https://github.com/AztecProtocol/aztec-packages/commit/13752339334be9c8cc0ae500d0e932f76d18a77d)) +* Implement poseidon2 opcode ([#4446](https://github.com/AztecProtocol/aztec-packages/issues/4446)) ([491a8df](https://github.com/AztecProtocol/aztec-packages/commit/491a8dfe81a33a7552686f70833f6130da944142)) +* Squash yp ypb + other build improvements. ([#4901](https://github.com/AztecProtocol/aztec-packages/issues/4901)) ([be5855c](https://github.com/AztecProtocol/aztec-packages/commit/be5855cdbd1993155bd228afbeafee2c447b46a5)) + ## [0.24.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg.js-v0.23.0...barretenberg.js-v0.24.0) (2024-02-13) diff --git a/barretenberg/ts/package.json b/barretenberg/ts/package.json index 4ad19c24ad7..0cb4b05ccf4 100644 --- a/barretenberg/ts/package.json +++ b/barretenberg/ts/package.json @@ -1,6 +1,6 @@ { "name": "@aztec/bb.js", - "version": "0.24.0", + "version": "0.25.0", "homepage": "https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg/ts", "license": "MIT", "type": "module", diff --git a/boxes/.dockerignore b/boxes/.dockerignore deleted file mode 100644 index 7eab5f5a5cb..00000000000 --- a/boxes/.dockerignore +++ /dev/null @@ -1,9 +0,0 @@ -.yarn/* -!.yarn/releases -dest -node_modules -.tsbuildinfo -Dockerfile* -.dockerignore -docker-compose.yml -**/artifacts \ No newline at end of file diff --git a/boxes/Dockerfile b/boxes/Dockerfile index dc1ca00f1bd..9c40953a0eb 100644 --- a/boxes/Dockerfile +++ b/boxes/Dockerfile @@ -1,4 +1,3 @@ -# Builds the boxes (they were copied into yarn-project-base so the cli can unbox). # Produces a container that can be run to test a specific box. See docker-compose.yml. FROM aztecprotocol/aztec AS aztec FROM aztecprotocol/noir as noir @@ -10,7 +9,9 @@ RUN apt update && apt install netcat-openbsd COPY --from=aztec /usr/src /usr/src COPY --from=noir /usr/src/noir/noir-repo/target/release/nargo /usr/src/noir/noir-repo/target/release/nargo COPY --from=noir-projects /usr/src/noir-projects/aztec-nr /usr/src/noir-projects/aztec-nr +COPY --from=noir-projects /usr/src/noir-projects/noir-protocol-circuits/crates/types /usr/src/noir-projects/noir-protocol-circuits/crates/types WORKDIR /usr/src/boxes +COPY . . ENV AZTEC_NARGO=/usr/src/noir/noir-repo/target/release/nargo ENV AZTEC_CLI=/usr/src/yarn-project/cli/aztec-cli-dest RUN yarn && yarn build diff --git a/boxes/Dockerfile.files b/boxes/Dockerfile.files deleted file mode 100644 index 411f2bf1bc7..00000000000 --- a/boxes/Dockerfile.files +++ /dev/null @@ -1,4 +0,0 @@ -# For carrying the files into yarn-project-base, needed for the cli to do unboxing. -FROM scratch -WORKDIR /usr/src/boxes -COPY . . \ No newline at end of file diff --git a/boxes/react/.gitignore b/boxes/react/.gitignore index 7e20050d72e..6a477d2e402 100644 --- a/boxes/react/.gitignore +++ b/boxes/react/.gitignore @@ -6,3 +6,7 @@ dist artifacts src/contracts/target src/contracts/log +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/boxes/react/package.json b/boxes/react/package.json index 9a5969152b6..6234edb6857 100644 --- a/boxes/react/package.json +++ b/boxes/react/package.json @@ -11,10 +11,12 @@ "prep": "yarn clean && yarn compile && yarn codegen", "dev": "yarn prep && webpack serve --mode development", "build": "yarn prep && webpack", - "serve": "serve -p 3000 ./dist", + "serve": "webpack serve --no-open --mode development", "formatting": "prettier --check ./src && eslint ./src", "formatting:fix": "prettier -w ./src", - "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --runInBand" + "test": "yarn test:node && yarn test:browser", + "test:node": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --runInBand", + "test:browser": "npx playwright test" }, "jest": { "preset": "ts-jest/presets/default-esm", @@ -45,6 +47,7 @@ "yup": "^1.2.0" }, "devDependencies": { + "@playwright/test": "1.42.0", "@types/jest": "^29.5.0", "@types/node": "^20.5.9", "@types/react": "^18.2.15", diff --git a/boxes/react/playwright.config.ts b/boxes/react/playwright.config.ts new file mode 100644 index 00000000000..96c5e3b073d --- /dev/null +++ b/boxes/react/playwright.config.ts @@ -0,0 +1,38 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './tests', + testMatch: '**.spec.ts', + fullyParallel: true, + retries: 3, + workers: process.env.CI ? 1 : 3, + reporter: 'list', + use: { + baseURL: 'http://127.0.0.1:5173', + trace: 'on-first-retry', + screenshot: 'only-on-failure', + video: 'on-first-retry', + }, + expect: { + timeout: 90000, + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + ], + webServer: { + command: 'yarn serve', + port: 5173, + }, +}); diff --git a/boxes/react/tests/browser.spec.ts b/boxes/react/tests/browser.spec.ts new file mode 100644 index 00000000000..45072b6edd2 --- /dev/null +++ b/boxes/react/tests/browser.spec.ts @@ -0,0 +1,25 @@ +import { test, expect } from '@playwright/test'; + +test('test', async ({ page }) => { + test.slow(); + await page.goto('/'); + + // Deploy contract + await page.getByRole('button', { name: 'Deploy dummy contract' }).click(); + await expect(page.getByText('Deploying contract...')).toBeVisible(); + await expect(page.getByText('Address:')).toBeVisible(); + + // Read number + await page.getByRole('button', { name: 'Read' }).click(); + await expect(page.getByText('Number is:')).toBeVisible(); + + // Set number + await page.locator('#numberToSet').fill('1'); + await page.getByRole('button', { name: 'Write' }).click(); + await expect(page.getByText('Setting number...')).toBeVisible(); + await expect(page.getByText('Number set to: 1')).toBeVisible(); + + // Read number + await page.getByRole('button', { name: 'Read' }).click(); + await expect(page.getByText('Number is: 1')).toBeVisible(); +}); diff --git a/boxes/react/tests/blank.contract.test.ts b/boxes/react/tests/node.test.ts similarity index 100% rename from boxes/react/tests/blank.contract.test.ts rename to boxes/react/tests/node.test.ts diff --git a/boxes/react/webpack.config.js b/boxes/react/webpack.config.js index 7746f6e0b16..d5e6fc11e01 100644 --- a/boxes/react/webpack.config.js +++ b/boxes/react/webpack.config.js @@ -1,40 +1,41 @@ -import { createRequire } from "module"; -import webpack from "webpack"; -import HtmlWebpackPlugin from "html-webpack-plugin"; +import { createRequire } from 'module'; +import webpack from 'webpack'; +import HtmlWebpackPlugin from 'html-webpack-plugin'; const require = createRequire(import.meta.url); export default (_, argv) => ({ - target: "web", - mode: "production", - devtool: "source-map", + target: 'web', + mode: 'production', + devtool: 'source-map', entry: { - main: "./src/index.tsx", + main: './src/index.tsx', }, module: { rules: [ { test: /\.tsx?$/, - use: "ts-loader", + use: 'ts-loader', }, { test: /\.css$/i, - use: ["style-loader", "css-loader", "postcss-loader"], + use: ['style-loader', 'css-loader', 'postcss-loader'], }, ], }, plugins: [ new HtmlWebpackPlugin({ - template: "./index.html", + template: './index.html', }), new webpack.DefinePlugin({ - "process.env": { - NODE_ENV: JSON.stringify(argv.mode || "production"), + 'process.env': { + NODE_ENV: JSON.stringify(argv.mode || 'production'), + PXE_URL: JSON.stringify(process.env.PXE_URL || 'http://localhost:8080'), }, }), - new webpack.ProvidePlugin({ Buffer: ["buffer", "Buffer"] }), + new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] }), ], resolve: { - extensions: [".tsx", ".ts", ".js"], + extensions: ['.tsx', '.ts', '.js'], fallback: { crypto: false, os: false, @@ -42,12 +43,12 @@ export default (_, argv) => ({ path: false, url: false, worker_threads: false, - events: require.resolve("events/"), - buffer: require.resolve("buffer/"), - util: require.resolve("util/"), - stream: require.resolve("stream-browserify"), - string_decoder: require.resolve("string_decoder/"), - tty: require.resolve("tty-browserify"), + events: require.resolve('events/'), + buffer: require.resolve('buffer/'), + util: require.resolve('util/'), + stream: require.resolve('stream-browserify'), + string_decoder: require.resolve('string_decoder/'), + tty: require.resolve('tty-browserify'), }, }, devServer: { diff --git a/boxes/vanilla-js/src/index.ts b/boxes/vanilla-js/src/index.ts index 2ddcd10107a..c3cd0e55712 100644 --- a/boxes/vanilla-js/src/index.ts +++ b/boxes/vanilla-js/src/index.ts @@ -1,20 +1,14 @@ -import { - GrumpkinScalar, - createPXEClient, - AccountManager, - ContractDeployer, - Fr, - AccountWalletWithPrivateKey, -} from '@aztec/aztec.js'; +import { GrumpkinScalar, createPXEClient, AccountManager, ContractDeployer, Fr, Wallet } from '@aztec/aztec.js'; import { SingleKeyAccountContract } from '@aztec/accounts/single_key'; import { VanillaContract } from '../artifacts/Vanilla'; const privateKey: GrumpkinScalar = GrumpkinScalar.random(); const pxe = createPXEClient(process.env.PXE_URL || 'http://localhost:8080'); + const account = new AccountManager(pxe, privateKey, new SingleKeyAccountContract(privateKey)); let contract: any = null; -let wallet: AccountWalletWithPrivateKey | null = null; +let wallet: Wallet | null = null; const setWait = (state: boolean): void => document.querySelectorAll('*').forEach((e: HTMLElement & HTMLButtonElement) => { @@ -47,9 +41,9 @@ document.querySelector('#set').addEventListener('submit', async (e: Event) => { const { value } = document.querySelector('#number') as HTMLInputElement; const owner = wallet.getCompleteAddress().address; await contract.methods.setNumber(parseInt(value), owner).send().wait(); - alert('Number set!'); setWait(false); + alert('Number set!'); }); document.querySelector('#get').addEventListener('click', async () => { diff --git a/boxes/yarn.lock b/boxes/yarn.lock index f05ae16efc5..596734f6248 100644 --- a/boxes/yarn.lock +++ b/boxes/yarn.lock @@ -20,12 +20,12 @@ __metadata: linkType: hard "@ampproject/remapping@npm:^2.2.0": - version: 2.2.1 - resolution: "@ampproject/remapping@npm:2.2.1" + version: 2.3.0 + resolution: "@ampproject/remapping@npm:2.3.0" dependencies: - "@jridgewell/gen-mapping": "npm:^0.3.0" - "@jridgewell/trace-mapping": "npm:^0.3.9" - checksum: 10c0/92ce5915f8901d8c7cd4f4e6e2fe7b9fd335a29955b400caa52e0e5b12ca3796ada7c2f10e78c9c5b0f9c2539dff0ffea7b19850a56e1487aa083531e1e46d43 + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/81d63cca5443e0f0c72ae18b544cc28c7c0ec2cea46e7cb888bb0e0f411a1191d0d6b7af798d54e30777d8d1488b2ec0732aac2be342d3d7d3ffd271c6f489ed languageName: node linkType: hard @@ -77,6 +77,7 @@ __metadata: dependencies: "@aztec/accounts": "npm:latest" "@aztec/aztec.js": "npm:latest" + "@playwright/test": "npm:1.42.0" "@types/jest": "npm:^29.5.0" "@types/node": "npm:^20.5.9" "@types/react": "npm:^18.2.15" @@ -261,25 +262,25 @@ __metadata: linkType: hard "@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.23.9": - version: 7.23.9 - resolution: "@babel/core@npm:7.23.9" + version: 7.24.0 + resolution: "@babel/core@npm:7.24.0" dependencies: "@ampproject/remapping": "npm:^2.2.0" "@babel/code-frame": "npm:^7.23.5" "@babel/generator": "npm:^7.23.6" "@babel/helper-compilation-targets": "npm:^7.23.6" "@babel/helper-module-transforms": "npm:^7.23.3" - "@babel/helpers": "npm:^7.23.9" - "@babel/parser": "npm:^7.23.9" - "@babel/template": "npm:^7.23.9" - "@babel/traverse": "npm:^7.23.9" - "@babel/types": "npm:^7.23.9" + "@babel/helpers": "npm:^7.24.0" + "@babel/parser": "npm:^7.24.0" + "@babel/template": "npm:^7.24.0" + "@babel/traverse": "npm:^7.24.0" + "@babel/types": "npm:^7.24.0" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 10c0/03883300bf1252ab4c9ba5b52f161232dd52873dbe5cde9289bb2bb26e935c42682493acbac9194a59a3b6cbd17f4c4c84030db8d6d482588afe64531532ff9b + checksum: 10c0/bb37cbf0bdfd676b246af0a3d9a7932d10573f2d45114fdda02a71889e35530ce13d8930177e78b065d6734b8d45a4fbf7c77f223b1d44b4a28cfe5fefee93ed languageName: node linkType: hard @@ -359,9 +360,9 @@ __metadata: linkType: hard "@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.8.0": - version: 7.22.5 - resolution: "@babel/helper-plugin-utils@npm:7.22.5" - checksum: 10c0/d2c4bfe2fa91058bcdee4f4e57a3f4933aed7af843acfd169cd6179fab8d13c1d636474ecabb2af107dc77462c7e893199aa26632bac1c6d7e025a17cbb9d20d + version: 7.24.0 + resolution: "@babel/helper-plugin-utils@npm:7.24.0" + checksum: 10c0/90f41bd1b4dfe7226b1d33a4bb745844c5c63e400f9e4e8bf9103a7ceddd7d425d65333b564d9daba3cebd105985764d51b4bd4c95822b97c2e3ac1201a8a5da languageName: node linkType: hard @@ -404,14 +405,14 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.23.9": - version: 7.23.9 - resolution: "@babel/helpers@npm:7.23.9" +"@babel/helpers@npm:^7.24.0": + version: 7.24.0 + resolution: "@babel/helpers@npm:7.24.0" dependencies: - "@babel/template": "npm:^7.23.9" - "@babel/traverse": "npm:^7.23.9" - "@babel/types": "npm:^7.23.9" - checksum: 10c0/f69fd0aca96a6fb8bd6dd044cd8a5c0f1851072d4ce23355345b9493c4032e76d1217f86b70df795e127553cf7f3fcd1587ede9d1b03b95e8b62681ca2165b87 + "@babel/template": "npm:^7.24.0" + "@babel/traverse": "npm:^7.24.0" + "@babel/types": "npm:^7.24.0" + checksum: 10c0/dd27c9f11c1c5244ef312fae37636f2fcc69c541c46508017b846c4cf680af059f1922ce84e3f778f123a70d027ded75c96070ee8e906f3bc52dc26dc43df608 languageName: node linkType: hard @@ -426,12 +427,12 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9": - version: 7.23.9 - resolution: "@babel/parser@npm:7.23.9" +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.24.0": + version: 7.24.0 + resolution: "@babel/parser@npm:7.24.0" bin: parser: ./bin/babel-parser.js - checksum: 10c0/7df97386431366d4810538db4b9ec538f4377096f720c0591c7587a16f6810e62747e9fbbfa1ff99257fd4330035e4fb1b5b77c7bd3b97ce0d2e3780a6618975 + checksum: 10c0/77593d0b9de9906823c4d653bb6cda1c7593837598516330f655f70cba6224a37def7dbe5b4dad0038482d407d8d209eb8be5f48ca9a13357d769f829c5adb8e languageName: node linkType: hard @@ -589,20 +590,20 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.22.15, @babel/template@npm:^7.23.9, @babel/template@npm:^7.3.3": - version: 7.23.9 - resolution: "@babel/template@npm:7.23.9" +"@babel/template@npm:^7.22.15, @babel/template@npm:^7.24.0, @babel/template@npm:^7.3.3": + version: 7.24.0 + resolution: "@babel/template@npm:7.24.0" dependencies: "@babel/code-frame": "npm:^7.23.5" - "@babel/parser": "npm:^7.23.9" - "@babel/types": "npm:^7.23.9" - checksum: 10c0/0e8b60119433787742bc08ae762bbd8d6755611c4cabbcb7627b292ec901a55af65d93d1c88572326069efb64136ef151ec91ffb74b2df7689bbab237030833a + "@babel/parser": "npm:^7.24.0" + "@babel/types": "npm:^7.24.0" + checksum: 10c0/9d3dd8d22fe1c36bc3bdef6118af1f4b030aaf6d7d2619f5da203efa818a2185d717523486c111de8d99a8649ddf4bbf6b2a7a64962d8411cf6a8fa89f010e54 languageName: node linkType: hard -"@babel/traverse@npm:^7.23.9": - version: 7.23.9 - resolution: "@babel/traverse@npm:7.23.9" +"@babel/traverse@npm:^7.24.0": + version: 7.24.0 + resolution: "@babel/traverse@npm:7.24.0" dependencies: "@babel/code-frame": "npm:^7.23.5" "@babel/generator": "npm:^7.23.6" @@ -610,22 +611,22 @@ __metadata: "@babel/helper-function-name": "npm:^7.23.0" "@babel/helper-hoist-variables": "npm:^7.22.5" "@babel/helper-split-export-declaration": "npm:^7.22.6" - "@babel/parser": "npm:^7.23.9" - "@babel/types": "npm:^7.23.9" + "@babel/parser": "npm:^7.24.0" + "@babel/types": "npm:^7.24.0" debug: "npm:^4.3.1" globals: "npm:^11.1.0" - checksum: 10c0/d1615d1d02f04d47111a7ea4446a1a6275668ca39082f31d51f08380de9502e19862be434eaa34b022ce9a17dbb8f9e2b73a746c654d9575f3a680a7ffdf5630 + checksum: 10c0/55ffd2b0ce0fbd0a09051edc4def4fb1e96f35e0b100c0dc2a7429df569971ae312c290e980e423471f350961705698a257c7eea8c8304918024cc26f02468ba languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.6, @babel/types@npm:^7.23.9, @babel/types@npm:^7.3.3, @babel/types@npm:^7.8.3": - version: 7.23.9 - resolution: "@babel/types@npm:7.23.9" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.6, @babel/types@npm:^7.24.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.8.3": + version: 7.24.0 + resolution: "@babel/types@npm:7.24.0" dependencies: "@babel/helper-string-parser": "npm:^7.23.4" "@babel/helper-validator-identifier": "npm:^7.22.20" to-fast-properties: "npm:^2.0.0" - checksum: 10c0/edc7bb180ce7e4d2aea10c6972fb10474341ac39ba8fdc4a27ffb328368dfdfbf40fca18e441bbe7c483774500d5c05e222cec276c242e952853dcaf4eb884f7 + checksum: 10c0/777a0bb5dbe038ca4c905fdafb1cdb6bdd10fe9d63ce13eca0bd91909363cbad554a53dc1f902004b78c1dcbc742056f877f2c99eeedff647333b1fadf51235d languageName: node linkType: hard @@ -687,10 +688,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:8.56.0": - version: 8.56.0 - resolution: "@eslint/js@npm:8.56.0" - checksum: 10c0/60b3a1cf240e2479cec9742424224465dc50e46d781da1b7f5ef240501b2d1202c225bd456207faac4b34a64f4765833345bc4ddffd00395e1db40fa8c426f5a +"@eslint/js@npm:8.57.0": + version: 8.57.0 + resolution: "@eslint/js@npm:8.57.0" + checksum: 10c0/9a518bb8625ba3350613903a6d8c622352ab0c6557a59fe6ff6178bf882bf57123f9d92aa826ee8ac3ee74b9c6203fe630e9ee00efb03d753962dcf65ee4bd94 languageName: node linkType: hard @@ -701,7 +702,7 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.11.13": +"@humanwhocodes/config-array@npm:^0.11.14": version: 0.11.14 resolution: "@humanwhocodes/config-array@npm:0.11.14" dependencies: @@ -1059,14 +1060,14 @@ __metadata: languageName: node linkType: hard -"@jridgewell/gen-mapping@npm:^0.3.0, @jridgewell/gen-mapping@npm:^0.3.2": - version: 0.3.3 - resolution: "@jridgewell/gen-mapping@npm:0.3.3" +"@jridgewell/gen-mapping@npm:^0.3.0, @jridgewell/gen-mapping@npm:^0.3.2, @jridgewell/gen-mapping@npm:^0.3.5": + version: 0.3.5 + resolution: "@jridgewell/gen-mapping@npm:0.3.5" dependencies: - "@jridgewell/set-array": "npm:^1.0.1" + "@jridgewell/set-array": "npm:^1.2.1" "@jridgewell/sourcemap-codec": "npm:^1.4.10" - "@jridgewell/trace-mapping": "npm:^0.3.9" - checksum: 10c0/376fc11cf5a967318ba3ddd9d8e91be528eab6af66810a713c49b0c3f8dc67e9949452c51c38ab1b19aa618fb5e8594da5a249977e26b1e7fea1ee5a1fcacc74 + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/1be4fd4a6b0f41337c4f5fdf4afc3bd19e39c3691924817108b82ffcb9c9e609c273f936932b9fba4b3a298ce2eb06d9bff4eb1cc3bd81c4f4ee1b4917e25feb languageName: node linkType: hard @@ -1077,10 +1078,10 @@ __metadata: languageName: node linkType: hard -"@jridgewell/set-array@npm:^1.0.1": - version: 1.1.2 - resolution: "@jridgewell/set-array@npm:1.1.2" - checksum: 10c0/bc7ab4c4c00470de4e7562ecac3c0c84f53e7ee8a711e546d67c47da7febe7c45cd67d4d84ee3c9b2c05ae8e872656cdded8a707a283d30bd54fbc65aef821ab +"@jridgewell/set-array@npm:^1.2.1": + version: 1.2.1 + resolution: "@jridgewell/set-array@npm:1.2.1" + checksum: 10c0/2a5aa7b4b5c3464c895c802d8ae3f3d2b92fcbe84ad12f8d0bfbb1f5ad006717e7577ee1fd2eac00c088abe486c7adb27976f45d2941ff6b0b92b2c3302c60f4 languageName: node linkType: hard @@ -1111,13 +1112,13 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.20, @jridgewell/trace-mapping@npm:^0.3.9": - version: 0.3.22 - resolution: "@jridgewell/trace-mapping@npm:0.3.22" +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.20, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.9": + version: 0.3.25 + resolution: "@jridgewell/trace-mapping@npm:0.3.25" dependencies: "@jridgewell/resolve-uri": "npm:^3.1.0" "@jridgewell/sourcemap-codec": "npm:^1.4.14" - checksum: 10c0/18cf19f88e2792c1c91515f2b629aae05f3cdbb2e60c3886e16e80725234ce26dd10144c4981c05d9366e7094498c0b4fe5c1a89f4a730d7376a4ba4af448149 + checksum: 10c0/3d1ce6ebc69df9682a5a8896b414c6537e428a1d68b02fcc8363b04284a8ca0df04d0ee3013132252ab14f2527bc13bea6526a912ecb5658f0e39fd2860b4df4 languageName: node linkType: hard @@ -1468,12 +1469,12 @@ __metadata: linkType: hard "@types/eslint@npm:*": - version: 8.56.2 - resolution: "@types/eslint@npm:8.56.2" + version: 8.56.5 + resolution: "@types/eslint@npm:8.56.5" dependencies: "@types/estree": "npm:*" "@types/json-schema": "npm:*" - checksum: 10c0/e33ca87a30a9454ba9943e1270ac759996f5fe598a1c1afbaec1d1e7346a339e20bf2a9d81f177067116bbaa6cfa4f748993cb338f57978ae862ad38ffae56fe + checksum: 10c0/1d5d70ea107c63adfaf63020f85859c404f90c21ada2a655376b8e76109df354643797e30c7afc3b2de84797d9f5ce9f03f53a5d29a186706a44afd90f76597c languageName: node linkType: hard @@ -1639,11 +1640,11 @@ __metadata: linkType: hard "@types/node@npm:*, @types/node@npm:^20.11.16, @types/node@npm:^20.11.17, @types/node@npm:^20.5.9": - version: 20.11.19 - resolution: "@types/node@npm:20.11.19" + version: 20.11.24 + resolution: "@types/node@npm:20.11.24" dependencies: undici-types: "npm:~5.26.4" - checksum: 10c0/f451ef0a1d78f29c57bad7b77e49ebec945f2a6d0d7a89851d7e185ee9fe7ad94d651c0dfbcb7858c9fa791310c8b40a881e2260f56bd3c1b7e7ae92723373ae + checksum: 10c0/5a62225eb4797b41e6953f9c08c4611d607b5422ddd153312fc81ed6ed37115228ae27e3e3caa1a3bf52d88310306a196ba1cfbd8b2ec918a20f64d80dfa22c9 languageName: node linkType: hard @@ -1662,9 +1663,9 @@ __metadata: linkType: hard "@types/qs@npm:*": - version: 6.9.11 - resolution: "@types/qs@npm:6.9.11" - checksum: 10c0/657a50f05b694d6fd3916d24177cfa0f3b8b87d9deff4ffa4dddcb0b03583ebf7c47b424b8de400270fb9a5cc1e9cf790dd82c833c6935305851e7da8ede3ff5 + version: 6.9.12 + resolution: "@types/qs@npm:6.9.12" + checksum: 10c0/21a74f2b78d0839cee37f1a632f3361352f7dceac9edffd117227a695a13e58e18c138aac1f29403f2408221e678f538ca0b37d55012f8bba96d55905edbfe82 languageName: node linkType: hard @@ -1685,13 +1686,13 @@ __metadata: linkType: hard "@types/react@npm:*, @types/react@npm:^18.2.15": - version: 18.2.57 - resolution: "@types/react@npm:18.2.57" + version: 18.2.62 + resolution: "@types/react@npm:18.2.62" dependencies: "@types/prop-types": "npm:*" "@types/scheduler": "npm:*" csstype: "npm:^3.0.2" - checksum: 10c0/d5ed2f04c069c591e41ef1bea5b70f89dc7a4edff2254c4df801ddaa21b43b2aa70c106c049b9b6736f98f5afe66576d0e75a9e47c7044f2660b1744ff64f535 + checksum: 10c0/a45a986723b0febdcdcea754cfa426345f588fd9a522ab4136220a0b514ccba8f4986b23c73dd8e863d465ee1e733779bffffdd57c19488a318a5135f030c1e8 languageName: node linkType: hard @@ -1710,9 +1711,9 @@ __metadata: linkType: hard "@types/semver@npm:^7.5.0": - version: 7.5.7 - resolution: "@types/semver@npm:7.5.7" - checksum: 10c0/fb72d8b86a7779650f14ae89542f1da2ab624adb8188d98754b1d29a2fe3d41f0348bf9435b60ad145df1812fd2a09b3256779aa23b532c199f3dee59619a1eb + version: 7.5.8 + resolution: "@types/semver@npm:7.5.8" + checksum: 10c0/8663ff927234d1c5fcc04b33062cb2b9fcfbe0f5f351ed26c4d1e1581657deebd506b41ff7fdf89e787e3d33ce05854bc01686379b89e9c49b564c4cfa988efa languageName: node linkType: hard @@ -2592,11 +2593,11 @@ __metadata: linkType: hard "autoprefixer@npm:^10.4.15": - version: 10.4.17 - resolution: "autoprefixer@npm:10.4.17" + version: 10.4.18 + resolution: "autoprefixer@npm:10.4.18" dependencies: - browserslist: "npm:^4.22.2" - caniuse-lite: "npm:^1.0.30001578" + browserslist: "npm:^4.23.0" + caniuse-lite: "npm:^1.0.30001591" fraction.js: "npm:^4.3.7" normalize-range: "npm:^0.1.2" picocolors: "npm:^1.0.0" @@ -2605,7 +2606,7 @@ __metadata: postcss: ^8.1.0 bin: autoprefixer: bin/autoprefixer - checksum: 10c0/1d21cc8edb7bf993682094ceed03a32c18f5293f071182a64c2c6defb44bbe91d576ad775d2347469a81997b80cea0bbc4ad3eeb5b12710f9feacf2e6c04bb51 + checksum: 10c0/b6e1c1ba2fc6c09360cdcd75b00ce809c5dbe1ad4c30f0186764609a982aa5563d45965cb9e6a9d195c639a9fb1dcac2594484fc41624050195f626e9add666e languageName: node linkType: hard @@ -2747,12 +2748,12 @@ __metadata: languageName: node linkType: hard -"body-parser@npm:1.20.1": - version: 1.20.1 - resolution: "body-parser@npm:1.20.1" +"body-parser@npm:1.20.2": + version: 1.20.2 + resolution: "body-parser@npm:1.20.2" dependencies: bytes: "npm:3.1.2" - content-type: "npm:~1.0.4" + content-type: "npm:~1.0.5" debug: "npm:2.6.9" depd: "npm:2.0.0" destroy: "npm:1.2.0" @@ -2760,10 +2761,10 @@ __metadata: iconv-lite: "npm:0.4.24" on-finished: "npm:2.4.1" qs: "npm:6.11.0" - raw-body: "npm:2.5.1" + raw-body: "npm:2.5.2" type-is: "npm:~1.6.18" unpipe: "npm:1.0.0" - checksum: 10c0/a202d493e2c10a33fb7413dac7d2f713be579c4b88343cd814b6df7a38e5af1901fc31044e04de176db56b16d9772aa25a7723f64478c20f4d91b1ac223bf3b8 + checksum: 10c0/06f1438fff388a2e2354c96aa3ea8147b79bfcb1262dfcc2aae68ec13723d01d5781680657b74e9f83c808266d5baf52804032fbde2b7382b89bd8cdb273ace9 languageName: node linkType: hard @@ -2872,7 +2873,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.21.10, browserslist@npm:^4.22.2": +"browserslist@npm:^4.21.10, browserslist@npm:^4.22.2, browserslist@npm:^4.23.0": version: 4.23.0 resolution: "browserslist@npm:4.23.0" dependencies: @@ -3086,10 +3087,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001578, caniuse-lite@npm:^1.0.30001587": - version: 1.0.30001589 - resolution: "caniuse-lite@npm:1.0.30001589" - checksum: 10c0/20debfb949413f603011bc7dacaf050010778bc4f8632c86fafd1bd0c43180c95ae7c31f6c82348f6309e5e221934e327c3607a216e3f09640284acf78cd6d4d +"caniuse-lite@npm:^1.0.30001587, caniuse-lite@npm:^1.0.30001591": + version: 1.0.30001593 + resolution: "caniuse-lite@npm:1.0.30001593" + checksum: 10c0/c1601015ee4846da731f78164d4d863e6ee49a32988c4d879e9ab07ebdedcd32122161a3ff09f991485208d7a743db62fb5bd3dfbe4312f271c03f810a85cb4f languageName: node linkType: hard @@ -3478,7 +3479,7 @@ __metadata: languageName: node linkType: hard -"content-type@npm:^1.0.4, content-type@npm:~1.0.4": +"content-type@npm:^1.0.4, content-type@npm:~1.0.4, content-type@npm:~1.0.5": version: 1.0.5 resolution: "content-type@npm:1.0.5" checksum: 10c0/b76ebed15c000aee4678c3707e0860cb6abd4e680a598c0a26e17f0bfae723ec9cc2802f0ff1bc6e4d80603719010431d2231018373d4dde10f9ccff9dadf5af @@ -4019,9 +4020,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.4.668": - version: 1.4.679 - resolution: "electron-to-chromium@npm:1.4.679" - checksum: 10c0/6f1385e080360f19ec7b6e9d1072a13325cc110ca6fc10c0bb5f4c427003abecc66074c85509bedae8352ce28557fc26f5a1f238d11f0ee0c481c42a55c9fda4 + version: 1.4.690 + resolution: "electron-to-chromium@npm:1.4.690" + checksum: 10c0/fba87387968bac5ac55161dc176e55d92a54146ae1e5fb16c3fdd5bf4f250ce6f271713659c0cdfa7fc0fd9a9ea1a79803f266fa2b936535b208c0759a4d3983 languageName: node linkType: hard @@ -4078,12 +4079,12 @@ __metadata: linkType: hard "enhanced-resolve@npm:^5.0.0, enhanced-resolve@npm:^5.12.0, enhanced-resolve@npm:^5.15.0": - version: 5.15.0 - resolution: "enhanced-resolve@npm:5.15.0" + version: 5.15.1 + resolution: "enhanced-resolve@npm:5.15.1" dependencies: graceful-fs: "npm:^4.2.4" tapable: "npm:^2.2.0" - checksum: 10c0/69984a7990913948b4150855aed26a84afb4cb1c5a94fb8e3a65bd00729a73fc2eaff6871fb8e345377f294831afe349615c93560f2f54d61b43cdfdf668f19a + checksum: 10c0/f56a0f3726dc5fb65cb4518ab0806aecfd553f4cd4146f403ffe618ece36610443d8624a89d18fe0bb0be307b1c9ca8fb835267345ca4afc25d2932d58ced715 languageName: node linkType: hard @@ -4136,16 +4137,16 @@ __metadata: linkType: hard "es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3": - version: 1.22.4 - resolution: "es-abstract@npm:1.22.4" + version: 1.22.5 + resolution: "es-abstract@npm:1.22.5" dependencies: array-buffer-byte-length: "npm:^1.0.1" arraybuffer.prototype.slice: "npm:^1.0.3" - available-typed-arrays: "npm:^1.0.6" + available-typed-arrays: "npm:^1.0.7" call-bind: "npm:^1.0.7" es-define-property: "npm:^1.0.0" es-errors: "npm:^1.3.0" - es-set-tostringtag: "npm:^2.0.2" + es-set-tostringtag: "npm:^2.0.3" es-to-primitive: "npm:^1.2.1" function.prototype.name: "npm:^1.1.6" get-intrinsic: "npm:^1.2.4" @@ -4153,15 +4154,15 @@ __metadata: globalthis: "npm:^1.0.3" gopd: "npm:^1.0.1" has-property-descriptors: "npm:^1.0.2" - has-proto: "npm:^1.0.1" + has-proto: "npm:^1.0.3" has-symbols: "npm:^1.0.3" hasown: "npm:^2.0.1" internal-slot: "npm:^1.0.7" is-array-buffer: "npm:^3.0.4" is-callable: "npm:^1.2.7" - is-negative-zero: "npm:^2.0.2" + is-negative-zero: "npm:^2.0.3" is-regex: "npm:^1.1.4" - is-shared-array-buffer: "npm:^1.0.2" + is-shared-array-buffer: "npm:^1.0.3" is-string: "npm:^1.0.7" is-typed-array: "npm:^1.1.13" is-weakref: "npm:^1.0.2" @@ -4174,13 +4175,13 @@ __metadata: string.prototype.trim: "npm:^1.2.8" string.prototype.trimend: "npm:^1.0.7" string.prototype.trimstart: "npm:^1.0.7" - typed-array-buffer: "npm:^1.0.1" - typed-array-byte-length: "npm:^1.0.0" - typed-array-byte-offset: "npm:^1.0.0" - typed-array-length: "npm:^1.0.4" + typed-array-buffer: "npm:^1.0.2" + typed-array-byte-length: "npm:^1.0.1" + typed-array-byte-offset: "npm:^1.0.2" + typed-array-length: "npm:^1.0.5" unbox-primitive: "npm:^1.0.2" which-typed-array: "npm:^1.1.14" - checksum: 10c0/dc332c3a010c5e7b77b7ea8a4532ac455fa02e7bcabf996a47447165bafa72d0d99967407d0cf5dbbb5fbbf87f53cd8b706608ec70953523b8cd2b831b9a9d64 + checksum: 10c0/4bca5a60f0dff6c0a5690d8e51374cfcb8760d5dbbb1069174b4d41461cf4e0c3e0c1993bccbc5aa0799ff078199f1bcde2122b8709e0d17c2beffafff01010a languageName: node linkType: hard @@ -4214,7 +4215,7 @@ __metadata: languageName: node linkType: hard -"es-set-tostringtag@npm:^2.0.2": +"es-set-tostringtag@npm:^2.0.3": version: 2.0.3 resolution: "es-set-tostringtag@npm:2.0.3" dependencies: @@ -4321,14 +4322,14 @@ __metadata: linkType: hard "eslint-module-utils@npm:^2.7.4, eslint-module-utils@npm:^2.8.0": - version: 2.8.0 - resolution: "eslint-module-utils@npm:2.8.0" + version: 2.8.1 + resolution: "eslint-module-utils@npm:2.8.1" dependencies: debug: "npm:^3.2.7" peerDependenciesMeta: eslint: optional: true - checksum: 10c0/c7a8d1a58d76ec8217a8fea49271ec8132d1b9390965a75f6a4ecbc9e5983d742195b46d2e4378231d2186801439fe1aa5700714b0bfd4eb17aac6e1b65309df + checksum: 10c0/1aeeb97bf4b688d28de136ee57c824480c37691b40fa825c711a4caf85954e94b99c06ac639d7f1f6c1d69223bd21bcb991155b3e589488e958d5b83dfd0f882 languageName: node linkType: hard @@ -4425,14 +4426,14 @@ __metadata: linkType: hard "eslint@npm:^8.21.0, eslint@npm:^8.35.0": - version: 8.56.0 - resolution: "eslint@npm:8.56.0" + version: 8.57.0 + resolution: "eslint@npm:8.57.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" "@eslint-community/regexpp": "npm:^4.6.1" "@eslint/eslintrc": "npm:^2.1.4" - "@eslint/js": "npm:8.56.0" - "@humanwhocodes/config-array": "npm:^0.11.13" + "@eslint/js": "npm:8.57.0" + "@humanwhocodes/config-array": "npm:^0.11.14" "@humanwhocodes/module-importer": "npm:^1.0.1" "@nodelib/fs.walk": "npm:^1.2.8" "@ungap/structured-clone": "npm:^1.2.0" @@ -4468,7 +4469,7 @@ __metadata: text-table: "npm:^0.2.0" bin: eslint: bin/eslint.js - checksum: 10c0/2be598f7da1339d045ad933ffd3d4742bee610515cd2b0d9a2b8b729395a01d4e913552fff555b559fccaefd89d7b37632825789d1b06470608737ae69ab43fb + checksum: 10c0/00bb96fd2471039a312435a6776fe1fd557c056755eaa2b96093ef3a8508c92c8775d5f754768be6b1dddd09fdd3379ddb231eeb9b6c579ee17ea7d68000a529 languageName: node linkType: hard @@ -4609,12 +4610,12 @@ __metadata: linkType: hard "express@npm:^4.17.3": - version: 4.18.2 - resolution: "express@npm:4.18.2" + version: 4.18.3 + resolution: "express@npm:4.18.3" dependencies: accepts: "npm:~1.3.8" array-flatten: "npm:1.1.1" - body-parser: "npm:1.20.1" + body-parser: "npm:1.20.2" content-disposition: "npm:0.5.4" content-type: "npm:~1.0.4" cookie: "npm:0.5.0" @@ -4643,7 +4644,7 @@ __metadata: type-is: "npm:~1.6.18" utils-merge: "npm:1.0.1" vary: "npm:~1.1.2" - checksum: 10c0/75af556306b9241bc1d7bdd40c9744b516c38ce50ae3210658efcbf96e3aed4ab83b3432f06215eae5610c123bc4136957dc06e50dfc50b7d4d775af56c4c59c + checksum: 10c0/0b9eeafbac549e3c67d92d083bf1773e358359f41ad142b92121935c6348d29079b75054555b3f62de39263fffc8ba06898b09fdd3e213e28e714c03c5d9f44c languageName: node linkType: hard @@ -5456,9 +5457,9 @@ __metadata: linkType: hard "html-entities@npm:^2.3.2": - version: 2.4.0 - resolution: "html-entities@npm:2.4.0" - checksum: 10c0/42bbd5d91f451625d7e35aaed41c8cd110054c0d0970764cb58df467b3f27f20199e8cf7b4aebc8d4eeaf17a27c0d1fb165f2852db85de200995d0f009c9011d + version: 2.5.2 + resolution: "html-entities@npm:2.5.2" + checksum: 10c0/f20ffb4326606245c439c231de40a7c560607f639bf40ffbfb36b4c70729fd95d7964209045f1a4e62fe17f2364cef3d6e49b02ea09016f207fde51c2211e481 languageName: node linkType: hard @@ -6001,7 +6002,7 @@ __metadata: languageName: node linkType: hard -"is-negative-zero@npm:^2.0.2": +"is-negative-zero@npm:^2.0.3": version: 2.0.3 resolution: "is-negative-zero@npm:2.0.3" checksum: 10c0/bcdcf6b8b9714063ffcfa9929c575ac69bfdabb8f4574ff557dfc086df2836cf07e3906f5bbc4f2a5c12f8f3ba56af640c843cdfc74da8caed86c7c7d66fd08e @@ -6071,7 +6072,7 @@ __metadata: languageName: node linkType: hard -"is-shared-array-buffer@npm:^1.0.2": +"is-shared-array-buffer@npm:^1.0.2, is-shared-array-buffer@npm:^1.0.3": version: 1.0.3 resolution: "is-shared-array-buffer@npm:1.0.3" dependencies: @@ -8620,19 +8621,7 @@ __metadata: languageName: node linkType: hard -"raw-body@npm:2.5.1": - version: 2.5.1 - resolution: "raw-body@npm:2.5.1" - dependencies: - bytes: "npm:3.1.2" - http-errors: "npm:2.0.0" - iconv-lite: "npm:0.4.24" - unpipe: "npm:1.0.0" - checksum: 10c0/5dad5a3a64a023b894ad7ab4e5c7c1ce34d3497fc7138d02f8c88a3781e68d8a55aa7d4fd3a458616fa8647cc228be314a1c03fb430a07521de78b32c4dd09d2 - languageName: node - linkType: hard - -"raw-body@npm:^2.3.3": +"raw-body@npm:2.5.2, raw-body@npm:^2.3.3": version: 2.5.2 resolution: "raw-body@npm:2.5.2" dependencies: @@ -9358,14 +9347,14 @@ __metadata: linkType: hard "side-channel@npm:^1.0.4": - version: 1.0.5 - resolution: "side-channel@npm:1.0.5" + version: 1.0.6 + resolution: "side-channel@npm:1.0.6" dependencies: - call-bind: "npm:^1.0.6" + call-bind: "npm:^1.0.7" es-errors: "npm:^1.3.0" get-intrinsic: "npm:^1.2.4" object-inspect: "npm:^1.13.1" - checksum: 10c0/31312fecb68997ce2893b1f6d1fd07d6dd41e05cc938e82004f056f7de96dd9df599ef9418acdf730dda948e867e933114bd2efe4170c0146d1ed7009700c252 + checksum: 10c0/d2afd163dc733cc0a39aa6f7e39bf0c436293510dbccbff446733daeaf295857dbccf94297092ec8c53e2503acac30f0b78830876f0485991d62a90e9cad305f languageName: node linkType: hard @@ -9456,12 +9445,12 @@ __metadata: linkType: hard "socks@npm:^2.6.2, socks@npm:^2.7.1": - version: 2.8.0 - resolution: "socks@npm:2.8.0" + version: 2.8.1 + resolution: "socks@npm:2.8.1" dependencies: ip-address: "npm:^9.0.5" smart-buffer: "npm:^4.2.0" - checksum: 10c0/208fa5d5ae47857653c4fc039d47e4c1e76313b24052151a949aa98f027f9aaba8fc6c5dc0f7f2d9ceeb94e9940217581f2d9798436563c1494b67a6cb68611f + checksum: 10c0/ac77b515c260473cc7c4452f09b20939e22510ce3ae48385c516d1d5784374d5cc75be3cb18ff66cc985a7f4f2ef8fef84e984c5ec70aad58355ed59241f40a8 languageName: node linkType: hard @@ -9892,8 +9881,8 @@ __metadata: linkType: hard "terser@npm:^5.10.0, terser@npm:^5.26.0": - version: 5.27.2 - resolution: "terser@npm:5.27.2" + version: 5.28.1 + resolution: "terser@npm:5.28.1" dependencies: "@jridgewell/source-map": "npm:^0.3.3" acorn: "npm:^8.8.2" @@ -9901,7 +9890,7 @@ __metadata: source-map-support: "npm:~0.5.20" bin: terser: bin/terser - checksum: 10c0/027b2499bbb07b427681e50e77ffed1285138b279a845db4ca2128204654e536b251455776a4e9453ef598db7b06f41c12edb46ed9cc7667da635272a08eb502 + checksum: 10c0/e0d9a3cd260b4e35b49e828687658e36b0f50dce7cc2e18f024725846013ffa0e9eb8ac61a7a1bbf6684e6c14493ccf155a0f5937a47c746f534208f9000ac29 languageName: node linkType: hard @@ -10229,7 +10218,7 @@ __metadata: languageName: node linkType: hard -"typed-array-buffer@npm:^1.0.1": +"typed-array-buffer@npm:^1.0.2": version: 1.0.2 resolution: "typed-array-buffer@npm:1.0.2" dependencies: @@ -10240,7 +10229,7 @@ __metadata: languageName: node linkType: hard -"typed-array-byte-length@npm:^1.0.0": +"typed-array-byte-length@npm:^1.0.1": version: 1.0.1 resolution: "typed-array-byte-length@npm:1.0.1" dependencies: @@ -10253,7 +10242,7 @@ __metadata: languageName: node linkType: hard -"typed-array-byte-offset@npm:^1.0.0": +"typed-array-byte-offset@npm:^1.0.2": version: 1.0.2 resolution: "typed-array-byte-offset@npm:1.0.2" dependencies: @@ -10267,7 +10256,7 @@ __metadata: languageName: node linkType: hard -"typed-array-length@npm:^1.0.4": +"typed-array-length@npm:^1.0.5": version: 1.0.5 resolution: "typed-array-length@npm:1.0.5" dependencies: diff --git a/build-system/scripts/build b/build-system/scripts/build index 8b6ae7beb19..090878dd135 100755 --- a/build-system/scripts/build +++ b/build-system/scripts/build @@ -31,28 +31,6 @@ echo "Working directory: $PWD" echo "Dockerfile: $DOCKERFILE" echo "Build directory: $BUILD_DIR" -# Fetch images with retries -function fetch_image() { - echo "Pulling: $1" - if ! retry docker pull $1 > /dev/null 2>&1; then - echo "Image not found: $1" - return 1 - fi - return 0 -} - -# Fetch images, but don't assume this will work -function try_fetch_image() { - echo "Pulling: $1" - if ! docker pull $1 > /dev/null 2>&1; then - echo "Image not found: $1" - return 1 - fi - return 0 -} - -NOREPO=$(query_manifest noRepo $REPOSITORY) - # Login to ECR and ensure repository exists. retry ensure_repo $REPOSITORY $ECR_REGION refresh_lifecycle # Login to dockerhub. @@ -91,6 +69,7 @@ if [ -d $ROOT_PATH/$PROJECT_DIR/terraform ]; then fi # For each dependency, substitute references to the dependency in dockerfile, with the relevent built image uri. +# This is necessary vs pulling and retagging the image, as that doesn't work with buildx. # We have to perform a bit of probing to determine which actual image we want to use. # When we used buildx to create a multiarch image, there will be no images with "-$ARCH" suffixes (normalise this?). # Also we sometimes build an arm image from an x86 parent, so there won't always be an arm parent, and we fallback. @@ -115,9 +94,12 @@ for PARENT_REPO in $(query_manifest dependencies $REPOSITORY); do # Substitute references to parent repo, with the relevent built image uri. DEPLOY_URI=aztecprotocol/$PARENT_REPO PARENT_IMAGE_URI=$ECR_URL/$PARENT_REPO:$PARENT_IMAGE_TAG - awk '{if ($1 == "FROM" && $2 == "'$DEPLOY_URI'") $2 = "'$PARENT_IMAGE_URI'"; print $0}' $DOCKERFILE > _temp && mv _temp $DOCKERFILE + sed -i "s#^FROM \\(.*\\)${DEPLOY_URI}\\( \|$\\)#FROM \\1${PARENT_IMAGE_URI}\\2#" $DOCKERFILE done +# Build a dockerignore file that only permits git files and untracked files. +create_dockerignore $REPOSITORY + COMMIT_TAG_VERSION=$(extract_tag_version $REPOSITORY false) echo "Commit tag version: $COMMIT_TAG_VERSION" diff --git a/build-system/scripts/build_local b/build-system/scripts/build_local index 41e24b0c23c..33ef6e7b0dc 100755 --- a/build-system/scripts/build_local +++ b/build-system/scripts/build_local @@ -40,6 +40,10 @@ for DEP in ${DEPS[@]}; do PROJECTS+=("$DEP:$BUILD_DIR:$DOCKERFILE") done +if [ -z "$NO_CACHE" ] && can_use_ci_cache; then + USE_CACHE=1 +fi + for E in "${PROJECTS[@]}"; do ARR=(${E//:/ }) PROJECT_DIR_NAME=${ARR[0]} @@ -65,6 +69,9 @@ for E in "${PROJECTS[@]}"; do fi fi + # Build a dockerignore file that only permits git files and untracked files. + create_dockerignore $REPO + DEPLOY_IMAGE_URI=aztecprotocol/$REPO:latest CACHE_IMAGE_URI=$(calculate_image_uri $REPO) ARR=(${CACHE_IMAGE_URI//:/ }) @@ -101,7 +108,7 @@ for E in "${PROJECTS[@]}"; do if [ -z "$NO_CACHE" ] && docker image ls --format "{{.Repository}}:{{.Tag}}" | grep -q -w "$CACHE_IMAGE_URI$"; then echo -e "${GREEN}Image exists locally. Tagging as $DEPLOY_IMAGE_URI${RESET}" else - if [ -z "$NO_CACHE" ] && [ -f ~/.aws/credentials ] && ecr_login && image_exists $REPO $TAG ; then + if [ -n "${USE_CACHE:-}" ] && image_exists $REPO $TAG ; then docker pull $CACHE_IMAGE_URI else docker build ${ADDITIONAL_ARGS:-} --build-arg COMMIT_HASH=$COMMIT_HASH -f $DOCKERFILE -t $CACHE_IMAGE_URI . diff --git a/build-system/scripts/cond_spot_run_script b/build-system/scripts/cond_spot_run_script index 23a678fcd32..41fcffa93ad 100755 --- a/build-system/scripts/cond_spot_run_script +++ b/build-system/scripts/cond_spot_run_script @@ -35,6 +35,6 @@ echo "Success tag: $SUCCESS_TAG" if ! check_rebuild $SUCCESS_TAG $REPOSITORY; then init_submodules $REPOSITORY - spot_run_script $SUCCESS_TAG $CPUS $ARCH $@ + spot_run_script $REPOSITORY $SUCCESS_TAG $CPUS $ARCH $@ retry tag_remote_image $REPOSITORY $BASE_TAG $SUCCESS_TAG fi diff --git a/build-system/scripts/create_dockerignore b/build-system/scripts/create_dockerignore new file mode 100755 index 00000000000..feade28a12f --- /dev/null +++ b/build-system/scripts/create_dockerignore @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Build a dockerignore file that only permits git files and untracked files. +# Exludes any docker related files. +[ -n "${BUILD_SYSTEM_DEBUG:-}" ] && set -x # conditionally trace +set -eu + +REPOSITORY=$1 + +BUILD_DIR=$(query_manifest buildDir $REPOSITORY) +DOCKERFILE=$(query_manifest dockerfile $REPOSITORY) + +cd $BUILD_DIR +DOCKERIGNOREFILE=$DOCKERFILE.dockerignore +echo '*' > $DOCKERIGNOREFILE +(git ls-files; git ls-files --others --exclude-standard) | sort -u | sed 's/^/!/' >> $DOCKERIGNOREFILE +echo '**/Dockerfile*' >> $DOCKERIGNOREFILE \ No newline at end of file diff --git a/build-system/scripts/ecr_login b/build-system/scripts/ecr_login index 5d9e6671e59..0d11088065b 100755 --- a/build-system/scripts/ecr_login +++ b/build-system/scripts/ecr_login @@ -1,5 +1,6 @@ #!/usr/bin/env bash set -euo pipefail + REGION=${1:-$ECR_REGION} aws ecr get-login-password --region $REGION \ | docker login --username AWS --password-stdin $AWS_ACCOUNT.dkr.ecr.$REGION.amazonaws.com 2> /dev/null \ No newline at end of file diff --git a/build-system/scripts/generate_circleci_config.py b/build-system/scripts/generate_circleci_config.py index 51dafde0af1..0b1d002714e 100755 --- a/build-system/scripts/generate_circleci_config.py +++ b/build-system/scripts/generate_circleci_config.py @@ -47,8 +47,6 @@ def is_already_built_circleci_job(circleci_job, already_built_manifest_jobs): def get_already_built_circleci_job_names(circleci_jobs): already_built_manifest_jobs = list(get_already_built_manifest_job_names()) - for key in already_built_manifest_jobs: - eprint("Detected cached manifest key:", key) for job_name, circleci_job in circleci_jobs.items(): if is_already_built_circleci_job(circleci_job, already_built_manifest_jobs): yield job_name @@ -58,19 +56,20 @@ def _get_already_built_manifest_job_names(manifest_name): content_hash = subprocess.check_output(['calculate_content_hash', manifest_name]).decode("utf-8") completed = subprocess.run(["check_rebuild", f"cache-{content_hash}", manifest_name], stdout=subprocess.DEVNULL) if completed.returncode == 0: - return manifest_name + return manifest_name, content_hash else: - return None + return None, None def get_already_built_manifest_job_names(): manifest_names = get_manifest_job_names() - with ProcessPoolExecutor() as executor: + with ProcessPoolExecutor(max_workers=8) as executor: futures = {executor.submit(_get_already_built_manifest_job_names, key): key for key in manifest_names} for future in as_completed(futures): - result = future.result() - if result is not None: - yield result + key, content_hash = future.result() + if key is not None: + eprint("Detected cached manifest key:", key, "with content hash", content_hash) + yield key def remove_jobs_from_workflow(jobs, to_remove): """ diff --git a/build-system/scripts/remove_old_images b/build-system/scripts/remove_old_images index 7c1c927e6b6..41e194ff8f6 100755 --- a/build-system/scripts/remove_old_images +++ b/build-system/scripts/remove_old_images @@ -11,3 +11,5 @@ for IMAGE in $(docker images --format "{{.ID}}" $ECR_URL/$REPOSITORY --filter "b echo "Removing $IMAGE..." docker rmi --force $IMAGE done + +docker image prune -f > /dev/null \ No newline at end of file diff --git a/build-system/scripts/setup_env b/build-system/scripts/setup_env index 11fd7e70bfa..63c7035e169 100755 --- a/build-system/scripts/setup_env +++ b/build-system/scripts/setup_env @@ -100,6 +100,7 @@ echo export DEPLOY_TAG=${DEPLOY_TAG:-} >> $BASH_ENV echo export BRANCH=$BRANCH >> $BASH_ENV echo export PULL_REQUEST=$PULL_REQUEST >> $BASH_ENV echo export DRY_DEPLOY=${DRY_DEPLOY:-0} >> $BASH_ENV +echo export DOCKER_CLI_HINTS=false >> $BASH_ENV # We want very strict failures on any failing command, undefined variable, or commands that pipe to other commands. echo set -euo pipefail >> $BASH_ENV diff --git a/build-system/scripts/spot_run_script b/build-system/scripts/spot_run_script index d5e77e35712..d36a2b249b7 100755 --- a/build-system/scripts/spot_run_script +++ b/build-system/scripts/spot_run_script @@ -8,10 +8,11 @@ # JOB_NAME: Set within setup-env. The job name as per CI. [ -n "${BUILD_SYSTEM_DEBUG:-}" ] && set -x # conditionally trace set -eu -NAME=$1 -CPUS=$2 -ARCH=$3 -shift 3 +REPOSITORY=$1 +NAME=$2 +CPUS=$3 +ARCH=$4 +shift 4 IP= # On any sort of exit (error or not). @@ -19,6 +20,11 @@ function on_exit { # We want to execute all this block, regardless of potential errors. set +e + if [[ "$COMMIT_MESSAGE" == *"[ci no-term $REPOSITORY]"* ]]; then + echo "Not terminating instance on request. You can connect with 'ssh -i build_instance_key ubuntu@$IP'" + return + fi + if [ -n "$IP" ]; then echo "Terminating spot instance..." ssh -F $SSH_CONFIG_PATH $IP sudo halt -p > /dev/null 2>&1 diff --git a/build_manifest.yml b/build_manifest.yml index 6100e90d66a..c2a3678dca4 100644 --- a/build_manifest.yml +++ b/build_manifest.yml @@ -142,24 +142,19 @@ barretenberg-docs: l1-contracts: buildDir: l1-contracts -# Just contains the source code for the boxes (starter kits). -# Needed to pull into yarn-project, as we wanted to keep them separate to yarn-project itself. -boxes-files: - buildDir: boxes - dockerfile: Dockerfile.files - -# Contains just the npm/js dependencies needed by yarn-project. -# It's its own thing, to avoid continuously downloading all dependencies every build. -yarn-project-base: +# Runs all yarn-projects checks and tests. +yarn-project-test: buildDir: yarn-project - projectDir: yarn-project/yarn-project-base + dockerfile: Dockerfile.test rebuildPatterns: - - ^yarn-project/yarn-project-base/ - - ^yarn-project/yarn.lock - - ^yarn-project/.*/package.json$ + - ^yarn-project/.*\.(ts|tsx|js|cjs|mjs|json|html|md|sh|nr|toml|snap)$ + - ^yarn-project/Dockerfile$ + - ^yarn-project/cli/aztec-cli dependencies: - bb.js - noir-packages + - l1-contracts + - noir-projects # Builds all of yarn-project, with all developer dependencies. # Creates a runnable container used to run tests and formatting checks. @@ -170,41 +165,34 @@ yarn-project: - ^yarn-project/Dockerfile$ - ^yarn-project/cli/aztec-cli dependencies: - - yarn-project-base + - bb.js + - noir-packages - l1-contracts - - boxes-files - noir-projects - -# Productionifies yarn-project (removes all dev dependencies, multiarch). -yarn-project-prod: - buildDir: yarn-project - dockerfile: Dockerfile.prod - rebuildPatterns: - - ^yarn-project/Dockerfile.prod - dependencies: - - yarn-project - multiarch: buildx + multiarch: host # A runnable container, sets entrypoint to be the aztec infrastructure entrypoint. aztec: buildDir: yarn-project projectDir: yarn-project/aztec dependencies: - - yarn-project-prod + - yarn-project multiarch: buildx +# Aztec faucet server. Has these dependencies because it's part of workspace. Consider moving out? aztec-faucet: buildDir: yarn-project projectDir: yarn-project/aztec-faucet dependencies: - - yarn-project-prod + - bb.js + - noir-packages # A runnable container, sets entrypoint to be the aztec-cli entrypoint. cli: buildDir: yarn-project projectDir: yarn-project/cli dependencies: - - yarn-project-prod + - yarn-project multiarch: buildx # Builds all the boxes. They are then independently tested in the container. @@ -217,12 +205,16 @@ boxes: runDependencies: - aztec -# Builds a runnable container for running end-to-end tests (requires installing puppeteer etc). +# Builds a runnable container for running end-to-end tests (requires installing chromium etc). +# We add the runnable dependency 'aztec' to ensure the container is pulled before running the test compose file. end-to-end: buildDir: yarn-project projectDir: yarn-project/end-to-end dependencies: - - yarn-project + - bb.js + - noir-packages + - l1-contracts + - noir-projects runDependencies: - aztec @@ -241,7 +233,9 @@ docs: - ^.release-please-manifest\.json$ - ^.*\.nr$ dependencies: - - yarn-project + - bb.js + - noir-packages + - l1-contracts - noir-projects yellow-paper: diff --git a/docs/.dockerignore b/docs/.dockerignore deleted file mode 100644 index b5f7883caaf..00000000000 --- a/docs/.dockerignore +++ /dev/null @@ -1,22 +0,0 @@ -**/node_modules -*.docusaurus -*.processed-docs -*.processed-docs-cache -*Dockerfile -*.CONTRIBUTING.md -*.LICENSE - -# Ignore C++ object files and executables -build/ -Debug/ -Release/ -barretenberg/ - -# Ignore Node.js build artifacts -*/node_modules/ -*.log -npm-debug.log -yarn.lock -yarn-error.log -package-lock.json -dist/ diff --git a/docs/Dockerfile b/docs/Dockerfile index d963bf86ec7..e9d9f58beec 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,5 +1,40 @@ -FROM aztecprotocol/yarn-project +FROM --platform=linux/amd64 aztecprotocol/bb.js as bb.js +FROM --platform=linux/amd64 aztecprotocol/noir-packages as noir-packages +FROM --platform=linux/amd64 aztecprotocol/l1-contracts as contracts +FROM --platform=linux/amd64 aztecprotocol/noir-projects as noir-projects + +FROM node:18.19.0 as builder +RUN apt update && apt install -y jq curl perl && rm -rf /var/lib/apt/lists/* && apt-get clean + +# Copy in portalled packages. +COPY --from=bb.js /usr/src/barretenberg/ts /usr/src/barretenberg/ts +COPY --from=noir-packages /usr/src/noir/packages /usr/src/noir/packages +COPY --from=contracts /usr/src/l1-contracts /usr/src/l1-contracts +COPY --from=noir-projects /usr/src/noir-projects /usr/src/noir-projects + WORKDIR /usr/src COPY . . + +# 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 +# correctly for portalled packages, is to use --preserve-symlinks when running node. +# This does kind of work, but jest doesn't honor it correctly, so this seems like a neat workaround. +# Also, --preserve-symlinks causes duplication of portalled instances such as bb.js, and breaks the singleton logic +# by initialising the module more than once. So at present I don't see a viable alternative. +RUN ln -s /usr/src/yarn-project/node_modules /usr/src/node_modules + +# TODO: Replace puppeteer with puppeteer-core to avoid this. +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true + +WORKDIR /usr/src/yarn-project +RUN ./bootstrap.sh + WORKDIR /usr/src/docs -RUN yarn && yarn build \ No newline at end of file +RUN yarn && yarn build + +WORKDIR /usr/src/yarn-project +RUN yarn workspaces focus @aztec/scripts --production && yarn cache clean + +FROM node:18.19.1-slim +COPY --from=builder /usr/src/docs /usr/src/docs +COPY --from=builder /usr/src/yarn-project /usr/src/yarn-project \ No newline at end of file diff --git a/docs/Dockerfile.dockerignore b/docs/Dockerfile.dockerignore deleted file mode 100644 index cedb373cab3..00000000000 --- a/docs/Dockerfile.dockerignore +++ /dev/null @@ -1,14 +0,0 @@ -# The build context for docs is the root of the repository. -# Be very specific about what we include. -* - -!docs -docs/node_modules -!l1-contracts/src -!l1-contracts/test -!barretenberg/cpp/src/barretenberg -!.release-please-manifest.json -!boxes - -# Docs build fetches code snippets from the last release using git show. -!.git \ No newline at end of file diff --git a/docs/deploy_netlify.sh b/docs/deploy_netlify.sh index 1a1ff46a78c..aa38bf72b78 100755 --- a/docs/deploy_netlify.sh +++ b/docs/deploy_netlify.sh @@ -39,8 +39,6 @@ elif [ "$1" != "master" ]; then UNIQUE_DEPLOY_URL=$(echo "$DEPLOY_OUTPUT" | grep -E "https://.*aztec-docs-dev.netlify.app" | awk '{print $4}') echo "Unique deploy URL: $UNIQUE_DEPLOY_URL" - extract_repo yarn-project /usr/src project - cd project/src/yarn-project/scripts - + cd ../yarn-project/scripts UNIQUE_DEPLOY_URL=$UNIQUE_DEPLOY_URL yarn docs-preview-comment fi diff --git a/docs/docs/developers/contracts/writing_contracts/example_contract.md b/docs/docs/developers/contracts/writing_contracts/example_contract.md index bb6806b49c1..82428915fb4 100644 --- a/docs/docs/developers/contracts/writing_contracts/example_contract.md +++ b/docs/docs/developers/contracts/writing_contracts/example_contract.md @@ -8,6 +8,10 @@ In keeping with the origins of blockchain, here's an example of a simple private #include_code easy_private_token_contract /noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr rust +The prelude consists of more commonly imported aztec types that are needed for development. Here is what the prelude includes: + +#include_code prelude /noir-projects/aztec-nr/aztec/src/prelude.nr rust + :::info Disclaimer Please note that any example contract set out herein is provided solely for informational purposes only and does not constitute any inducement to use or deploy. Any implementation of any such contract with an interface or any other infrastructure should be used in accordance with applicable laws and regulations. -::: +::: \ No newline at end of file diff --git a/docs/docs/developers/contracts/writing_contracts/functions/main.md b/docs/docs/developers/contracts/writing_contracts/functions/main.md index 8164067e8cb..91b0fea0ca6 100644 --- a/docs/docs/developers/contracts/writing_contracts/functions/main.md +++ b/docs/docs/developers/contracts/writing_contracts/functions/main.md @@ -6,7 +6,7 @@ Functions serve as the building blocks of smart contracts. Functions can be eith For a more practical guide of using multiple types of functions, follow the [token tutorial](../../../tutorials/writing_token_contract.md). -Currently, any function is "mutable" in the sense that it might alter state. In the future, we will support static calls, similarly to EVM. A static call is essentially a call that does not alter state (it keeps state static). +Currently, any function is "mutable" in the sense that it might alter state. However, we also support support static calls, similarly to EVM. A static call is essentially a call that does not alter state (it keeps state static). ## Constructors diff --git a/docs/docs/developers/limitations/main.md b/docs/docs/developers/limitations/main.md index 1e6619333e3..baf3e719021 100644 --- a/docs/docs/developers/limitations/main.md +++ b/docs/docs/developers/limitations/main.md @@ -30,8 +30,6 @@ Help shape and define: - It is a testing environment, it is insecure, unaudited and does not generate any proofs, its only for testing purposes; - Constructors can not call nor alter public state - The constructor is executed exclusively in private domain, WITHOUT the ability to call public functions or alter public state. This means to set initial storage values, you need to follow a pattern similar to [proxies in Ethereum](https://blog.openzeppelin.com/proxy-patterns), where you `initialize` the contract with values after it have been deployed, see [constructor](../contracts/writing_contracts/functions/write_constructor.md). -- No static nor delegate calls (see [mutability](../contracts/writing_contracts/functions/main.md)). - - These values are unused in the call-context. - Beware that what you think of as a `view` could alter state ATM! Notably the account could alter state or re-enter whenever the account contract's `is_valid` function is called. - `msg_sender` is currently leaking when doing private -> public calls - The `msg_sender` will always be set, if you call a public function from the private world, the `msg_sender` will be set to the private caller's address. See [function context](../contracts/writing_contracts/functions/context.md). diff --git a/docs/docs/learn/concepts/accounts/authwit.md b/docs/docs/learn/concepts/accounts/authwit.md index 31fa51e1efa..b8412741746 100644 --- a/docs/docs/learn/concepts/accounts/authwit.md +++ b/docs/docs/learn/concepts/accounts/authwit.md @@ -132,10 +132,6 @@ sequenceDiagram The call to the account contract for checking authentication should be a static call, meaning that it cannot change state or make calls that change state. If this call is not static, it could be used to re-enter the flow and change the state of the contract. ::: -:::danger Static call currently unsupported -The current execution layer does not implement static call. So currently you will be passing along the control flow :grimacing:. -::: - :::danger Re-entries The above flow could be re-entered at token transfer. It is mainly for show to illustrate a logic outline. ::: diff --git a/docs/docs/misc/migration_notes.md b/docs/docs/misc/migration_notes.md index b31899c6083..1ae42fced3b 100644 --- a/docs/docs/misc/migration_notes.md +++ b/docs/docs/misc/migration_notes.md @@ -8,6 +8,61 @@ Aztec is in full-speed development. Literally every version breaks compatibility ## 0.25.0 +### [Aztec.nr] Static calls + +It is now possible to perform static calls from both public and private functions. Static calls forbid any modification to the state, including L2->L1 messages or log generation. Once a static context is set through a static all, every subsequent call will also be treated as static via context propagation. + +```rust +context.static_call_private_function(targetContractAddress, targetSelector, args); + +context.static_call_public_function(targetContractAddress, targetSelector, args); +``` + +### [Aztec.nr] Introduction to `prelude` + +A new `prelude` module to include common Aztec modules and types. +This simplifies dependency syntax. For example: +```rust +use dep::aztec::protocol_types::address::AztecAddress; +use dep::aztec::{ + context::{PrivateContext, Context}, note::{note_header::NoteHeader, utils as note_utils}, + state_vars::Map +}; +``` +Becomes: +```rust +use dep::aztec::prelude::{AztecAddress, NoteHeader, PrivateContext, Map}; +use dep::aztec::context::Context; +use dep::aztec::notes::utils as note_utils; +``` + +This will be further simplified in future versions (See [4496](https://github.com/AztecProtocol/aztec-packages/pull/4496) for further details). + +The prelude consists of + +#include_code prelude /noir-projects/aztec-nr/aztec/src/prelude.nr rust + +### `internal` is now a macro + +The `internal` keyword is now removed from Noir, and is replaced by an `aztec(internal)` attribute in the function. The resulting behavior is exactly the same: these functions will only be callable from within the same contract. + +Before: +```rust +#[aztec(private)] +internal fn double(input: Field) -> Field { + input * 2 +} +``` + +After: +```rust +#[aztec(private)] +#[aztec(internal)] +fn double(input: Field) -> Field { + input * 2 +} +``` + ### [Aztec.nr] No SafeU120 anymore! Noir now have overflow checks by default. So we don't need SafeU120 like libraries anymore. diff --git a/l1-contracts/.dockerignore b/l1-contracts/.dockerignore deleted file mode 100644 index 760149ef1b8..00000000000 --- a/l1-contracts/.dockerignore +++ /dev/null @@ -1,8 +0,0 @@ -.foundry -cache -Dockerfile -README.md -.github -lib/ -out/ -node_modules/ diff --git a/l1-contracts/Dockerfile b/l1-contracts/Dockerfile index bb0511f6657..671da76b806 100644 --- a/l1-contracts/Dockerfile +++ b/l1-contracts/Dockerfile @@ -4,11 +4,6 @@ RUN apk update && apk add git jq bash nodejs npm yarn python3 py3-pip && pip3 in WORKDIR /usr/src/l1-contracts COPY . . RUN git init -# Install deps -RUN forge install --no-commit \ - https://github.com/foundry-rs/forge-std \ - https://github.com/openzeppelin/openzeppelin-contracts -# Run build and tests RUN forge clean && forge fmt --check && forge build && forge test RUN yarn && yarn lint RUN git add . && yarn slither && yarn slither-has-diff diff --git a/l1-contracts/slither_output.md b/l1-contracts/slither_output.md index 1017d2edd73..420d9f75330 100644 --- a/l1-contracts/slither_output.md +++ b/l1-contracts/slither_output.md @@ -32,9 +32,9 @@ src/core/libraries/HeaderLib.sol#L150 - [ ] ID-2 -[TxsDecoder.decode(bytes).vars](src/core/libraries/decoders/TxsDecoder.sol#L89) is a local variable never initialized +[TxsDecoder.decode(bytes).vars](src/core/libraries/decoders/TxsDecoder.sol#L86) is a local variable never initialized -src/core/libraries/decoders/TxsDecoder.sol#L89 +src/core/libraries/decoders/TxsDecoder.sol#L86 ## unused-return @@ -50,20 +50,27 @@ src/core/Rollup.sol#L53-L91 Impact: Medium Confidence: High - [ ] ID-4 +Dubious typecast in [TxsDecoder.read4(bytes,uint256)](src/core/libraries/decoders/TxsDecoder.sol#L359-L361): + bytes => bytes4 casting occurs in [uint256(uint32(bytes4(slice(_data,_offset,4))))](src/core/libraries/decoders/TxsDecoder.sol#L360) + +src/core/libraries/decoders/TxsDecoder.sol#L359-L361 + + + - [ ] ID-5 Dubious typecast in [MessagesDecoder.read1(bytes,uint256)](src/core/libraries/decoders/MessagesDecoder.sol#L158-L160): bytes => bytes1 casting occurs in [uint256(uint8(bytes1(_data)))](src/core/libraries/decoders/MessagesDecoder.sol#L159) src/core/libraries/decoders/MessagesDecoder.sol#L158-L160 - - [ ] ID-5 + - [ ] ID-6 Dubious typecast in [Outbox.sendL1Messages(bytes32[])](src/core/messagebridge/Outbox.sol#L38-L46): uint256 => uint32 casting occurs in [version = uint32(REGISTRY.getVersionFor(msg.sender))](src/core/messagebridge/Outbox.sol#L40) src/core/messagebridge/Outbox.sol#L38-L46 - - [ ] ID-6 + - [ ] ID-7 Dubious typecast in [Inbox.sendL2Message(DataStructures.L2Actor,uint32,bytes32,bytes32)](src/core/messagebridge/Inbox.sol#L45-L91): uint256 => uint64 casting occurs in [fee = uint64(msg.value)](src/core/messagebridge/Inbox.sol#L64) uint256 => uint32 casting occurs in [entries.insert(key,fee,uint32(_recipient.version),_deadline,_errIncompatibleEntryArguments)](src/core/messagebridge/Inbox.sol#L76) @@ -71,21 +78,21 @@ Dubious typecast in [Inbox.sendL2Message(DataStructures.L2Actor,uint32,bytes32,b src/core/messagebridge/Inbox.sol#L45-L91 - - [ ] ID-7 -Dubious typecast in [TxsDecoder.read4(bytes,uint256)](src/core/libraries/decoders/TxsDecoder.sol#L362-L364): - bytes => bytes4 casting occurs in [uint256(uint32(bytes4(slice(_data,_offset,4))))](src/core/libraries/decoders/TxsDecoder.sol#L363) + - [ ] ID-8 +Dubious typecast in [TxsDecoder.read1(bytes,uint256)](src/core/libraries/decoders/TxsDecoder.sol#L349-L351): + bytes => bytes1 casting occurs in [uint256(uint8(bytes1(slice(_data,_offset,1))))](src/core/libraries/decoders/TxsDecoder.sol#L350) -src/core/libraries/decoders/TxsDecoder.sol#L362-L364 +src/core/libraries/decoders/TxsDecoder.sol#L349-L351 - - [ ] ID-8 + - [ ] ID-9 Dubious typecast in [MessagesDecoder.read4(bytes,uint256)](src/core/libraries/decoders/MessagesDecoder.sol#L168-L170): bytes => bytes4 casting occurs in [uint256(uint32(bytes4(_data)))](src/core/libraries/decoders/MessagesDecoder.sol#L169) src/core/libraries/decoders/MessagesDecoder.sol#L168-L170 - - [ ] ID-9 + - [ ] ID-10 Dubious typecast in [HeaderLib.decode(bytes)](src/core/libraries/HeaderLib.sol#L145-L189): bytes => bytes32 casting occurs in [header.lastArchive = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L153-L155) bytes => bytes4 casting occurs in [header.lastArchive = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L153-L155) @@ -113,20 +120,13 @@ Dubious typecast in [HeaderLib.decode(bytes)](src/core/libraries/HeaderLib.sol#L src/core/libraries/HeaderLib.sol#L145-L189 - - [ ] ID-10 + - [ ] ID-11 Dubious typecast in [Inbox.batchConsume(bytes32[],address)](src/core/messagebridge/Inbox.sol#L122-L143): uint256 => uint32 casting occurs in [expectedVersion = uint32(REGISTRY.getVersionFor(msg.sender))](src/core/messagebridge/Inbox.sol#L128) src/core/messagebridge/Inbox.sol#L122-L143 - - [ ] ID-11 -Dubious typecast in [TxsDecoder.read1(bytes,uint256)](src/core/libraries/decoders/TxsDecoder.sol#L352-L354): - bytes => bytes1 casting occurs in [uint256(uint8(bytes1(slice(_data,_offset,1))))](src/core/libraries/decoders/TxsDecoder.sol#L353) - -src/core/libraries/decoders/TxsDecoder.sol#L352-L354 - - ## reentrancy-events Impact: Low Confidence: Medium @@ -221,13 +221,6 @@ src/core/messagebridge/Inbox.sol#L21-L231 Impact: Informational Confidence: High - [ ] ID-22 -[TxsDecoder.computeRoot(bytes32[])](src/core/libraries/decoders/TxsDecoder.sol#L294-L313) uses assembly - - [INLINE ASM](src/core/libraries/decoders/TxsDecoder.sol#L301-L303) - -src/core/libraries/decoders/TxsDecoder.sol#L294-L313 - - - - [ ] ID-23 [MessagesDecoder.decode(bytes)](src/core/libraries/decoders/MessagesDecoder.sol#L60-L150) uses assembly - [INLINE ASM](src/core/libraries/decoders/MessagesDecoder.sol#L79-L81) - [INLINE ASM](src/core/libraries/decoders/MessagesDecoder.sol#L112-L118) @@ -235,6 +228,13 @@ src/core/libraries/decoders/TxsDecoder.sol#L294-L313 src/core/libraries/decoders/MessagesDecoder.sol#L60-L150 + - [ ] ID-23 +[TxsDecoder.computeRoot(bytes32[])](src/core/libraries/decoders/TxsDecoder.sol#L291-L310) uses assembly + - [INLINE ASM](src/core/libraries/decoders/TxsDecoder.sol#L298-L300) + +src/core/libraries/decoders/TxsDecoder.sol#L291-L310 + + ## dead-code Impact: Informational Confidence: Medium diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index 53c1b15328f..68aa3833958 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -53,7 +53,7 @@ contract Rollup is IRollup { function process( bytes calldata _header, bytes32 _archive, - bytes calldata _body, // TODO(#3938) Update this to pass in only th messages and not the whole body. + bytes calldata _body, // TODO(#4492) Nuke this when updating to the new message model bytes memory _proof ) external override(IRollup) { // Decode and validate header diff --git a/l1-contracts/src/core/libraries/decoders/TxsDecoder.sol b/l1-contracts/src/core/libraries/decoders/TxsDecoder.sol index 8075daab50e..442b6a41005 100644 --- a/l1-contracts/src/core/libraries/decoders/TxsDecoder.sol +++ b/l1-contracts/src/core/libraries/decoders/TxsDecoder.sol @@ -52,11 +52,10 @@ library TxsDecoder { struct ArrayOffsets { uint256 noteHash; uint256 nullifier; - uint256 publicData; uint256 l2ToL1Msgs; + uint256 publicData; uint256 contracts; uint256 contractData; - uint256 l1ToL2Msgs; } struct Counts { @@ -71,11 +70,9 @@ library TxsDecoder { // Note: Used in `computeConsumables` to get around stack too deep errors. struct ConsumablesVars { bytes32[] baseLeaves; - bytes32[] l2ToL1Msgs; bytes baseLeaf; bytes32 encryptedLogsHash; bytes32 unencryptedLogsHash; - uint256 l1Tol2MsgsCount; } /** @@ -91,8 +88,8 @@ library TxsDecoder { { // L1 to L2 messages + // TODO(#4492): update this when implementing the new message model uint256 count = read4(_body, offset); - vars.l1Tol2MsgsCount = count; offset += 0x4 + count * 0x20; count = read4(_body, offset); // number of tx effects diff --git a/noir-projects/.dockerignore b/noir-projects/.dockerignore deleted file mode 100644 index f01ca50f653..00000000000 --- a/noir-projects/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -aztec-nr/.gitattributes -aztec-nr/.gitrepo diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index 61746bdd239..30c6d140273 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -328,6 +328,7 @@ impl PrivateContext { is_static_call: bool, is_delegate_call: bool ) -> [Field; RETURN_VALUES_LENGTH] { + let mut is_static_call = is_static_call | self.inputs.call_context.is_static_call; let item = call_private_function_internal( contract_address, function_selector, @@ -434,6 +435,7 @@ impl PrivateContext { is_static_call: bool, is_delegate_call: bool ) { + let mut is_static_call = is_static_call | self.inputs.call_context.is_static_call; let fields = enqueue_public_function_call_internal( contract_address, function_selector, diff --git a/noir-projects/aztec-nr/aztec/src/initializer.nr b/noir-projects/aztec-nr/aztec/src/initializer.nr index bb39faacca9..b33c49274cf 100644 --- a/noir-projects/aztec-nr/aztec/src/initializer.nr +++ b/noir-projects/aztec-nr/aztec/src/initializer.nr @@ -1,5 +1,5 @@ use dep::protocol_types::hash::silo_nullifier; -use crate::context::{PrivateContext, ContextInterface}; +use crate::context::{PrivateContext, PublicContext, ContextInterface}; use crate::history::nullifier_inclusion::prove_nullifier_inclusion; pub fn mark_as_initialized(context: &mut PrivateContext) { @@ -7,6 +7,14 @@ pub fn mark_as_initialized(context: &mut PrivateContext) { context.push_new_nullifier(init_nullifier, 0); } +// TODO(@spalladino): Using the trait here fails with "No matching impl found for `&mut TContext: ContextInterface`" +// on the `push_new_nullifier` call. Remove this method in favor of a single method that uses the trait (and update +// the noir compiler macro accordingly) once we sort it out. +pub fn mark_as_initialized_public(context: &mut PublicContext) { + let init_nullifier = compute_unsiloed_contract_initialization_nullifier(*context); + context.push_new_nullifier(init_nullifier, 0); +} + pub fn assert_is_initialized(context: &mut TContext) where TContext: ContextInterface { let init_nullifier = compute_contract_initialization_nullifier(*context); prove_nullifier_inclusion(init_nullifier, *context); diff --git a/noir-projects/aztec-nr/aztec/src/prelude.nr b/noir-projects/aztec-nr/aztec/src/prelude.nr index 9d65d3f1dc6..b843647d933 100644 --- a/noir-projects/aztec-nr/aztec/src/prelude.nr +++ b/noir-projects/aztec-nr/aztec/src/prelude.nr @@ -1,14 +1,16 @@ +// docs:start:prelude use dep::protocol_types::{address::{AztecAddress, EthAddress}, abis::function_selector::FunctionSelector}; use crate::{ state_vars::{ - map::Map, private_immutable::PrivateImmutable, private_mutable::PrivateMutable, - public_immutable::PublicImmutable, public_mutable::PublicMutable, private_set::PrivateSet, - shared_immutable::SharedImmutable -}, + map::Map, private_immutable::PrivateImmutable, private_mutable::PrivateMutable, + public_immutable::PublicImmutable, public_mutable::PublicMutable, private_set::PrivateSet, + shared_immutable::SharedImmutable + }, log::{emit_unencrypted_log, emit_encrypted_log}, context::PrivateContext, note::{ - note_header::NoteHeader, note_interface::NoteInterface, note_getter_options::NoteGetterOptions, - note_viewer_options::NoteViewerOptions, - utils::compute_note_hash_and_nullifier as utils_compute_note_hash_and_nullifier -} + note_header::NoteHeader, note_interface::NoteInterface, note_getter_options::NoteGetterOptions, + note_viewer_options::NoteViewerOptions, + utils::compute_note_hash_and_nullifier as utils_compute_note_hash_and_nullifier + } }; +// docs:end:prelude diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr index 8d53aaf5506..f983dbf3836 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr @@ -86,7 +86,9 @@ contract AppSubscription { } #[aztec(public)] - internal fn init( + #[aztec(internal)] + #[aztec(noinitcheck)] + fn init( target_address: AztecAddress, subscription_token_address: AztecAddress, subscription_recipient_address: AztecAddress, @@ -101,12 +103,14 @@ contract AppSubscription { } #[aztec(public)] - internal fn assert_not_expired(expiry_block_number: Field) { + #[aztec(internal)] + fn assert_not_expired(expiry_block_number: Field) { assert((context.block_number()) as u64 < expiry_block_number as u64); } #[aztec(public)] - internal fn assert_block_number(expiry_block_number: Field) { + #[aztec(internal)] + fn assert_block_number(expiry_block_number: Field) { assert( (context.block_number() + SUBSCRIPTION_DURATION_IN_BLOCKS) as u64 >= expiry_block_number as u64 diff --git a/noir-projects/noir-contracts/contracts/card_game_contract/src/main.nr b/noir-projects/noir-contracts/contracts/card_game_contract/src/main.nr index c28738266e1..59fb565ea9e 100644 --- a/noir-projects/noir-contracts/contracts/card_game_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/card_game_contract/src/main.nr @@ -54,7 +54,8 @@ contract CardGame { } #[aztec(public)] - internal fn on_game_joined(game: u32, player: AztecAddress, deck_strength: u32) { + #[aztec(internal)] + fn on_game_joined(game: u32, player: AztecAddress, deck_strength: u32) { let game_storage = storage.games.at(game as Field); let mut game_data = game_storage.read(); @@ -91,7 +92,8 @@ contract CardGame { } #[aztec(public)] - internal fn on_card_played(game: u32, player: AztecAddress, card_as_field: Field) { + #[aztec(internal)] + fn on_card_played(game: u32, player: AztecAddress, card_as_field: Field) { let game_storage = storage.games.at(game as Field); let mut game_data = game_storage.read(); @@ -121,7 +123,8 @@ contract CardGame { } #[aztec(public)] - internal fn on_cards_claimed(game: u32, player: AztecAddress, cards_hash: Field) { + #[aztec(internal)] + fn on_cards_claimed(game: u32, player: AztecAddress, cards_hash: Field) { let game_storage = storage.games.at(game as Field); let mut game_data = game_storage.read(); diff --git a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr index 1571bfa6353..1017c6e30fd 100644 --- a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr @@ -21,18 +21,11 @@ contract Child { fn value(input: Field) -> Field { input + context.chain_id() + context.version() } - - fn check_sender(call_context: CallContext) { - assert( - call_context.msg_sender.eq(call_context.storage_contract_address), "Sender must be this contract" - ); - } - // Returns a sum of the input and the chain id and version of the contract in private circuit public input's return_values. // Can only be called from this contract. #[aztec(private)] + #[aztec(internal)] fn valueInternal(input: Field) -> Field { - check_sender(inputs.call_context); input + context.chain_id() + context.version() } @@ -83,8 +76,8 @@ contract Child { // Increments `current_value` by `new_value`. Can only be called from this contract. #[aztec(public)] + #[aztec(internal)] fn pubIncValueInternal(new_value: Field) -> Field { - check_sender(inputs.call_context); let old_value = storage.current_value.read(); storage.current_value.write(old_value + new_value); emit_unencrypted_log(&mut context, new_value); diff --git a/noir-projects/noir-contracts/contracts/delegated_on_contract/src/main.nr b/noir-projects/noir-contracts/contracts/delegated_on_contract/src/main.nr index 8b45ca01741..d8e1370dbe9 100644 --- a/noir-projects/noir-contracts/contracts/delegated_on_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/delegated_on_contract/src/main.nr @@ -38,15 +38,4 @@ contract DelegatedOn { unconstrained fn view_public_value() -> pub Field { storage.current_value.read() } - - // TODO: remove this placeholder once https://github.com/AztecProtocol/aztec-packages/issues/2918 is implemented - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; 0] - ) -> pub [Field; 4] { - [0, 0, 0, 0] - } } diff --git a/noir-projects/noir-contracts/contracts/delegator_contract/src/main.nr b/noir-projects/noir-contracts/contracts/delegator_contract/src/main.nr index fde7188bc23..34a82765004 100644 --- a/noir-projects/noir-contracts/contracts/delegator_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/delegator_contract/src/main.nr @@ -54,16 +54,4 @@ contract Delegator { unconstrained fn view_public_value() -> pub Field { storage.current_value.read() } - - // TODO: remove this placeholder once https://github.com/AztecProtocol/aztec-packages/issues/2918 is implemented - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; VALUE_NOTE_LEN] - ) -> pub [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - dep::aztec::note::utils::compute_note_hash_and_nullifier(ValueNote::deserialize_content, note_header, serialized_note) - } } diff --git a/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr b/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr index 7378f2b6139..0c3fe155477 100644 --- a/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr @@ -214,7 +214,8 @@ contract DocsExample { } #[aztec(public)] - internal fn update_leader(account: AztecAddress, points: u8) { + #[aztec(internal)] + fn update_leader(account: AztecAddress, points: u8) { let new_leader = Leader { account, points }; storage.leader.write(new_leader); } diff --git a/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr b/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr index 86991286193..60cbf894ef1 100644 --- a/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr @@ -28,7 +28,9 @@ contract EasyPrivateVoting { // docs:end:constructor // docs:start:initialize #[aztec(public)] // annotation to mark function as public and expose public context - internal fn _initialize(admin: AztecAddress) { // internal - can only be called by contract + #[aztec(internal)] // internal - can only be called by contract + #[aztec(noinitcheck)] + fn _initialize(admin: AztecAddress) { storage.admin.write(admin); storage.voteEnded.write(false); } @@ -49,7 +51,8 @@ contract EasyPrivateVoting { // docs:start:add_to_tally_public #[aztec(public)] - internal fn add_to_tally_public(candidate: Field) { + #[aztec(internal)] + fn add_to_tally_public(candidate: Field) { assert(storage.voteEnded.read() == false, "Vote has ended"); // assert that vote has not ended let new_tally = storage.tally.at(candidate).read() + 1; storage.tally.at(candidate).write(new_tally); diff --git a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr index 94bdd54d3d8..62066fb0678 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr @@ -51,12 +51,14 @@ contract EcdsaAccount { } #[aztec(private)] - internal fn cancel_authwit(outer_hash: Field) { + #[aztec(internal)] + fn cancel_authwit(outer_hash: Field) { context.push_new_nullifier(outer_hash, 0); } #[aztec(public)] - internal fn approve_public_authwit(outer_hash: Field) { + #[aztec(internal)] + fn approve_public_authwit(outer_hash: Field) { let actions = AccountActions::public(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl); actions.approve_public_authwit(outer_hash) } diff --git a/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr b/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr index d54a4b69afc..261b708cede 100644 --- a/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr @@ -1,39 +1,31 @@ // Sample escrow contract that stores a balance of a private token on behalf of an owner. contract Escrow { - use dep::aztec::prelude::{AztecAddress, EthAddress, FunctionSelector, NoteHeader, NoteGetterOptions, PrivateContext, PrivateSet}; + use dep::aztec::prelude::{AztecAddress, EthAddress, FunctionSelector, NoteHeader, PrivateContext, PrivateImmutable}; use dep::aztec::{context::{PublicContext, Context}, oracle::get_public_key::get_public_key}; use dep::address_note::address_note::{AddressNote, ADDRESS_NOTE_LEN}; struct Storage { - owners: PrivateSet, + owner: PrivateImmutable, } // Creates a new instance - // docs:start:constructor #[aztec(private)] #[aztec(initializer)] fn constructor(owner: pub AztecAddress) { - let this = context.this_address(); - - // Create a new note and add it to the owners set. - let mut note = AddressNote::new(owner, this); - - // Insert the owner into storage - storage.owners.insert(&mut note, true); + let mut note = AddressNote::new(owner, owner); + storage.owner.initialize(&mut note, true); } - // docs:end:constructor - // Withdraws balance. Requires that msg.sender is registered as an owner. + // Withdraws balance. Requires that msg.sender is the owner. #[aztec(private)] fn withdraw(token: AztecAddress, amount: Field, recipient: AztecAddress) { let this = context.this_address(); let sender = context.msg_sender(); - let options = NoteGetterOptions::new().select(0, sender.to_field(), Option::none()).select(1, this.to_field(), Option::none()).set_limit(1); - let notes = storage.owners.get_notes(options); - assert(notes[0].is_some(), "Sender is not an owner."); + let note = storage.owner.get_note(); + assert(note.address == sender); // get_notes removes the note from the set, so we insert it again. PrivateSet is not the best type of state // variable for this use case. diff --git a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr index 5bc8535adc4..7207f0ef308 100644 --- a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr @@ -23,7 +23,9 @@ contract FPC { } #[aztec(public)] - internal fn _initialize(other_asset: AztecAddress, fee_asset: AztecAddress) { + #[aztec(internal)] + #[aztec(noinitcheck)] + fn _initialize(other_asset: AztecAddress, fee_asset: AztecAddress) { storage.other_asset.initialize(other_asset); storage.fee_asset.initialize(fee_asset); } @@ -63,12 +65,14 @@ contract FPC { } #[aztec(public)] - internal fn prepare_fee(from: AztecAddress, amount: Field, asset: AztecAddress, nonce: Field) { + #[aztec(internal)] + fn prepare_fee(from: AztecAddress, amount: Field, asset: AztecAddress, nonce: Field) { let _res = Token::at(asset).transfer_public(context, from, context.this_address(), amount, nonce); } #[aztec(public)] - internal fn pay_fee(refund_address: AztecAddress, amount: Field, asset: AztecAddress) { + #[aztec(internal)] + fn pay_fee(refund_address: AztecAddress, amount: Field, asset: AztecAddress) { let refund = context.call_public_function( storage.fee_asset.read_public(), FunctionSelector::from_signature("pay_fee(Field)"), diff --git a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index 389aeacc1ae..541d54515b2 100644 --- a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -41,7 +41,9 @@ contract InclusionProofs { } #[aztec(public)] - internal fn _initialize(value: Field) { + #[aztec(internal)] + #[aztec(noinitcheck)] + fn _initialize(value: Field) { storage.public_value.write(value); } diff --git a/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr b/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr index 7ed23878394..7c06b7bc7c7 100644 --- a/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr @@ -130,7 +130,8 @@ contract Lending { } #[aztec(public)] - internal fn _deposit(owner: AztecAddress, amount: Field, collateral_asset: AztecAddress) { + #[aztec(internal)] + fn _deposit(owner: AztecAddress, amount: Field, collateral_asset: AztecAddress) { let _asset = Lending::at(context.this_address()).update_accumulator(context); let coll_asset = storage.collateral_asset.read(); @@ -163,7 +164,8 @@ contract Lending { } #[aztec(public)] - internal fn _withdraw(owner: AztecAddress, recipient: AztecAddress, amount: Field) { + #[aztec(internal)] + fn _withdraw(owner: AztecAddress, recipient: AztecAddress, amount: Field) { let asset = Lending::at(context.this_address()).update_accumulator(context); let price = PriceFeed::at(asset.oracle).get_price(context); @@ -220,7 +222,8 @@ contract Lending { } #[aztec(public)] - internal fn _borrow(owner: AztecAddress, to: AztecAddress, amount: Field) { + #[aztec(internal)] + fn _borrow(owner: AztecAddress, to: AztecAddress, amount: Field) { let asset = Lending::at(context.this_address()).update_accumulator(context); let price = PriceFeed::at(asset.oracle).get_price(context); @@ -282,7 +285,8 @@ contract Lending { } #[aztec(public)] - internal fn _repay(owner: AztecAddress, amount: Field, stable_coin: AztecAddress) { + #[aztec(internal)] + fn _repay(owner: AztecAddress, amount: Field, stable_coin: AztecAddress) { let asset = Lending::at(context.this_address()).update_accumulator(context); // To ensure that private is using the correct token. diff --git a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr index 056ba803ec7..dccf19cd04c 100644 --- a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr @@ -149,6 +149,39 @@ contract Parent { return_values[0] } + #[aztec(private)] + fn privateCall( + targetContract: AztecAddress, + targetSelector: FunctionSelector, + args: [Field; 2] + ) -> Field { + // Call the target private function + let return_values = context.call_private_function(targetContract, targetSelector, args); + + // Copy the return value from the call to this function's return values + return_values[0] + } + + // Private function to set a static context and verify correct propagation for nested private calls + #[aztec(private)] + fn privateStaticCallNested( + targetContract: AztecAddress, + targetSelector: FunctionSelector, + args: [Field; 2] + ) -> Field { + // Call the target private function statically + let privateCallSelector = FunctionSelector::from_signature("privateCall((Field),(u32),[Field;2])"); + let thisAddress = context.this_address(); + let return_values = context.static_call_private_function( + thisAddress, + privateCallSelector, + [targetContract.to_field(), targetSelector.to_field(), args[0], args[1]] + ); + + // Copy the return value from the call to this function's return values + return_values[0] + } + // Public function to directly call another public function to the targetContract using the selector and value provided #[aztec(public)] fn publicStaticCall( @@ -161,6 +194,41 @@ contract Parent { return_values[0] } + // Public function to set a static context and verify correct propagation for nested public calls + #[aztec(public)] + fn publicNestedStaticCall( + targetContract: AztecAddress, + targetSelector: FunctionSelector, + args: [Field; 1] + ) -> Field { + // Call the target public function through the pub entrypoint statically + let pubEntryPointSelector = FunctionSelector::from_signature("pubEntryPoint((Field),(u32),Field)"); + let thisAddress = context.this_address(); + let return_values = context.static_call_public_function( + thisAddress, + pubEntryPointSelector, + [targetContract.to_field(), targetSelector.to_field(), args[0]] + ); + return_values[0] + } + + // Private function to enqueue a static call to the pubEntryPoint function of another contract, passing the target arguments provided + #[aztec(private)] + fn enqueueStaticNestedCallToPubFunction( + targetContract: AztecAddress, + targetSelector: FunctionSelector, + args: [Field; 1] + ) { + // Call the target public function through the pub entrypoint statically + let pubEntryPointSelector = FunctionSelector::from_signature("pubEntryPoint((Field),(u32),Field)"); + let thisAddress = context.this_address(); + context.static_call_public_function( + thisAddress, + pubEntryPointSelector, + [targetContract.to_field(), targetSelector.to_field(), args[0]] + ); + } + // Private function to enqueue a static call to the pubEntryPoint function of another contract, passing the target arguments provided #[aztec(private)] fn enqueueStaticCallToPubFunction( diff --git a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr index 7d122947eb5..0f5355b1978 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr @@ -54,12 +54,14 @@ contract SchnorrAccount { } #[aztec(private)] - internal fn cancel_authwit(outer_hash: Field) { + #[aztec(internal)] + fn cancel_authwit(outer_hash: Field) { context.push_new_nullifier(outer_hash, 0); } #[aztec(public)] - internal fn approve_public_authwit(outer_hash: Field) { + #[aztec(internal)] + fn approve_public_authwit(outer_hash: Field) { let actions = AccountActions::public(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl); actions.approve_public_authwit(outer_hash) } diff --git a/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr index 5411e14a692..4fbb4a9e6ba 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr @@ -37,12 +37,14 @@ contract SchnorrHardcodedAccount { } #[aztec(private)] - internal fn cancel_authwit(outer_hash: Field) { + #[aztec(internal)] + fn cancel_authwit(outer_hash: Field) { context.push_new_nullifier(outer_hash, 0); } #[aztec(public)] - internal fn approve_public_authwit(outer_hash: Field) { + #[aztec(internal)] + fn approve_public_authwit(outer_hash: Field) { let actions = AccountActions::public(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl); actions.approve_public_authwit(outer_hash) } diff --git a/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr index ed6bfef6352..d1372e30ba0 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr @@ -34,12 +34,14 @@ contract SchnorrSingleKeyAccount { } #[aztec(private)] - internal fn cancel_authwit(outer_hash: Field) { + #[aztec(internal)] + fn cancel_authwit(outer_hash: Field) { context.push_new_nullifier(outer_hash, 0); } #[aztec(public)] - internal fn approve_public_authwit(outer_hash: Field) { + #[aztec(internal)] + fn approve_public_authwit(outer_hash: Field) { let actions = AccountActions::public(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl); actions.approve_public_authwit(outer_hash) } diff --git a/noir-projects/noir-contracts/contracts/slow_tree_contract/src/main.nr b/noir-projects/noir-contracts/contracts/slow_tree_contract/src/main.nr index 7962e69f110..3c3ab166eb9 100644 --- a/noir-projects/noir-contracts/contracts/slow_tree_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/slow_tree_contract/src/main.nr @@ -73,7 +73,8 @@ contract SlowTree { // docs:end:read_at_private // docs:start:assert_current_root #[aztec(public)] - internal fn _assert_current_root(caller: Field, expected: Field) { + #[aztec(internal)] + fn _assert_current_root(caller: Field, expected: Field) { let root = storage.trees.at(caller).current_root(); assert(root == expected, "Root does not match expected"); } @@ -115,7 +116,8 @@ contract SlowTree { // docs:end:update_at_private // docs:start:_update #[aztec(public)] - internal fn _update( + #[aztec(internal)] + fn _update( caller: Field, index: Field, new_value: Field, diff --git a/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr index 6e51a861eea..dddb482211e 100644 --- a/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr @@ -20,6 +20,13 @@ contract StatefulTest { let _res = context.call_private_function(context.this_address(), selector, [owner.to_field(), value]); } + #[aztec(public)] + #[aztec(initializer)] + fn public_constructor(owner: AztecAddress, value: Field) { + let selector = FunctionSelector::from_signature("increment_public_value_no_init_check((Field),Field)"); + let _res = context.call_public_function(context.this_address(), selector, [owner.to_field(), value]); + } + #[aztec(private)] fn create_note(owner: AztecAddress, value: Field) { if (value != 0) { @@ -63,12 +70,12 @@ contract StatefulTest { #[aztec(public)] fn increment_public_value(owner: AztecAddress, value: Field) { - assert_is_initialized(&mut context); let loc = storage.public_values.at(owner); loc.write(loc.read() + value); } #[aztec(public)] + #[aztec(noinitcheck)] fn increment_public_value_no_init_check(owner: AztecAddress, value: Field) { let loc = storage.public_values.at(owner); loc.write(loc.read() + value); diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr index 6fe2dc4b885..9c1d222a0a8 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr @@ -70,13 +70,16 @@ contract TokenBlacklist { } #[aztec(public)] - internal fn _init_slow_tree(caller: AztecAddress) { + #[aztec(internal)] + fn _init_slow_tree(caller: AztecAddress) { assert(storage.admin.read().eq(caller), "caller is not admin"); } /////// #[aztec(public)] - internal fn _initialize(new_admin: AztecAddress, slow_updates_contract: AztecAddress) { + #[aztec(internal)] + #[aztec(noinitcheck)] + fn _initialize(new_admin: AztecAddress, slow_updates_contract: AztecAddress) { assert(!new_admin.is_zero(), "invalid admin"); storage.admin.write(new_admin); // docs:start:write_slow_update_public @@ -282,13 +285,15 @@ contract TokenBlacklist { /// Internal /// #[aztec(public)] - internal fn _increase_public_balance(to: AztecAddress, amount: Field) { + #[aztec(internal)] + fn _increase_public_balance(to: AztecAddress, amount: Field) { let new_balance = storage.public_balances.at(to).read().add(U128::from_integer(amount)); storage.public_balances.at(to).write(new_balance); } #[aztec(public)] - internal fn _reduce_total_supply(amount: Field) { + #[aztec(internal)] + fn _reduce_total_supply(amount: Field) { // Only to be called from burn. let new_supply = storage.total_supply.read().sub(U128::from_integer(amount)); storage.total_supply.write(new_supply); diff --git a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr index 22274097e2d..1870537b2b9 100644 --- a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr @@ -138,7 +138,9 @@ contract TokenBridge { // docs:end:read_token #[aztec(public)] - internal fn _initialize(token: AztecAddress) { + #[aztec(internal)] + #[aztec(noinitcheck)] + fn _initialize(token: AztecAddress) { storage.token.write(token); } @@ -147,14 +149,16 @@ contract TokenBridge { // Also, note that user hashes their secret in private and only sends the hash in public // meaning only user can `redeem_shield` at a later time with their secret. #[aztec(public)] - internal fn _call_mint_on_token(amount: Field, secret_hash: Field) { + #[aztec(internal)] + fn _call_mint_on_token(amount: Field, secret_hash: Field) { Token::at(storage.token.read()).mint_private(context, amount, secret_hash); } // docs:end:call_mint_on_token // docs:start:assert_token_is_same #[aztec(public)] - internal fn _assert_token_is_same(token: AztecAddress) { + #[aztec(internal)] + fn _assert_token_is_same(token: AztecAddress) { assert(storage.token.read().eq(token), "Token address is not the same as seen in storage"); } // docs:end:assert_token_is_same diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index e57a39a3db2..50c4558d3cf 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -176,7 +176,8 @@ contract Token { } #[aztec(public)] - internal fn assert_minter_and_mint(minter: AztecAddress, amount: Field) { + #[aztec(internal)] + fn assert_minter_and_mint(minter: AztecAddress, amount: Field) { assert(storage.minters.at(minter).read(), "caller is not minter"); let supply = storage.total_supply.read() + U128::from_integer(amount); storage.total_supply.write(supply); @@ -312,7 +313,9 @@ contract Token { // docs:start:initialize #[aztec(public)] - internal fn _initialize( + #[aztec(internal)] + #[aztec(noinitcheck)] + fn _initialize( new_admin: AztecAddress, name: FieldCompressedString, symbol: FieldCompressedString, @@ -333,7 +336,8 @@ contract Token { // docs:start:increase_public_balance #[aztec(public)] - internal fn _increase_public_balance(to: AztecAddress, amount: Field) { + #[aztec(internal)] + fn _increase_public_balance(to: AztecAddress, amount: Field) { let new_balance = storage.public_balances.at(to).read().add(U128::from_integer(amount)); storage.public_balances.at(to).write(new_balance); } @@ -341,7 +345,8 @@ contract Token { // docs:start:reduce_total_supply #[aztec(public)] - internal fn _reduce_total_supply(amount: Field) { + #[aztec(internal)] + fn _reduce_total_supply(amount: Field) { // Only to be called from burn. let new_supply = storage.total_supply.read().sub(U128::from_integer(amount)); storage.total_supply.write(new_supply); diff --git a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr index 7295fd47488..7a1fd038c80 100644 --- a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr @@ -195,7 +195,8 @@ contract Uniswap { // Note that private can't read public return values so created an internal public that handles everything // this method is used for both private and public swaps. #[aztec(public)] - internal fn _approve_bridge_and_exit_input_asset_to_L1(token: AztecAddress, token_bridge: AztecAddress, amount: Field) { + #[aztec(internal)] + fn _approve_bridge_and_exit_input_asset_to_L1(token: AztecAddress, token_bridge: AztecAddress, amount: Field) { // approve bridge to burn this contract's funds (required when exiting on L1, as it burns funds on L2): let nonce_for_burn_approval = storage.nonce_for_burn_approval.read(); let selector = FunctionSelector::from_signature("burn_public((Field),Field,Field)"); @@ -223,7 +224,8 @@ contract Uniswap { // docs:start:assert_token_is_same #[aztec(public)] - internal fn _assert_token_is_same(token: AztecAddress, token_bridge: AztecAddress) { + #[aztec(internal)] + fn _assert_token_is_same(token: AztecAddress, token_bridge: AztecAddress) { assert( token.eq(TokenBridge::at(token_bridge).token(context)), "input_asset address is not the same as seen in the bridge contract" ); diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr index 2bbaeda1a3a..3af65aac18b 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr @@ -25,7 +25,7 @@ struct BaseOrMergeRollupPublicInputs { // U128 isn't safe if it's an input to the circuit (it won't automatically constrain the witness) // So we want to constrain it when casting these fields to U128 - // TODO(#3938): split this to txs_hash and out_hash + // TODO(#4492): update this when implementing the new message model // We hash public inputs to make them constant-sized (to then be unpacked on-chain) calldata_hash : [Field; 2], } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr index 01570f0aa9f..bb669298d4e 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr @@ -299,7 +299,6 @@ impl BaseRollupInputs { let new_contracts = combined.new_contracts; calldata_hash_inputs[offset] = new_contracts[0].contract_address.to_field(); - // TODO(#3938): make portal address 20 bytes here when updating the hashing calldata_hash_inputs[offset + 1] = new_contracts[0].portal_contract_address.to_field(); offset += MAX_NEW_CONTRACTS_PER_TX * 2; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root.nr index 6adee65ff88..9a4c9d78466 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root.nr @@ -71,7 +71,7 @@ impl RootRollupInputs { aggregation_object, archive, header, - // TODO(#3938): Nuke this once body hash/calldata hash is updated + // TODO(#4492): update this when implementing the new message model l1_to_l2_messages_hash: compute_messages_hash(self.new_l1_to_l2_messages) } } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_public_inputs.nr index 34a8763e739..1f9a2113afc 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_public_inputs.nr @@ -14,6 +14,6 @@ struct RootRollupPublicInputs { // New block header header: Header, - // TODO(#3938): Nuke this once body hash/calldata hash is updated + // TODO(#4492): Nuke this once body hash/calldata hash is updated l1_to_l2_messages_hash : [Field; NUM_FIELDS_PER_SHA256], } diff --git a/noir/.dockerignore b/noir/.dockerignore deleted file mode 100644 index f68bd5840b4..00000000000 --- a/noir/.dockerignore +++ /dev/null @@ -1,30 +0,0 @@ -**/Dockerfile* -**/.dockerignore - -# Yarn - -**/.pnp._ -**/.yarn/_ -**/!.yarn/patches -**/!.yarn/plugins -**/!.yarn/releases -**/!.yarn/sdks -**/!.yarn/versions - -packages -**/package.tgz -**/target -**/node_modules -**/outputs - -# Noir.js - -**/tooling/noir_js/lib - -# Wasm build artifacts - -**/compiler/wasm/nodejs -**/compiler/wasm/web -**/tooling/noirc_abi_wasm/nodejs -**/tooling/noirc_abi_wasm/web -**/tooling/noir_js/lib diff --git a/noir/noir-repo/.aztec-sync-commit b/noir/noir-repo/.aztec-sync-commit new file mode 100644 index 00000000000..78e53d61647 --- /dev/null +++ b/noir/noir-repo/.aztec-sync-commit @@ -0,0 +1 @@ +73d640a4a033f0c865d45da470ef40c1fb03a844 diff --git a/noir/noir-repo/.dockerignore b/noir/noir-repo/.dockerignore deleted file mode 100644 index 559b271bf38..00000000000 --- a/noir/noir-repo/.dockerignore +++ /dev/null @@ -1,27 +0,0 @@ -Dockerfile* -.dockerignore - -# Yarn -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/sdks -!.yarn/versions - -packages -**/package.tgz -**/target -**/node_modules -**/outputs - -# Noir.js -tooling/noir_js/lib - -# Wasm build artifacts -compiler/wasm/nodejs -compiler/wasm/web -tooling/noirc_abi_wasm/nodejs -tooling/noirc_abi_wasm/web -tooling/noir_js/lib diff --git a/noir/noir-repo/.gitrepo b/noir/noir-repo/.gitrepo index 48acdbfe88c..24317f85d26 100644 --- a/noir/noir-repo/.gitrepo +++ b/noir/noir-repo/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/noir-lang/noir branch = master - commit = e80c5f73a4cdcba3f5cf44576c605ba1e611a2ab + commit = 5f57ebb7ff4b810802f90699a10f4325ef904f2e parent = 8307dadd853d5091841e169c841ab6b09c223efb method = merge cmdver = 0.4.6 diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index c0438eaf81f..b2b6f8037bb 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -2634,9 +2634,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index f73417a4b5b..8a0c4692282 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -217,8 +217,10 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::PedersenCommitment { inputs, .. } | BlackBoxFuncCall::PedersenHash { inputs, .. } | BlackBoxFuncCall::BigIntFromLeBytes { inputs, .. } - | BlackBoxFuncCall::Poseidon2Permutation { inputs, .. } - | BlackBoxFuncCall::Sha256Compression { inputs, .. } => inputs.to_vec(), + | BlackBoxFuncCall::Poseidon2Permutation { inputs, .. } => inputs.to_vec(), + BlackBoxFuncCall::Sha256Compression { inputs, hash_values, .. } => { + inputs.iter().chain(hash_values).copied().collect() + } BlackBoxFuncCall::AND { lhs, rhs, .. } | BlackBoxFuncCall::XOR { lhs, rhs, .. } => { vec![*lhs, *rhs] } diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/constant_backpropagation.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/constant_backpropagation.rs new file mode 100644 index 00000000000..0e7d28104da --- /dev/null +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/constant_backpropagation.rs @@ -0,0 +1,331 @@ +use std::collections::{BTreeMap, BTreeSet, HashMap}; + +use crate::{ + compiler::optimizers::GeneralOptimizer, + pwg::{ + arithmetic::ExpressionSolver, blackbox::solve_range_opcode, directives::solve_directives, + BrilligSolver, BrilligSolverStatus, + }, +}; +use acir::{ + circuit::{ + brillig::{Brillig, BrilligInputs, BrilligOutputs}, + directives::Directive, + opcodes::BlackBoxFuncCall, + Circuit, Opcode, + }, + native_types::{Expression, Witness, WitnessMap}, +}; +use acvm_blackbox_solver::StubbedBlackBoxSolver; + +/// `ConstantBackpropagationOptimizer` will attempt to determine any constant witnesses within the program. +/// It does this by attempting to solve the program without any inputs (i.e. using an empty witness map), +/// any values which it can determine are then enforced to be constant values. +/// +/// The optimizer will then replace any witnesses wherever they appear within the circuit with these constant values. +/// This is repeated until the circuit stabilizes. +pub(crate) struct ConstantBackpropagationOptimizer { + circuit: Circuit, +} + +impl ConstantBackpropagationOptimizer { + /// Creates a new `ConstantBackpropagationOptimizer` + pub(crate) fn new(circuit: Circuit) -> Self { + Self { circuit } + } + + fn gather_known_witnesses(&self) -> (WitnessMap, BTreeSet) { + // We do not want to affect the circuit's interface so avoid optimizing away these witnesses. + let mut required_witnesses: BTreeSet = self + .circuit + .private_parameters + .union(&self.circuit.public_parameters.0) + .chain(&self.circuit.return_values.0) + .copied() + .collect(); + + for opcode in &self.circuit.opcodes { + match &opcode { + Opcode::BlackBoxFuncCall(func_call) => { + required_witnesses.extend( + func_call.get_inputs_vec().into_iter().map(|func_input| func_input.witness), + ); + required_witnesses.extend(func_call.get_outputs_vec()); + } + + Opcode::MemoryInit { init, .. } => { + required_witnesses.extend(init); + } + + Opcode::MemoryOp { op, .. } => { + required_witnesses.insert(op.index.to_witness().unwrap()); + required_witnesses.insert(op.value.to_witness().unwrap()); + } + + _ => (), + }; + } + + let mut known_witnesses = WitnessMap::new(); + for opcode in self.circuit.opcodes.iter().rev() { + if let Opcode::AssertZero(expr) = opcode { + let solve_result = ExpressionSolver::solve(&mut known_witnesses, expr); + // It doesn't matter what the result is. We expect most opcodes to not be solved successfully so we discard errors. + // At the same time, if the expression can be solved then we track this by the updates to `known_witnesses` + drop(solve_result); + } + } + + // We want to retain any references to required witnesses so we "forget" these assignments. + let known_witnesses: BTreeMap<_, _> = known_witnesses + .into_iter() + .filter(|(witness, _)| !required_witnesses.contains(witness)) + .collect(); + + (known_witnesses.into(), required_witnesses) + } + + /// Returns a `Circuit` where with any constant witnesses replaced with the constant they resolve to. + #[tracing::instrument(level = "trace", skip_all)] + pub(crate) fn backpropagate_constants( + circuit: Circuit, + order_list: Vec, + ) -> (Circuit, Vec) { + let old_circuit_size = circuit.opcodes.len(); + + let optimizer = Self::new(circuit); + let (circuit, order_list) = optimizer.backpropagate_constants_iteration(order_list); + + let new_circuit_size = circuit.opcodes.len(); + if new_circuit_size < old_circuit_size { + Self::backpropagate_constants(circuit, order_list) + } else { + (circuit, order_list) + } + } + + /// Applies a single round of constant backpropagation to a `Circuit`. + pub(crate) fn backpropagate_constants_iteration( + mut self, + order_list: Vec, + ) -> (Circuit, Vec) { + let (mut known_witnesses, required_witnesses) = self.gather_known_witnesses(); + + let opcodes = std::mem::take(&mut self.circuit.opcodes); + + fn remap_expression(known_witnesses: &WitnessMap, expression: Expression) -> Expression { + GeneralOptimizer::optimize(ExpressionSolver::evaluate(&expression, known_witnesses)) + } + + let mut new_order_list = Vec::with_capacity(order_list.len()); + let mut new_opcodes = Vec::with_capacity(opcodes.len()); + for (idx, opcode) in opcodes.into_iter().enumerate() { + let new_opcode = match opcode { + Opcode::AssertZero(expression) => { + let new_expr = remap_expression(&known_witnesses, expression); + if new_expr.is_zero() { + continue; + } + + // Attempt to solve the opcode to see if we can determine the value of any witnesses in the expression. + // We only do this _after_ we apply any simplifications to create the new opcode as we want to + // keep the constraint on the witness which we are solving for here. + let solve_result = ExpressionSolver::solve(&mut known_witnesses, &new_expr); + // It doesn't matter what the result is. We expect most opcodes to not be solved successfully so we discard errors. + // At the same time, if the expression can be solved then we track this by the updates to `known_witnesses` + drop(solve_result); + + Opcode::AssertZero(new_expr) + } + Opcode::Brillig(brillig) => { + let remapped_inputs = brillig + .inputs + .into_iter() + .map(|input| match input { + BrilligInputs::Single(expr) => { + BrilligInputs::Single(remap_expression(&known_witnesses, expr)) + } + BrilligInputs::Array(expr_array) => { + let new_input: Vec<_> = expr_array + .into_iter() + .map(|expr| remap_expression(&known_witnesses, expr)) + .collect(); + + BrilligInputs::Array(new_input) + } + input @ BrilligInputs::MemoryArray(_) => input, + }) + .collect(); + + let remapped_predicate = brillig + .predicate + .map(|predicate| remap_expression(&known_witnesses, predicate)); + + let new_brillig = Brillig { + inputs: remapped_inputs, + predicate: remapped_predicate, + ..brillig + }; + + let brillig_output_is_required_witness = + new_brillig.outputs.iter().any(|output| match output { + BrilligOutputs::Simple(witness) => required_witnesses.contains(witness), + BrilligOutputs::Array(witness_array) => witness_array + .iter() + .any(|witness| required_witnesses.contains(witness)), + }); + + if brillig_output_is_required_witness { + // If one of the brillig opcode's outputs is a required witness then we can't remove the opcode. In this case we can't replace + // all of the uses of this witness with the calculated constant so we'll be attempting to use an uninitialized witness. + // + // We then do not attempt execution of this opcode and just simplify the inputs. + Opcode::Brillig(new_brillig) + } else if let Ok(mut solver) = BrilligSolver::new( + &known_witnesses, + &HashMap::new(), + &new_brillig, + &StubbedBlackBoxSolver, + idx, + ) { + match solver.solve() { + Ok(BrilligSolverStatus::Finished) => { + // Write execution outputs + match solver.finalize(&mut known_witnesses, &new_brillig) { + Ok(()) => { + // If we've managed to execute the brillig opcode at compile time, we can now just write in the + // results as constants for the rest of the circuit. + continue; + } + _ => Opcode::Brillig(new_brillig), + } + } + Ok(BrilligSolverStatus::InProgress) => unreachable!( + "Solver should either finish, block on foreign call, or error." + ), + Ok(BrilligSolverStatus::ForeignCallWait(_)) | Err(_) => { + Opcode::Brillig(new_brillig) + } + } + } else { + Opcode::Brillig(new_brillig) + } + } + + Opcode::Directive(Directive::ToLeRadix { a, b, radix }) => { + if b.iter().all(|output| known_witnesses.contains_key(output)) { + continue; + } else if b.iter().any(|witness| required_witnesses.contains(witness)) { + // If one of the brillig opcode's outputs is a required witness then we can't remove the opcode. In this case we can't replace + // all of the uses of this witness with the calculated constant so we'll be attempting to use an uninitialized witness. + // + // We then do not attempt execution of this opcode and just simplify the inputs. + Opcode::Directive(Directive::ToLeRadix { + a: remap_expression(&known_witnesses, a), + b, + radix, + }) + } else { + let directive = Directive::ToLeRadix { + a: remap_expression(&known_witnesses, a), + b, + radix, + }; + let result = solve_directives(&mut known_witnesses, &directive); + + match result { + Ok(()) => continue, + Err(_) => Opcode::Directive(directive), + } + } + } + + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input }) => { + if solve_range_opcode(&known_witnesses, &input).is_ok() { + continue; + } else { + opcode + } + } + + Opcode::BlackBoxFuncCall(_) + | Opcode::MemoryOp { .. } + | Opcode::MemoryInit { .. } => opcode, + }; + + new_opcodes.push(new_opcode); + new_order_list.push(order_list[idx]); + } + + self.circuit.opcodes = new_opcodes; + + (self.circuit, new_order_list) + } +} + +#[cfg(test)] +mod tests { + use std::collections::BTreeSet; + + use crate::compiler::optimizers::constant_backpropagation::ConstantBackpropagationOptimizer; + use acir::{ + brillig::MemoryAddress, + circuit::{ + brillig::{Brillig, BrilligOutputs}, + opcodes::{BlackBoxFuncCall, FunctionInput}, + Circuit, ExpressionWidth, Opcode, PublicInputs, + }, + native_types::Witness, + }; + use brillig_vm::brillig::Opcode as BrilligOpcode; + + fn test_circuit(opcodes: Vec) -> Circuit { + Circuit { + current_witness_index: 1, + expression_width: ExpressionWidth::Bounded { width: 3 }, + opcodes, + private_parameters: BTreeSet::new(), + public_parameters: PublicInputs::default(), + return_values: PublicInputs::default(), + assert_messages: Default::default(), + recursive: false, + } + } + + #[test] + fn retain_brillig_with_required_witness_outputs() { + let brillig_opcode = Opcode::Brillig(Brillig { + inputs: Vec::new(), + outputs: vec![BrilligOutputs::Simple(Witness(1))], + bytecode: vec![ + BrilligOpcode::Const { + destination: MemoryAddress(0), + bit_size: 32, + value: 1u128.into(), + }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 1 }, + ], + predicate: None, + }); + let blackbox_opcode = Opcode::BlackBoxFuncCall(BlackBoxFuncCall::AND { + lhs: FunctionInput { witness: Witness(1), num_bits: 64 }, + rhs: FunctionInput { witness: Witness(2), num_bits: 64 }, + output: Witness(3), + }); + + let opcodes = vec![brillig_opcode, blackbox_opcode]; + // The optimizer should keep the lowest bit size range constraint + let circuit = test_circuit(opcodes); + let acir_opcode_positions = circuit.opcodes.iter().enumerate().map(|(i, _)| i).collect(); + let optimizer = ConstantBackpropagationOptimizer::new(circuit); + + let (optimized_circuit, _) = + optimizer.backpropagate_constants_iteration(acir_opcode_positions); + + assert_eq!( + optimized_circuit.opcodes.len(), + 2, + "The brillig opcode should not be removed as the output is needed as a witness" + ); + } +} diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/general.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/general.rs index 2bd781f7bb5..a48a590a05e 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/general.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/general.rs @@ -13,7 +13,8 @@ impl GeneralOptimizer { pub(crate) fn optimize(opcode: Expression) -> Expression { // XXX: Perhaps this optimization can be done on the fly let opcode = remove_zero_coefficients(opcode); - simplify_mul_terms(opcode) + let opcode = simplify_mul_terms(opcode); + simplify_linear_terms(opcode) } } @@ -42,3 +43,20 @@ fn simplify_mul_terms(mut gate: Expression) -> Expression { gate.mul_terms = hash_map.into_iter().map(|((w_l, w_r), scale)| (scale, w_l, w_r)).collect(); gate } + +// Simplifies all linear terms with the same variables +fn simplify_linear_terms(mut gate: Expression) -> Expression { + let mut hash_map: IndexMap = IndexMap::new(); + + // Canonicalize the ordering of the terms, lets just order by variable name + for (scale, witness) in gate.linear_combinations.into_iter() { + *hash_map.entry(witness).or_insert_with(FieldElement::zero) += scale; + } + + gate.linear_combinations = hash_map + .into_iter() + .filter(|(_, scale)| scale != &FieldElement::zero()) + .map(|(witness, scale)| (scale, witness)) + .collect(); + gate +} diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/mod.rs index 923756580b3..04d3f99a408 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/mod.rs @@ -1,5 +1,6 @@ use acir::circuit::{Circuit, Opcode}; +// mod constant_backpropagation; mod general; mod redundant_range; mod unused_memory; @@ -8,6 +9,7 @@ pub(crate) use general::GeneralOptimizer; pub(crate) use redundant_range::RangeOptimizer; use tracing::info; +// use self::constant_backpropagation::ConstantBackpropagationOptimizer; use self::unused_memory::UnusedMemoryOptimizer; use super::{transform_assert_messages, AcirTransformationMap}; @@ -26,6 +28,15 @@ pub fn optimize(acir: Circuit) -> (Circuit, AcirTransformationMap) { /// Applies [`ProofSystemCompiler`][crate::ProofSystemCompiler] independent optimizations to a [`Circuit`]. #[tracing::instrument(level = "trace", name = "optimize_acir" skip(acir))] pub(super) fn optimize_internal(acir: Circuit) -> (Circuit, Vec) { + // Track original acir opcode positions throughout the transformation passes of the compilation + // by applying the modifications done to the circuit opcodes and also to the opcode_positions (delete and insert) + let acir_opcode_positions = (0..acir.opcodes.len()).collect(); + + if acir.opcodes.len() == 1 && matches!(acir.opcodes[0], Opcode::Brillig(_)) { + info!("Program is fully unconstrained, skipping optimization pass"); + return (acir, acir_opcode_positions); + } + info!("Number of opcodes before: {}", acir.opcodes.len()); // General optimizer pass @@ -42,20 +53,22 @@ pub(super) fn optimize_internal(acir: Circuit) -> (Circuit, Vec) { .collect(); let acir = Circuit { opcodes, ..acir }; - // Track original acir opcode positions throughout the transformation passes of the compilation - // by applying the modifications done to the circuit opcodes and also to the opcode_positions (delete and insert) - let acir_opcode_positions = (0..acir.opcodes.len()).collect(); - // Unused memory optimization pass let memory_optimizer = UnusedMemoryOptimizer::new(acir); let (acir, acir_opcode_positions) = memory_optimizer.remove_unused_memory_initializations(acir_opcode_positions); + // let (acir, acir_opcode_positions) = + // ConstantBackpropagationOptimizer::backpropagate_constants(acir, acir_opcode_positions); + // Range optimization pass let range_optimizer = RangeOptimizer::new(acir); let (acir, acir_opcode_positions) = range_optimizer.replace_redundant_ranges(acir_opcode_positions); + // let (acir, acir_opcode_positions) = + // ConstantBackpropagationOptimizer::backpropagate_constants(acir, acir_opcode_positions); + info!("Number of opcodes after: {}", acir.opcodes.len()); (acir, acir_opcode_positions) diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/arithmetic.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/arithmetic.rs index 81462ea495e..dc9e13d44b6 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/arithmetic.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/arithmetic.rs @@ -7,7 +7,7 @@ use super::{insert_value, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionErro /// An Expression solver will take a Circuit's assert-zero opcodes with witness assignments /// and create the other witness variables -pub(super) struct ExpressionSolver; +pub(crate) struct ExpressionSolver; #[allow(clippy::enum_variant_names)] pub(super) enum OpcodeStatus { @@ -24,13 +24,18 @@ pub(crate) enum MulTerm { impl ExpressionSolver { /// Derives the rest of the witness based on the initial low level variables - pub(super) fn solve( + pub(crate) fn solve( initial_witness: &mut WitnessMap, opcode: &Expression, ) -> Result<(), OpcodeResolutionError> { let opcode = &ExpressionSolver::evaluate(opcode, initial_witness); // Evaluate multiplication term - let mul_result = ExpressionSolver::solve_mul_term(opcode, initial_witness); + let mul_result = + ExpressionSolver::solve_mul_term(opcode, initial_witness).map_err(|_| { + OpcodeResolutionError::OpcodeNotSolvable( + OpcodeNotSolvable::ExpressionHasTooManyUnknowns(opcode.clone()), + ) + })?; // Evaluate the fan-in terms let opcode_status = ExpressionSolver::solve_fan_in_term(opcode, initial_witness); @@ -54,9 +59,7 @@ impl ExpressionSolver { } } else { let assignment = -total_sum / (q + b); - // Add this into the witness assignments - insert_value(&w1, assignment, initial_witness)?; - Ok(()) + insert_value(&w1, assignment, initial_witness) } } else { // TODO: can we be more specific with this error? @@ -84,9 +87,7 @@ impl ExpressionSolver { } } else { let assignment = -(total_sum / partial_prod); - // Add this into the witness assignments - insert_value(&unknown_var, assignment, initial_witness)?; - Ok(()) + insert_value(&unknown_var, assignment, initial_witness) } } (MulTerm::Solved(a), OpcodeStatus::OpcodeSatisfied(b)) => { @@ -118,9 +119,7 @@ impl ExpressionSolver { } } else { let assignment = -(total_sum / coeff); - // Add this into the witness assignments - insert_value(&unknown_var, assignment, initial_witness)?; - Ok(()) + insert_value(&unknown_var, assignment, initial_witness) } } } @@ -130,16 +129,19 @@ impl ExpressionSolver { /// If the witness values are not known, then the function returns a None /// XXX: Do we need to account for the case where 5xy + 6x = 0 ? We do not know y, but it can be solved given x . But I believe x can be solved with another opcode /// XXX: What about making a mul opcode = a constant 5xy + 7 = 0 ? This is the same as the above. - fn solve_mul_term(arith_opcode: &Expression, witness_assignments: &WitnessMap) -> MulTerm { + fn solve_mul_term( + arith_opcode: &Expression, + witness_assignments: &WitnessMap, + ) -> Result { // First note that the mul term can only contain one/zero term // We are assuming it has been optimized. match arith_opcode.mul_terms.len() { - 0 => MulTerm::Solved(FieldElement::zero()), - 1 => ExpressionSolver::solve_mul_term_helper( + 0 => Ok(MulTerm::Solved(FieldElement::zero())), + 1 => Ok(ExpressionSolver::solve_mul_term_helper( &arith_opcode.mul_terms[0], witness_assignments, - ), - _ => panic!("Mul term in the assert-zero opcode must contain either zero or one term"), + )), + _ => Err(OpcodeStatus::OpcodeUnsolvable), } } @@ -209,7 +211,7 @@ impl ExpressionSolver { } // Partially evaluate the opcode using the known witnesses - pub(super) fn evaluate(expr: &Expression, initial_witness: &WitnessMap) -> Expression { + pub(crate) fn evaluate(expr: &Expression, initial_witness: &WitnessMap) -> Expression { let mut result = Expression::default(); for &(c, w1, w2) in &expr.mul_terms { let mul_result = ExpressionSolver::solve_mul_term_helper(&(c, w1, w2), initial_witness); diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/hash.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/hash.rs index 1bc26f06188..24c835a636a 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/hash.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/hash.rs @@ -143,12 +143,22 @@ pub(crate) fn solve_poseidon2_permutation_opcode( return Err(OpcodeResolutionError::BlackBoxFunctionFailed( acir::BlackBoxFunc::Poseidon2Permutation, format!( - "the number of inputs does not match specified length. {} > {}", + "the number of inputs does not match specified length. {} != {}", inputs.len(), len ), )); } + if len as usize != outputs.len() { + return Err(OpcodeResolutionError::BlackBoxFunctionFailed( + acir::BlackBoxFunc::Poseidon2Permutation, + format!( + "the number of outputs does not match specified length. {} != {}", + outputs.len(), + len + ), + )); + } // Read witness assignments let mut state = Vec::new(); diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs index 4309cad1b2e..6ee926043cd 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -25,7 +25,7 @@ use fixed_base_scalar_mul::{embedded_curve_add, fixed_base_scalar_mul}; use hash::{solve_generic_256_hash_opcode, solve_sha_256_permutation_opcode}; use logic::{and, xor}; use pedersen::pedersen; -use range::solve_range_opcode; +pub(crate) use range::solve_range_opcode; use signature::{ ecdsa::{secp256k1_prehashed, secp256r1_prehashed}, schnorr::schnorr_verify, diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/range.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/range.rs index 1b976e30ed5..2afe820b636 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/range.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/range.rs @@ -4,7 +4,7 @@ use crate::{ }; use acir::{circuit::opcodes::FunctionInput, native_types::WitnessMap}; -pub(super) fn solve_range_opcode( +pub(crate) fn solve_range_opcode( initial_witness: &WitnessMap, input: &FunctionInput, ) -> Result<(), OpcodeResolutionError> { diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs index b0fb7469fd9..51c7f4c6203 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs @@ -65,7 +65,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { /// Constructs a solver for a Brillig block given the bytecode and initial /// witness. - pub(super) fn new( + pub(crate) fn new( initial_witness: &WitnessMap, memory: &HashMap, brillig: &'b Brillig, @@ -134,7 +134,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { self.vm.get_call_stack() } - pub(super) fn solve(&mut self) -> Result { + pub(crate) fn solve(&mut self) -> Result { let status = self.vm.process_opcodes(); self.handle_vm_status(status) } @@ -177,7 +177,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { } } - pub(super) fn finalize( + pub(crate) fn finalize( self, witness: &mut WitnessMap, brillig: &Brillig, diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/directives/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/directives/mod.rs index 07226c85b27..ee544521fc7 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/directives/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/directives/mod.rs @@ -11,7 +11,7 @@ use super::{get_value, insert_value, ErrorLocation}; /// Returns `Ok(OpcodeResolution)` to signal whether the directive was successful solved. /// /// Returns `Err(OpcodeResolutionError)` if a circuit constraint is unsatisfied. -pub(super) fn solve_directives( +pub(crate) fn solve_directives( initial_witness: &mut WitnessMap, directive: &Directive, ) -> Result<(), OpcodeResolutionError> { diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/memory_op.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/memory_op.rs index 49ec652289e..e51797707a7 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/memory_op.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/memory_op.rs @@ -13,7 +13,7 @@ type MemoryIndex = u32; /// Maintains the state for solving [`MemoryInit`][`acir::circuit::Opcode::MemoryInit`] and [`MemoryOp`][`acir::circuit::Opcode::MemoryOp`] opcodes. #[derive(Default)] -pub(super) struct MemoryOpSolver { +pub(crate) struct MemoryOpSolver { pub(super) block_value: HashMap, pub(super) block_len: u32, } diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs index 2ee39a289e7..d8323e5ef5f 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs @@ -21,11 +21,11 @@ use thiserror::Error; // arithmetic pub(crate) mod arithmetic; // Brillig bytecode -mod brillig; +pub(crate) mod brillig; // Directives -mod directives; +pub(crate) mod directives; // black box functions -mod blackbox; +pub(crate) mod blackbox; mod memory_op; pub use self::brillig::{BrilligSolver, BrilligSolverStatus}; diff --git a/noir/noir-repo/aztec_macros/src/lib.rs b/noir/noir-repo/aztec_macros/src/lib.rs index febea007bd4..f9df3f10129 100644 --- a/noir/noir-repo/aztec_macros/src/lib.rs +++ b/noir/noir-repo/aztec_macros/src/lib.rs @@ -3,7 +3,7 @@ use std::vec; use convert_case::{Case, Casing}; use iter_extended::vecmap; -use noirc_errors::Location; +use noirc_errors::{Location, Spanned}; use noirc_frontend::hir::def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}; use noirc_frontend::hir::def_map::{LocalModuleId, ModuleId}; use noirc_frontend::macros_api::parse_program; @@ -11,17 +11,17 @@ use noirc_frontend::macros_api::FieldElement; use noirc_frontend::macros_api::{ BlockExpression, CallExpression, CastExpression, Distinctness, Expression, ExpressionKind, ForLoopStatement, ForRange, FunctionDefinition, FunctionReturnType, FunctionVisibility, - HirContext, HirExpression, HirLiteral, HirStatement, Ident, ImportStatement, IndexExpression, - LetStatement, Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, NoirStruct, - Param, Path, PathKind, Pattern, PrefixExpression, SecondaryAttribute, Signedness, Span, - Statement, StatementKind, StructType, Type, TypeImpl, UnaryOp, UnresolvedType, - UnresolvedTypeData, Visibility, + HirContext, HirExpression, HirLiteral, HirStatement, Ident, IndexExpression, LetStatement, + Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, NoirStruct, Param, Path, + PathKind, Pattern, PrefixExpression, SecondaryAttribute, Signedness, Span, Statement, + StatementKind, StructType, Type, TypeImpl, UnaryOp, UnresolvedType, UnresolvedTypeData, + Visibility, }; use noirc_frontend::macros_api::{CrateId, FileId}; use noirc_frontend::macros_api::{MacroError, MacroProcessor}; use noirc_frontend::macros_api::{ModuleDefId, NodeInterner, SortedModule, StructId}; use noirc_frontend::node_interner::{FuncId, TraitId, TraitImplId, TraitImplKind}; -use noirc_frontend::Lambda; +use noirc_frontend::{BinaryOpKind, ConstrainKind, ConstrainStatement, InfixExpression, Lambda}; pub struct AztecMacro; impl MacroProcessor for AztecMacro { @@ -221,6 +221,14 @@ fn lambda(parameters: Vec<(Pattern, UnresolvedType)>, body: Expression) -> Expre }))) } +fn make_eq(lhs: Expression, rhs: Expression) -> Expression { + expression(ExpressionKind::Infix(Box::new(InfixExpression { + lhs, + rhs, + operator: Spanned::from(Span::default(), BinaryOpKind::Equal), + }))) +} + macro_rules! chained_path { ( $base:expr ) => { { @@ -273,10 +281,6 @@ fn index_array_variable(array: Expression, index: &str) -> Expression { }))) } -fn import(path: Path) -> ImportStatement { - ImportStatement { path, alias: None } -} - // // Create AST Nodes for Aztec // @@ -296,7 +300,6 @@ fn transform( .map_err(|(err, file_id)| (err.into(), file_id))? { check_for_aztec_dependency(crate_id, context)?; - include_relevant_imports(&mut submodule.contents); } } Ok(ast) @@ -315,21 +318,6 @@ fn transform_hir( assign_storage_slots(crate_id, context) } -/// Includes an import to the aztec library if it has not been included yet -fn include_relevant_imports(ast: &mut SortedModule) { - // Create the aztec import path using the assumed chained_dep! macro - let aztec_import_path = import(chained_dep!("aztec")); - - // Check if the aztec import already exists - let is_aztec_imported = - ast.imports.iter().any(|existing_import| existing_import.path == aztec_import_path.path); - - // If aztec is not imported, add the import at the beginning - if !is_aztec_imported { - ast.imports.insert(0, aztec_import_path); - } -} - /// Creates an error alerting the user that they have not downloaded the Aztec-noir library fn check_for_aztec_dependency( crate_id: &CrateId, @@ -440,7 +428,7 @@ fn transform_module( .attributes .secondary .iter() - .any(|attr| is_custom_attribute(&attr, "aztec(initializer)")) + .any(|attr| is_custom_attribute(attr, "aztec(initializer)")) }); for func in module.functions.iter_mut() { @@ -448,6 +436,7 @@ fn transform_module( let mut is_public = false; let mut is_public_vm = false; let mut is_initializer = false; + let mut is_internal = false; let mut insert_init_check = has_initializer; for secondary_attribute in func.def.attributes.secondary.clone() { @@ -458,9 +447,10 @@ fn transform_module( insert_init_check = false; } else if is_custom_attribute(&secondary_attribute, "aztec(noinitcheck)") { insert_init_check = false; + } else if is_custom_attribute(&secondary_attribute, "aztec(internal)") { + is_internal = true; } else if is_custom_attribute(&secondary_attribute, "aztec(public)") { is_public = true; - insert_init_check = false; } else if is_custom_attribute(&secondary_attribute, "aztec(public-vm)") { is_public_vm = true; } @@ -474,6 +464,7 @@ fn transform_module( storage_defined, is_initializer, insert_init_check, + is_internal, ) .map_err(|err| (err, crate_graph.root_file_id))?; has_transformed_module = true; @@ -548,7 +539,7 @@ fn generate_storage_field_constructor( ( pattern("context"), make_type(UnresolvedTypeData::Named( - chained_path!("aztec", "context", "Context"), + chained_dep!("aztec", "context", "Context"), vec![], true, )), @@ -632,7 +623,7 @@ fn generate_storage_implementation(module: &mut SortedModule) -> Result<(), Azte &[( ident("context"), make_type(UnresolvedTypeData::Named( - chained_path!("aztec", "context", "Context"), + chained_dep!("aztec", "context", "Context"), vec![], true, )), @@ -666,22 +657,21 @@ fn transform_function( storage_defined: bool, is_initializer: bool, insert_init_check: bool, + is_internal: bool, ) -> Result<(), AztecMacroError> { let context_name = format!("{}Context", ty); let inputs_name = format!("{}ContextInputs", ty); let return_type_name = format!("{}CircuitPublicInputs", ty); + // Add check that msg sender equals this address and flag function as internal + if is_internal { + let is_internal_check = create_internal_check(func.name()); + func.def.body.0.insert(0, is_internal_check); + func.def.is_internal = true; + } + // Add initialization check if insert_init_check { - if ty == "Public" { - let error = AztecMacroError::UnsupportedAttributes { - span: func.def.name.span(), - secondary_message: Some( - "public functions do not yet support initialization check".to_owned(), - ), - }; - return Err(error); - } let init_check = create_init_check(); func.def.body.0.insert(0, init_check); } @@ -711,16 +701,7 @@ fn transform_function( // Before returning mark the contract as initialized if is_initializer { - if ty == "Public" { - let error = AztecMacroError::UnsupportedAttributes { - span: func.def.name.span(), - secondary_message: Some( - "public functions cannot yet be used as initializers".to_owned(), - ), - }; - return Err(error); - } - let mark_initialized = create_mark_as_initialized(); + let mark_initialized = create_mark_as_initialized(ty); func.def.body.0.push(mark_initialized); } @@ -1097,7 +1078,7 @@ fn generate_selector_impl(structure: &NoirStruct) -> TypeImpl { make_type(UnresolvedTypeData::Named(path(structure.name.clone()), vec![], true)); let selector_path = - chained_path!("aztec", "protocol_types", "abis", "function_selector", "FunctionSelector"); + chained_dep!("aztec", "protocol_types", "abis", "function_selector", "FunctionSelector"); let mut from_signature_path = selector_path.clone(); from_signature_path.segments.push(ident("from_signature")); @@ -1152,7 +1133,7 @@ fn create_inputs(ty: &str) -> Param { let context_pattern = Pattern::Identifier(context_ident); let path_snippet = ty.to_case(Case::Snake); // e.g. private_context_inputs - let type_path = chained_path!("aztec", "context", "inputs", &path_snippet, ty); + let type_path = chained_dep!("aztec", "context", "inputs", &path_snippet, ty); let context_type = make_type(UnresolvedTypeData::Named(type_path, vec![], true)); let visibility = Visibility::Private; @@ -1168,7 +1149,7 @@ fn create_inputs(ty: &str) -> Param { /// ``` fn create_init_check() -> Statement { make_statement(StatementKind::Expression(call( - variable_path(chained_path!("aztec", "initializer", "assert_is_initialized")), + variable_path(chained_dep!("aztec", "initializer", "assert_is_initialized")), vec![mutable_reference("context")], ))) } @@ -1179,13 +1160,33 @@ fn create_init_check() -> Statement { /// ```noir /// mark_as_initialized(&mut context); /// ``` -fn create_mark_as_initialized() -> Statement { +fn create_mark_as_initialized(ty: &str) -> Statement { + let name = if ty == "Public" { "mark_as_initialized_public" } else { "mark_as_initialized" }; make_statement(StatementKind::Expression(call( - variable_path(chained_path!("aztec", "initializer", "mark_as_initialized")), + variable_path(chained_dep!("aztec", "initializer", name)), vec![mutable_reference("context")], ))) } +/// Creates a check for internal functions ensuring that the caller is self. +/// +/// ```noir +/// assert(context.msg_sender() == context.this_address(), "Function can only be called internally"); +/// ``` +fn create_internal_check(fname: &str) -> Statement { + make_statement(StatementKind::Constrain(ConstrainStatement( + make_eq( + method_call(variable("context"), "msg_sender", vec![]), + method_call(variable("context"), "this_address", vec![]), + ), + Some(expression(ExpressionKind::Literal(Literal::Str(format!( + "Function {} can only be called internally", + fname + ))))), + ConstrainKind::Assert, + ))) +} + /// Creates the private context object to be accessed within the function, the parameters need to be extracted to be /// appended into the args hash object. /// @@ -1218,8 +1219,8 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac let let_hasher = mutable_assignment( "hasher", // Assigned to call( - variable_path(chained_path!("aztec", "hasher", "Hasher", "new")), // Path - vec![], // args + variable_path(chained_dep!("aztec", "hasher", "Hasher", "new")), // Path + vec![], // args ), ); @@ -1287,8 +1288,8 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac let let_context = mutable_assignment( "context", // Assigned to call( - variable_path(chained_path!("aztec", "context", &path_snippet, ty, "new")), // Path - vec![inputs_expression, hash_call], // args + variable_path(chained_dep!("aztec", "context", &path_snippet, ty, "new")), // Path + vec![inputs_expression, hash_call], // args ), ); injected_expressions.push(let_context); @@ -1318,8 +1319,8 @@ fn create_avm_context() -> Result { let let_context = mutable_assignment( "context", // Assigned to call( - variable_path(chained_path!("aztec", "context", "AVMContext", "new")), // Path - vec![], // args + variable_path(chained_dep!("aztec", "context", "AVMContext", "new")), // Path + vec![], // args ), ); @@ -1354,13 +1355,13 @@ fn create_avm_context() -> Result { /// Any primitive type that can be cast will be casted to a field and pushed to the context. fn abstract_return_values(func: &NoirFunction) -> Option { let current_return_type = func.return_type().typ; - let last_statement = func.def.body.0.last(); + let last_statement = func.def.body.0.last()?; // TODO: (length, type) => We can limit the size of the array returned to be limited by kernel size // Doesn't need done until we have settled on a kernel size // TODO: support tuples here and in inputs -> convert into an issue // Check if the return type is an expression, if it is, we can handle it - match last_statement? { + match last_statement { Statement { kind: StatementKind::Expression(expression), .. } => { match current_return_type { // Call serialize on structs, push the whole array, calling push_array @@ -1404,13 +1405,13 @@ fn abstract_return_values(func: &NoirFunction) -> Option { fn abstract_storage(typ: &str, unconstrained: bool) -> Statement { let init_context_call = if unconstrained { call( - variable_path(chained_path!("aztec", "context", "Context", "none")), // Path - vec![], // args + variable_path(chained_dep!("aztec", "context", "Context", "none")), // Path + vec![], // args ) } else { call( - variable_path(chained_path!("aztec", "context", "Context", typ)), // Path - vec![mutable_reference("context")], // args + variable_path(chained_dep!("aztec", "context", "Context", typ)), // Path + vec![mutable_reference("context")], // args ) }; @@ -1531,7 +1532,7 @@ fn make_castable_return_type(expression: Expression) -> Statement { /// } fn create_return_type(ty: &str) -> FunctionReturnType { let path_snippet = ty.to_case(Case::Snake); // e.g. private_circuit_public_inputs or public_circuit_public_inputs - let return_path = chained_path!("aztec", "protocol_types", "abis", &path_snippet, ty); + let return_path = chained_dep!("aztec", "protocol_types", "abis", &path_snippet, ty); return_type(return_path) } diff --git a/noir/noir-repo/bootstrap.sh b/noir/noir-repo/bootstrap.sh new file mode 100755 index 00000000000..54129c3d61a --- /dev/null +++ b/noir/noir-repo/bootstrap.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -eu + +cd $(dirname "$0") + +CMD=${1:-} + +if [ -n "$CMD" ]; then + if [ "$CMD" = "clean" ]; then + git clean -fdx + exit 0 + else + echo "Unknown command: $CMD" + exit 1 + fi +fi + +# Attempt to just pull artefacts from CI and exit on success. +[ -n "${USE_CACHE:-}" ] && ./bootstrap_cache.sh && exit + +./scripts/bootstrap_native.sh +./scripts/bootstrap_packages.sh diff --git a/noir/noir-repo/bootstrap_cache.sh b/noir/noir-repo/bootstrap_cache.sh new file mode 100755 index 00000000000..1cec6c81d8e --- /dev/null +++ b/noir/noir-repo/bootstrap_cache.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -eu + +cd "$(dirname "$0")" +source ../build-system/scripts/setup_env '' '' mainframe_$USER > /dev/null + +echo -e "\033[1mRetrieving noir packages from remote cache...\033[0m" +extract_repo noir-packages /usr/src/noir/packages ./ +echo -e "\033[1mRetrieving nargo from remote cache...\033[0m" +extract_repo noir /usr/src/noir/target/release ./target/ + +remove_old_images noir-packages +remove_old_images noir diff --git a/noir/noir-repo/compiler/integration-tests/package.json b/noir/noir-repo/compiler/integration-tests/package.json index 2e75ebc201e..798b7c55312 100644 --- a/noir/noir-repo/compiler/integration-tests/package.json +++ b/noir/noir-repo/compiler/integration-tests/package.json @@ -19,6 +19,7 @@ "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", "@nomicfoundation/hardhat-ethers": "^3.0.0", "@web/dev-server-esbuild": "^0.3.6", + "@web/dev-server-import-maps": "^0.2.0", "@web/test-runner": "^0.15.3", "@web/test-runner-playwright": "^0.10.0", "eslint": "^8.56.0", diff --git a/noir/noir-repo/compiler/integration-tests/test/mocks/os.js b/noir/noir-repo/compiler/integration-tests/test/mocks/os.js new file mode 100644 index 00000000000..32333568316 --- /dev/null +++ b/noir/noir-repo/compiler/integration-tests/test/mocks/os.js @@ -0,0 +1 @@ +export const os = { cpus: () => new Array(4) }; diff --git a/noir/noir-repo/compiler/integration-tests/web-test-runner.config.mjs b/noir/noir-repo/compiler/integration-tests/web-test-runner.config.mjs index 665ea262f99..4dfc96dd0a6 100644 --- a/noir/noir-repo/compiler/integration-tests/web-test-runner.config.mjs +++ b/noir/noir-repo/compiler/integration-tests/web-test-runner.config.mjs @@ -2,7 +2,8 @@ import { defaultReporter } from '@web/test-runner'; import { summaryReporter } from '@web/test-runner'; import { fileURLToPath } from 'url'; import { esbuildPlugin } from '@web/dev-server-esbuild'; -import { playwrightLauncher } from "@web/test-runner-playwright"; +import { playwrightLauncher } from '@web/test-runner-playwright'; +import { importMapsPlugin } from '@web/dev-server-import-maps'; let reporter = summaryReporter(); const debugPlugins = []; @@ -21,7 +22,7 @@ if (process.env.CI !== 'true' || process.env.RUNNER_DEBUG === '1') { export default { browsers: [ - playwrightLauncher({ product: "chromium" }), + playwrightLauncher({ product: 'chromium' }), // playwrightLauncher({ product: "webkit" }), // playwrightLauncher({ product: "firefox" }), ], @@ -29,6 +30,16 @@ export default { esbuildPlugin({ ts: true, }), + importMapsPlugin({ + inject: { + importMap: { + imports: { + // mock os module + os: '/test/mocks/os.js', + }, + }, + }, + }), ...debugPlugins, ], files: ['test/browser/**/*.test.ts'], diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index f01f60252f6..c04d8475f08 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -1,9 +1,7 @@ use crate::brillig::brillig_ir::brillig_variable::{ type_to_heap_value_type, BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable, }; -use crate::brillig::brillig_ir::{ - BrilligBinaryOp, BrilligContext, BRILLIG_INTEGER_ARITHMETIC_BIT_SIZE, -}; +use crate::brillig::brillig_ir::{BrilligBinaryOp, BrilligContext}; use crate::ssa::ir::dfg::CallStack; use crate::ssa::ir::instruction::ConstrainError; use crate::ssa::ir::{ @@ -626,37 +624,40 @@ impl<'block> BrilligBlock<'block> { } Instruction::RangeCheck { value, max_bit_size, assert_message } => { let value = self.convert_ssa_single_addr_value(*value, dfg); - // Cast original value to field - let left = SingleAddrVariable { - address: self.brillig_context.allocate_register(), - bit_size: FieldElement::max_num_bits(), - }; - self.convert_cast(left, value); + // SSA generates redundant range checks. A range check with a max bit size >= value.bit_size will always pass. + if value.bit_size > *max_bit_size { + // Cast original value to field + let left = SingleAddrVariable { + address: self.brillig_context.allocate_register(), + bit_size: FieldElement::max_num_bits(), + }; + self.convert_cast(left, value); - // Create a field constant with the max - let max = BigUint::from(2_u128).pow(*max_bit_size) - BigUint::from(1_u128); - let right = self.brillig_context.make_constant( - FieldElement::from_be_bytes_reduce(&max.to_bytes_be()).into(), - FieldElement::max_num_bits(), - ); + // Create a field constant with the max + let max = BigUint::from(2_u128).pow(*max_bit_size) - BigUint::from(1_u128); + let right = self.brillig_context.make_constant( + FieldElement::from_be_bytes_reduce(&max.to_bytes_be()).into(), + FieldElement::max_num_bits(), + ); - // Check if lte max - let brillig_binary_op = BrilligBinaryOp::Integer { - op: BinaryIntOp::LessThanEquals, - bit_size: FieldElement::max_num_bits(), - }; - let condition = self.brillig_context.allocate_register(); - self.brillig_context.binary_instruction( - left.address, - right, - condition, - brillig_binary_op, - ); + // Check if lte max + let brillig_binary_op = BrilligBinaryOp::Integer { + op: BinaryIntOp::LessThanEquals, + bit_size: FieldElement::max_num_bits(), + }; + let condition = self.brillig_context.allocate_register(); + self.brillig_context.binary_instruction( + left.address, + right, + condition, + brillig_binary_op, + ); - self.brillig_context.constrain_instruction(condition, assert_message.clone()); - self.brillig_context.deallocate_register(condition); - self.brillig_context.deallocate_register(left.address); - self.brillig_context.deallocate_register(right); + self.brillig_context.constrain_instruction(condition, assert_message.clone()); + self.brillig_context.deallocate_register(condition); + self.brillig_context.deallocate_register(left.address); + self.brillig_context.deallocate_register(right); + } } Instruction::IncrementRc { value } => { let rc_register = match self.convert_ssa_value(*value, dfg) { @@ -1197,7 +1198,7 @@ impl<'block> BrilligBlock<'block> { let left = self.convert_ssa_single_addr_value(binary.lhs, dfg); let right = self.convert_ssa_single_addr_value(binary.rhs, dfg); - let brillig_binary_op = + let (brillig_binary_op, is_signed) = convert_ssa_binary_op_to_brillig_binary_op(binary.operator, &binary_type); self.brillig_context.binary_instruction( @@ -1206,6 +1207,93 @@ impl<'block> BrilligBlock<'block> { result_variable.address, brillig_binary_op, ); + + self.add_overflow_check(brillig_binary_op, left, right, result_variable, is_signed); + } + + fn add_overflow_check( + &mut self, + binary_operation: BrilligBinaryOp, + left: SingleAddrVariable, + right: SingleAddrVariable, + result: SingleAddrVariable, + is_signed: bool, + ) { + let (op, bit_size) = if let BrilligBinaryOp::Integer { op, bit_size } = binary_operation { + (op, bit_size) + } else { + return; + }; + + match (op, is_signed) { + (BinaryIntOp::Add, false) => { + let condition = self.brillig_context.allocate_register(); + // Check that lhs <= result + self.brillig_context.binary_instruction( + left.address, + result.address, + condition, + BrilligBinaryOp::Integer { op: BinaryIntOp::LessThanEquals, bit_size }, + ); + self.brillig_context.constrain_instruction( + condition, + Some("attempt to add with overflow".to_string()), + ); + self.brillig_context.deallocate_register(condition); + } + (BinaryIntOp::Sub, false) => { + let condition = self.brillig_context.allocate_register(); + // Check that rhs <= lhs + self.brillig_context.binary_instruction( + right.address, + left.address, + condition, + BrilligBinaryOp::Integer { op: BinaryIntOp::LessThanEquals, bit_size }, + ); + self.brillig_context.constrain_instruction( + condition, + Some("attempt to subtract with overflow".to_string()), + ); + self.brillig_context.deallocate_register(condition); + } + (BinaryIntOp::Mul, false) => { + // Multiplication overflow is only possible for bit sizes > 1 + if bit_size > 1 { + let is_right_zero = self.brillig_context.allocate_register(); + let zero = self.brillig_context.make_constant(0_usize.into(), bit_size); + self.brillig_context.binary_instruction( + zero, + right.address, + is_right_zero, + BrilligBinaryOp::Integer { op: BinaryIntOp::Equals, bit_size }, + ); + self.brillig_context.if_not_instruction(is_right_zero, |ctx| { + let condition = ctx.allocate_register(); + // Check that result / rhs == lhs + ctx.binary_instruction( + result.address, + right.address, + condition, + BrilligBinaryOp::Integer { op: BinaryIntOp::UnsignedDiv, bit_size }, + ); + ctx.binary_instruction( + condition, + left.address, + condition, + BrilligBinaryOp::Integer { op: BinaryIntOp::Equals, bit_size }, + ); + ctx.constrain_instruction( + condition, + Some("attempt to multiply with overflow".to_string()), + ); + ctx.deallocate_register(condition); + }); + self.brillig_context.deallocate_register(is_right_zero); + self.brillig_context.deallocate_register(zero); + } + } + _ => {} + } } /// Converts an SSA `ValueId` into a `RegisterOrMemory`. Initializes if necessary. @@ -1403,8 +1491,6 @@ impl<'block> BrilligBlock<'block> { } /// Returns the type of the operation considering the types of the operands -/// TODO: SSA issues binary operations between fields and integers. -/// This probably should be explicitly casted in SSA to avoid having to coerce at this level. pub(crate) fn type_of_binary_operation(lhs_type: &Type, rhs_type: &Type) -> Type { match (lhs_type, rhs_type) { (_, Type::Function) | (Type::Function, _) => { @@ -1419,10 +1505,6 @@ pub(crate) fn type_of_binary_operation(lhs_type: &Type, rhs_type: &Type) -> Type (_, Type::Slice(..)) | (Type::Slice(..), _) => { unreachable!("Arrays are invalid in binary operations") } - // If either side is a Field constant then, we coerce into the type - // of the other operand - (Type::Numeric(NumericType::NativeField), typ) - | (typ, Type::Numeric(NumericType::NativeField)) => typ.clone(), // If both sides are numeric type, then we expect their types to be // the same. (Type::Numeric(lhs_type), Type::Numeric(rhs_type)) => { @@ -1441,7 +1523,7 @@ pub(crate) fn type_of_binary_operation(lhs_type: &Type, rhs_type: &Type) -> Type pub(crate) fn convert_ssa_binary_op_to_brillig_binary_op( ssa_op: BinaryOp, typ: &Type, -) -> BrilligBinaryOp { +) -> (BrilligBinaryOp, bool) { // First get the bit size and whether its a signed integer, if it is a numeric type // if it is not,then we return None, indicating that // it is a Field. @@ -1461,10 +1543,6 @@ pub(crate) fn convert_ssa_binary_op_to_brillig_binary_op( BinaryOp::Mul => BrilligBinaryOp::Field { op: BinaryFieldOp::Mul }, BinaryOp::Div => BrilligBinaryOp::Field { op: BinaryFieldOp::Div }, BinaryOp::Eq => BrilligBinaryOp::Field { op: BinaryFieldOp::Equals }, - BinaryOp::Lt => BrilligBinaryOp::Integer { - op: BinaryIntOp::LessThan, - bit_size: BRILLIG_INTEGER_ARITHMETIC_BIT_SIZE, - }, _ => unreachable!( "Field type cannot be used with {op}. This should have been caught by the frontend" ), @@ -1500,7 +1578,9 @@ pub(crate) fn convert_ssa_binary_op_to_brillig_binary_op( // If bit size is available then it is a binary integer operation match bit_size_signedness { - Some((bit_size, is_signed)) => binary_op_to_int_op(ssa_op, *bit_size, is_signed), - None => binary_op_to_field_op(ssa_op), + Some((bit_size, is_signed)) => { + (binary_op_to_int_op(ssa_op, *bit_size, is_signed), is_signed) + } + None => (binary_op_to_field_op(ssa_op), false), } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 90608974f98..f1a8f24ed03 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -29,17 +29,6 @@ use acvm::{ use debug_show::DebugShow; use num_bigint::BigUint; -/// Integer arithmetic in Brillig is limited to 127 bit -/// integers. -/// -/// We could lift this in the future and have Brillig -/// do big integer arithmetic when it exceeds the field size -/// or we could have users re-implement big integer arithmetic -/// in Brillig. -/// Since constrained functions do not have this property, it -/// would mean that unconstrained functions will differ from -/// constrained functions in terms of syntax compatibility. -pub(crate) const BRILLIG_INTEGER_ARITHMETIC_BIT_SIZE: u32 = 127; /// The Brillig VM does not apply a limit to the memory address space, /// As a convention, we take use 64 bits. This means that we assume that /// memory has 2^64 memory slots. @@ -356,6 +345,21 @@ impl BrilligContext { self.enter_section(end_section); } + /// This instruction issues a branch that jumps over the code generated by the given function if the condition is truthy + pub(crate) fn if_not_instruction( + &mut self, + condition: MemoryAddress, + f: impl FnOnce(&mut BrilligContext), + ) { + let (end_section, end_label) = self.reserve_next_section_label(); + + self.jump_if_instruction(condition, end_label.clone()); + + f(self); + + self.enter_section(end_section); + } + /// Adds a label to the next opcode pub(crate) fn enter_context(&mut self, label: T) { self.debug_show.enter_context(label.to_string()); @@ -533,7 +537,7 @@ impl BrilligContext { result: MemoryAddress, operation: BrilligBinaryOp, ) { - self.debug_show.binary_instruction(lhs, rhs, result, operation.clone()); + self.debug_show.binary_instruction(lhs, rhs, result, operation); match operation { BrilligBinaryOp::Field { op } => { let opcode = BrilligOpcode::BinaryFieldOp { op, destination: result, lhs, rhs }; @@ -1082,7 +1086,7 @@ impl BrilligContext { } /// Type to encapsulate the binary operation types in Brillig -#[derive(Clone)] +#[derive(Clone, Copy)] pub(crate) enum BrilligBinaryOp { Field { op: BinaryFieldOp }, Integer { op: BinaryIntOp, bit_size: u32 }, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/expr.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/expr.rs index b78f07c88f2..7b854e58fca 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -326,7 +326,10 @@ impl<'interner> TypeChecker<'interner> { assert_eq!(the_trait.generics.len(), constraint.trait_generics.len()); for (param, arg) in the_trait.generics.iter().zip(&constraint.trait_generics) { - bindings.insert(param.id(), (param.clone(), arg.clone())); + // Avoid binding t = t + if !arg.occurs(param.id()) { + bindings.insert(param.id(), (param.clone(), arg.clone())); + } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs index e105da1ccf0..b70aa43701c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs @@ -1558,7 +1558,7 @@ impl Type { } /// True if the given TypeVariableId is free anywhere within self - fn occurs(&self, target_id: TypeVariableId) -> bool { + pub fn occurs(&self, target_id: TypeVariableId) -> bool { match self { Type::Array(len, elem) => len.occurs(target_id) || elem.occurs(target_id), Type::String(len) => len.occurs(target_id), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs index fe12132e202..f096c220200 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs @@ -661,7 +661,6 @@ pub enum Keyword { If, Impl, In, - Internal, Let, Mod, Mut, @@ -673,6 +672,7 @@ pub enum Keyword { Struct, Trait, Type, + Unchecked, Unconstrained, Use, Where, @@ -703,7 +703,6 @@ impl fmt::Display for Keyword { Keyword::If => write!(f, "if"), Keyword::Impl => write!(f, "impl"), Keyword::In => write!(f, "in"), - Keyword::Internal => write!(f, "internal"), Keyword::Let => write!(f, "let"), Keyword::Mod => write!(f, "mod"), Keyword::Mut => write!(f, "mut"), @@ -715,6 +714,7 @@ impl fmt::Display for Keyword { Keyword::Struct => write!(f, "struct"), Keyword::Trait => write!(f, "trait"), Keyword::Type => write!(f, "type"), + Keyword::Unchecked => write!(f, "unchecked"), Keyword::Unconstrained => write!(f, "unconstrained"), Keyword::Use => write!(f, "use"), Keyword::Where => write!(f, "where"), @@ -748,7 +748,6 @@ impl Keyword { "if" => Keyword::If, "impl" => Keyword::Impl, "in" => Keyword::In, - "internal" => Keyword::Internal, "let" => Keyword::Let, "mod" => Keyword::Mod, "mut" => Keyword::Mut, @@ -760,6 +759,7 @@ impl Keyword { "struct" => Keyword::Struct, "trait" => Keyword::Trait, "type" => Keyword::Type, + "unchecked" => Keyword::Unchecked, "unconstrained" => Keyword::Unconstrained, "use" => Keyword::Use, "where" => Keyword::Where, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs index 1cb81e26a0a..75f4a6359bf 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs @@ -23,6 +23,8 @@ //! prevent other parsers from being tried afterward since there is no longer an error. Thus, they should //! be limited to cases like the above `fn` example where it is clear we shouldn't back out of the //! current parser to try alternative parsers in a `choice` expression. +use self::primitives::{keyword, mutable_reference, variable}; + use super::{ foldl_with_span, labels::ParsingRuleLabel, parameter_name_recovery, parameter_recovery, parenthesized, then_commit, then_commit_ignore, top_level_statement_recovery, ExprParser, @@ -35,13 +37,11 @@ use crate::ast::{ }; use crate::lexer::Lexer; use crate::parser::{force, ignore_then_commit, statement_recovery}; -use crate::token::{Attribute, Attributes, Keyword, SecondaryAttribute, Token, TokenKind}; +use crate::token::{Keyword, Token, TokenKind}; use crate::{ - BinaryOp, BinaryOpKind, BlockExpression, ConstrainKind, ConstrainStatement, Distinctness, - ForLoopStatement, ForRange, FunctionDefinition, FunctionReturnType, FunctionVisibility, Ident, - IfExpression, InfixExpression, LValue, Lambda, Literal, NoirFunction, NoirStruct, NoirTrait, - NoirTraitImpl, NoirTypeAlias, Param, Path, PathKind, Pattern, Recoverable, Statement, - TraitBound, TraitImplItem, TraitItem, TypeImpl, UnaryOp, UnresolvedTraitConstraint, + BinaryOp, BinaryOpKind, BlockExpression, Distinctness, ForLoopStatement, ForRange, + FunctionReturnType, Ident, IfExpression, InfixExpression, LValue, Literal, NoirTypeAlias, + Param, Path, Pattern, Recoverable, Statement, TraitBound, TypeImpl, UnresolvedTraitConstraint, UnresolvedTypeExpression, UseTree, UseTreeKind, Visibility, }; @@ -49,6 +49,23 @@ use chumsky::prelude::*; use iter_extended::vecmap; use noirc_errors::{Span, Spanned}; +mod assertion; +mod attributes; +mod function; +mod lambdas; +mod literals; +mod path; +mod primitives; +mod structs; +mod traits; + +#[cfg(test)] +mod test_helpers; + +use literals::literal; +use path::{maybe_empty_path, path}; +use primitives::{dereference, ident, negation, not, nothing, right_shift_operator, token_kind}; + /// Entry function for the parser - also handles lexing internally. /// /// Given a source_program string, return the ParsedModule Ast representation @@ -109,10 +126,10 @@ fn top_level_statement( module_parser: impl NoirParser, ) -> impl NoirParser { choice(( - function_definition(false).map(TopLevelStatement::Function), - struct_definition(), - trait_definition(), - trait_implementation(), + function::function_definition(false).map(TopLevelStatement::Function), + structs::struct_definition(), + traits::trait_definition(), + traits::trait_implementation(), implementation(), type_alias_definition().then_ignore(force(just(Token::Semicolon))), submodule(module_parser.clone()), @@ -124,6 +141,21 @@ fn top_level_statement( .recover_via(top_level_statement_recovery()) } +/// Parses a non-trait implementation, adding a set of methods to a type. +/// +/// implementation: 'impl' generics type '{' function_definition ... '}' +fn implementation() -> impl NoirParser { + keyword(Keyword::Impl) + .ignore_then(function::generics()) + .then(parse_type().map_with_span(|typ, span| (typ, span))) + .then_ignore(just(Token::LeftBrace)) + .then(spanned(function::function_definition(true)).repeated()) + .then_ignore(just(Token::RightBrace)) + .map(|((generics, (object_type, type_span)), methods)| { + TopLevelStatement::Impl(TypeImpl { generics, object_type, type_span, methods }) + }) +} + /// global_declaration: 'global' ident global_type_annotation '=' literal fn global_declaration() -> impl NoirParser { let p = ignore_then_commit( @@ -160,121 +192,11 @@ fn contract(module_parser: impl NoirParser) -> impl NoirParser impl NoirParser { - attributes() - .then(function_modifiers()) - .then_ignore(keyword(Keyword::Fn)) - .then(ident()) - .then(generics()) - .then(parenthesized(function_parameters(allow_self))) - .then(function_return_type()) - .then(where_clause()) - .then(spanned(block(fresh_statement()))) - .validate(|(((args, ret), where_clause), (body, body_span)), span, emit| { - let ((((attributes, modifiers), name), generics), parameters) = args; - - // Validate collected attributes, filtering them into function and secondary variants - let attributes = validate_attributes(attributes, span, emit); - FunctionDefinition { - span: body_span, - name, - attributes, - is_unconstrained: modifiers.0, - is_open: modifiers.2, - is_internal: modifiers.3, - visibility: if modifiers.1 { - FunctionVisibility::PublicCrate - } else if modifiers.4 { - FunctionVisibility::Public - } else { - FunctionVisibility::Private - }, - generics, - parameters, - body, - where_clause, - return_type: ret.1, - return_visibility: ret.0 .1, - return_distinctness: ret.0 .0, - } - .into() - }) -} - -/// function_modifiers: 'unconstrained'? 'pub(crate)'? 'pub'? 'open'? 'internal'? -/// -/// returns (is_unconstrained, is_pub_crate, is_open, is_internal, is_pub) for whether each keyword was present -fn function_modifiers() -> impl NoirParser<(bool, bool, bool, bool, bool)> { - keyword(Keyword::Unconstrained) - .or_not() - .then(is_pub_crate()) - .then(keyword(Keyword::Pub).or_not()) - .then(keyword(Keyword::Open).or_not()) - .then(keyword(Keyword::Internal).or_not()) - .map(|((((unconstrained, pub_crate), public), open), internal)| { - ( - unconstrained.is_some(), - pub_crate, - open.is_some(), - internal.is_some(), - public.is_some(), - ) - }) -} - -fn is_pub_crate() -> impl NoirParser { - (keyword(Keyword::Pub) - .then_ignore(just(Token::LeftParen)) - .then_ignore(keyword(Keyword::Crate)) - .then_ignore(just(Token::RightParen))) - .or_not() - .map(|a| a.is_some()) -} - -/// non_empty_ident_list: ident ',' non_empty_ident_list -/// | ident -/// -/// generics: '<' non_empty_ident_list '>' -/// | %empty -fn generics() -> impl NoirParser> { - ident() - .separated_by(just(Token::Comma)) - .allow_trailing() - .at_least(1) - .delimited_by(just(Token::Less), just(Token::Greater)) - .or_not() - .map(|opt| opt.unwrap_or_default()) -} - -fn struct_definition() -> impl NoirParser { - use self::Keyword::Struct; - use Token::*; - - let fields = struct_fields() - .delimited_by(just(LeftBrace), just(RightBrace)) - .recover_with(nested_delimiters( - LeftBrace, - RightBrace, - [(LeftParen, RightParen), (LeftBracket, RightBracket)], - |_| vec![], - )) - .or(just(Semicolon).to(Vec::new())); - - attributes().then_ignore(keyword(Struct)).then(ident()).then(generics()).then(fields).validate( - |(((raw_attributes, name), generics), fields), span, emit| { - let attributes = validate_struct_attributes(raw_attributes, span, emit); - TopLevelStatement::Struct(NoirStruct { name, attributes, generics, fields, span }) - }, - ) -} - fn type_alias_definition() -> impl NoirParser { use self::Keyword::Type; let p = ignore_then_commit(keyword(Type), ident()); - let p = then_commit(p, generics()); + let p = then_commit(p, function::generics()); let p = then_commit_ignore(p, just(Token::Assign)); let p = then_commit(p, parse_type()); @@ -283,13 +205,6 @@ fn type_alias_definition() -> impl NoirParser { }) } -fn lambda_return_type() -> impl NoirParser { - just(Token::Arrow) - .ignore_then(parse_type()) - .or_not() - .map(|ret| ret.unwrap_or_else(UnresolvedType::unspecified)) -} - fn function_return_type() -> impl NoirParser<((Distinctness, Visibility), FunctionReturnType)> { just(Token::Arrow) .ignore_then(optional_distinctness()) @@ -305,69 +220,6 @@ fn function_return_type() -> impl NoirParser<((Distinctness, Visibility), Functi }) } -fn attribute() -> impl NoirParser { - token_kind(TokenKind::Attribute).map(|token| match token { - Token::Attribute(attribute) => attribute, - _ => unreachable!("Parser should have already errored due to token not being an attribute"), - }) -} - -fn attributes() -> impl NoirParser> { - attribute().repeated() -} - -fn struct_fields() -> impl NoirParser> { - ident() - .then_ignore(just(Token::Colon)) - .then(parse_type()) - .separated_by(just(Token::Comma)) - .allow_trailing() -} - -fn lambda_parameters() -> impl NoirParser> { - let typ = parse_type().recover_via(parameter_recovery()); - let typ = just(Token::Colon).ignore_then(typ); - - let parameter = pattern() - .recover_via(parameter_name_recovery()) - .then(typ.or_not().map(|typ| typ.unwrap_or_else(UnresolvedType::unspecified))); - - parameter - .separated_by(just(Token::Comma)) - .allow_trailing() - .labelled(ParsingRuleLabel::Parameter) -} - -fn function_parameters<'a>(allow_self: bool) -> impl NoirParser> + 'a { - let typ = parse_type().recover_via(parameter_recovery()); - - let full_parameter = pattern() - .recover_via(parameter_name_recovery()) - .then_ignore(just(Token::Colon)) - .then(optional_visibility()) - .then(typ) - .map_with_span(|((pattern, visibility), typ), span| Param { - visibility, - pattern, - typ, - span, - }); - - let self_parameter = if allow_self { self_parameter().boxed() } else { nothing().boxed() }; - - let parameter = full_parameter.or(self_parameter); - - parameter - .separated_by(just(Token::Comma)) - .allow_trailing() - .labelled(ParsingRuleLabel::Parameter) -} - -/// This parser always parses no input and fails -fn nothing() -> impl NoirParser { - one_of([]).map(|_| unreachable!("parser should always error")) -} - fn self_parameter() -> impl NoirParser { let mut_ref_pattern = just(Token::Ampersand).then_ignore(keyword(Keyword::Mut)); let mut_pattern = keyword(Keyword::Mut); @@ -401,111 +253,6 @@ fn self_parameter() -> impl NoirParser { }) } -fn trait_definition() -> impl NoirParser { - keyword(Keyword::Trait) - .ignore_then(ident()) - .then(generics()) - .then(where_clause()) - .then_ignore(just(Token::LeftBrace)) - .then(trait_body()) - .then_ignore(just(Token::RightBrace)) - .map_with_span(|(((name, generics), where_clause), items), span| { - TopLevelStatement::Trait(NoirTrait { name, generics, where_clause, span, items }) - }) -} - -fn trait_body() -> impl NoirParser> { - trait_function_declaration() - .or(trait_type_declaration()) - .or(trait_constant_declaration()) - .repeated() -} - -fn optional_default_value() -> impl NoirParser> { - ignore_then_commit(just(Token::Assign), expression()).or_not() -} - -fn trait_constant_declaration() -> impl NoirParser { - keyword(Keyword::Let) - .ignore_then(ident()) - .then_ignore(just(Token::Colon)) - .then(parse_type()) - .then(optional_default_value()) - .then_ignore(just(Token::Semicolon)) - .validate(|((name, typ), default_value), span, emit| { - emit(ParserError::with_reason( - ParserErrorReason::ExperimentalFeature("Associated constants"), - span, - )); - TraitItem::Constant { name, typ, default_value } - }) -} - -/// trait_function_declaration: 'fn' ident generics '(' declaration_parameters ')' function_return_type -fn trait_function_declaration() -> impl NoirParser { - let trait_function_body_or_semicolon = - block(fresh_statement()).map(Option::from).or(just(Token::Semicolon).to(Option::None)); - - keyword(Keyword::Fn) - .ignore_then(ident()) - .then(generics()) - .then(parenthesized(function_declaration_parameters())) - .then(function_return_type().map(|(_, typ)| typ)) - .then(where_clause()) - .then(trait_function_body_or_semicolon) - .map(|(((((name, generics), parameters), return_type), where_clause), body)| { - TraitItem::Function { name, generics, parameters, return_type, where_clause, body } - }) -} - -fn validate_attributes( - attributes: Vec, - span: Span, - emit: &mut dyn FnMut(ParserError), -) -> Attributes { - let mut primary = None; - let mut secondary = Vec::new(); - - for attribute in attributes { - match attribute { - Attribute::Function(attr) => { - if primary.is_some() { - emit(ParserError::with_reason( - ParserErrorReason::MultipleFunctionAttributesFound, - span, - )); - } - primary = Some(attr); - } - Attribute::Secondary(attr) => secondary.push(attr), - } - } - - Attributes { function: primary, secondary } -} - -fn validate_struct_attributes( - attributes: Vec, - span: Span, - emit: &mut dyn FnMut(ParserError), -) -> Vec { - let mut struct_attributes = vec![]; - - for attribute in attributes { - match attribute { - Attribute::Function(..) => { - emit(ParserError::with_reason( - ParserErrorReason::NoFunctionAttributesAllowedOnStruct, - span, - )); - } - Attribute::Secondary(attr) => struct_attributes.push(attr), - } - } - - struct_attributes -} - /// Function declaration parameters differ from other parameters in that parameter /// patterns are not allowed in declarations. All parameters must be identifiers. fn function_declaration_parameters() -> impl NoirParser> { @@ -536,89 +283,6 @@ fn function_declaration_parameters() -> impl NoirParser impl NoirParser { - keyword(Keyword::Type).ignore_then(ident()).then_ignore(just(Token::Semicolon)).validate( - |name, span, emit| { - emit(ParserError::with_reason( - ParserErrorReason::ExperimentalFeature("Associated types"), - span, - )); - TraitItem::Type { name } - }, - ) -} - -/// Parses a non-trait implementation, adding a set of methods to a type. -/// -/// implementation: 'impl' generics type '{' function_definition ... '}' -fn implementation() -> impl NoirParser { - keyword(Keyword::Impl) - .ignore_then(generics()) - .then(parse_type().map_with_span(|typ, span| (typ, span))) - .then_ignore(just(Token::LeftBrace)) - .then(spanned(function_definition(true)).repeated()) - .then_ignore(just(Token::RightBrace)) - .map(|((generics, (object_type, type_span)), methods)| { - TopLevelStatement::Impl(TypeImpl { generics, object_type, type_span, methods }) - }) -} - -/// Parses a trait implementation, implementing a particular trait for a type. -/// This has a similar syntax to `implementation`, but the `for type` clause is required, -/// and an optional `where` clause is also useable. -/// -/// trait_implementation: 'impl' generics ident generic_args for type '{' trait_implementation_body '}' -fn trait_implementation() -> impl NoirParser { - keyword(Keyword::Impl) - .ignore_then(generics()) - .then(path()) - .then(generic_type_args(parse_type())) - .then_ignore(keyword(Keyword::For)) - .then(parse_type()) - .then(where_clause()) - .then_ignore(just(Token::LeftBrace)) - .then(trait_implementation_body()) - .then_ignore(just(Token::RightBrace)) - .map(|args| { - let ((other_args, where_clause), items) = args; - let (((impl_generics, trait_name), trait_generics), object_type) = other_args; - - TopLevelStatement::TraitImpl(NoirTraitImpl { - impl_generics, - trait_name, - trait_generics, - object_type, - items, - where_clause, - }) - }) -} - -fn trait_implementation_body() -> impl NoirParser> { - let function = function_definition(true).validate(|mut f, span, emit| { - if f.def().is_internal - || f.def().is_unconstrained - || f.def().is_open - || f.def().visibility != FunctionVisibility::Private - { - emit(ParserError::with_reason(ParserErrorReason::TraitImplFunctionModifiers, span)); - } - // Trait impl functions are always public - f.def_mut().visibility = FunctionVisibility::Public; - TraitImplItem::Function(f) - }); - - let alias = keyword(Keyword::Type) - .ignore_then(ident()) - .then_ignore(just(Token::Assign)) - .then(parse_type()) - .then_ignore(just(Token::Semicolon)) - .map(|(name, alias)| TraitImplItem::Type { name, alias }); - - function.or(alias).repeated() -} - fn where_clause() -> impl NoirParser> { struct MultiTraitConstraint { typ: UnresolvedType, @@ -713,45 +377,6 @@ fn use_statement() -> impl NoirParser { keyword(Keyword::Use).ignore_then(use_tree()).map(TopLevelStatement::Import) } -fn keyword(keyword: Keyword) -> impl NoirParser { - just(Token::Keyword(keyword)) -} - -fn token_kind(token_kind: TokenKind) -> impl NoirParser { - filter_map(move |span, found: Token| { - if found.kind() == token_kind { - Ok(found) - } else { - Err(ParserError::expected_label( - ParsingRuleLabel::TokenKind(token_kind.clone()), - found, - span, - )) - } - }) -} - -fn path() -> impl NoirParser { - let idents = || ident().separated_by(just(Token::DoubleColon)).at_least(1); - let make_path = |kind| move |segments, span| Path { segments, kind, span }; - - let prefix = |key| keyword(key).ignore_then(just(Token::DoubleColon)); - let path_kind = |key, kind| prefix(key).ignore_then(idents()).map_with_span(make_path(kind)); - - choice(( - path_kind(Keyword::Crate, PathKind::Crate), - path_kind(Keyword::Dep, PathKind::Dep), - idents().map_with_span(make_path(PathKind::Plain)), - )) -} - -fn empty_path() -> impl NoirParser { - let make_path = |kind| move |_, span| Path { segments: Vec::new(), kind, span }; - let path_kind = |key, kind| keyword(key).map_with_span(make_path(kind)); - - choice((path_kind(Keyword::Crate, PathKind::Crate), path_kind(Keyword::Dep, PathKind::Dep))) -} - fn rename() -> impl NoirParser> { ignore_then_commit(keyword(Keyword::As), ident()).or_not() } @@ -764,7 +389,7 @@ fn use_tree() -> impl NoirParser { }); let list = { - let prefix = path().or(empty_path()).then_ignore(just(Token::DoubleColon)); + let prefix = maybe_empty_path().then_ignore(just(Token::DoubleColon)); let tree = use_tree .separated_by(just(Token::Comma)) .allow_trailing() @@ -778,10 +403,6 @@ fn use_tree() -> impl NoirParser { }) } -fn ident() -> impl NoirParser { - token_kind(TokenKind::Ident).map_with_span(Ident::from_token) -} - fn statement<'a, P, P2>( expr_parser: P, expr_no_constructors: P2, @@ -792,9 +413,9 @@ where { recursive(|statement| { choice(( - constrain(expr_parser.clone()), - assertion(expr_parser.clone()), - assertion_eq(expr_parser.clone()), + assertion::constrain(expr_parser.clone()), + assertion::assertion(expr_parser.clone()), + assertion::assertion_eq(expr_parser.clone()), declaration(expr_parser.clone()), assignment(expr_parser.clone()), for_loop(expr_no_constructors, statement), @@ -808,64 +429,6 @@ fn fresh_statement() -> impl NoirParser { statement(expression(), expression_no_constructors(expression())) } -fn constrain<'a, P>(expr_parser: P) -> impl NoirParser + 'a -where - P: ExprParser + 'a, -{ - ignore_then_commit( - keyword(Keyword::Constrain).labelled(ParsingRuleLabel::Statement), - expr_parser, - ) - .map(|expr| StatementKind::Constrain(ConstrainStatement(expr, None, ConstrainKind::Constrain))) - .validate(|expr, span, emit| { - emit(ParserError::with_reason(ParserErrorReason::ConstrainDeprecated, span)); - expr - }) -} - -fn assertion<'a, P>(expr_parser: P) -> impl NoirParser + 'a -where - P: ExprParser + 'a, -{ - let argument_parser = - expr_parser.separated_by(just(Token::Comma)).allow_trailing().at_least(1).at_most(2); - - ignore_then_commit(keyword(Keyword::Assert), parenthesized(argument_parser)) - .labelled(ParsingRuleLabel::Statement) - .validate(|expressions, span, _| { - let condition = expressions.first().unwrap_or(&Expression::error(span)).clone(); - let message = expressions.get(1).cloned(); - StatementKind::Constrain(ConstrainStatement(condition, message, ConstrainKind::Assert)) - }) -} - -fn assertion_eq<'a, P>(expr_parser: P) -> impl NoirParser + 'a -where - P: ExprParser + 'a, -{ - let argument_parser = - expr_parser.separated_by(just(Token::Comma)).allow_trailing().at_least(2).at_most(3); - - ignore_then_commit(keyword(Keyword::AssertEq), parenthesized(argument_parser)) - .labelled(ParsingRuleLabel::Statement) - .validate(|exprs: Vec, span, _| { - let predicate = Expression::new( - ExpressionKind::Infix(Box::new(InfixExpression { - lhs: exprs.first().unwrap_or(&Expression::error(span)).clone(), - rhs: exprs.get(1).unwrap_or(&Expression::error(span)).clone(), - operator: Spanned::from(span, BinaryOpKind::Equal), - })), - span, - ); - let message = exprs.get(2).cloned(); - StatementKind::Constrain(ConstrainStatement( - predicate, - message, - ConstrainKind::AssertEq, - )) - }) -} - fn declaration<'a, P>(expr_parser: P) -> impl NoirParser + 'a where P: ExprParser + 'a, @@ -1308,13 +871,6 @@ fn create_infix_expression(lhs: Expression, (operator, rhs): (BinaryOp, Expressi Expression { span, kind: ExpressionKind::Infix(infix) } } -// Right-shift (>>) is issued as two separate > tokens by the lexer as this makes it easier -// to parse nested generic types. For normal expressions however, it means we have to manually -// parse two greater-than tokens as a single right-shift here. -fn right_shift_operator() -> impl NoirParser { - just(Token::Greater).then(just(Token::Greater)).to(Token::ShiftRight) -} - fn operator_with_precedence(precedence: Precedence) -> impl NoirParser> { right_shift_operator() .or(any()) // Parse any single token, we're validating it as an operator next @@ -1454,18 +1010,6 @@ where }) } -fn lambda<'a>( - expr_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - lambda_parameters() - .delimited_by(just(Token::Pipe), just(Token::Pipe)) - .then(lambda_return_type()) - .then(expr_parser) - .map(|((parameters, return_type), body)| { - ExpressionKind::Lambda(Box::new(Lambda { parameters, return_type, body })) - }) -} - fn for_loop<'a, P, S>(expr_no_constructors: P, statement: S) -> impl NoirParser + 'a where P: ExprParser + 'a, @@ -1530,41 +1074,6 @@ where expr_parser.separated_by(just(Token::Comma)).allow_trailing() } -fn not

(term_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - just(Token::Bang).ignore_then(term_parser).map(|rhs| ExpressionKind::prefix(UnaryOp::Not, rhs)) -} - -fn negation

(term_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - just(Token::Minus) - .ignore_then(term_parser) - .map(|rhs| ExpressionKind::prefix(UnaryOp::Minus, rhs)) -} - -fn mutable_reference

(term_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - just(Token::Ampersand) - .ignore_then(keyword(Keyword::Mut)) - .ignore_then(term_parser) - .map(|rhs| ExpressionKind::prefix(UnaryOp::MutableReference, rhs)) -} - -fn dereference

(term_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - just(Token::Star) - .ignore_then(term_parser) - .map(|rhs| ExpressionKind::prefix(UnaryOp::Dereference { implicitly_added: false }, rhs)) -} - /// Atoms are parameterized on whether constructor expressions are allowed or not. /// Certain constructs like `if` and `for` disallow constructor expressions when a /// block may be expected. @@ -1587,7 +1096,7 @@ where } else { nothing().boxed() }, - lambda(expr_parser.clone()), + lambdas::lambda(expr_parser.clone()), block(statement).map(ExpressionKind::Block), variable(), literal(), @@ -1655,167 +1164,12 @@ where long_form.or(short_form) } -fn variable() -> impl NoirParser { - path().map(ExpressionKind::Variable) -} - -fn literal() -> impl NoirParser { - token_kind(TokenKind::Literal).map(|token| match token { - Token::Int(x) => ExpressionKind::integer(x), - Token::Bool(b) => ExpressionKind::boolean(b), - Token::Str(s) => ExpressionKind::string(s), - Token::RawStr(s, hashes) => ExpressionKind::raw_string(s, hashes), - Token::FmtStr(s) => ExpressionKind::format_string(s), - unexpected => unreachable!("Non-literal {} parsed as a literal", unexpected), - }) -} - #[cfg(test)] mod test { - use noirc_errors::CustomDiagnostic; - + use super::test_helpers::*; use super::*; use crate::{ArrayLiteral, Literal}; - fn parse_with(parser: P, program: &str) -> Result> - where - P: NoirParser, - { - let (tokens, lexer_errors) = Lexer::lex(program); - if !lexer_errors.is_empty() { - return Err(vecmap(lexer_errors, Into::into)); - } - parser - .then_ignore(just(Token::EOF)) - .parse(tokens) - .map_err(|errors| vecmap(errors, Into::into)) - } - - fn parse_recover(parser: P, program: &str) -> (Option, Vec) - where - P: NoirParser, - { - let (tokens, lexer_errors) = Lexer::lex(program); - let (opt, errs) = parser.then_ignore(force(just(Token::EOF))).parse_recovery(tokens); - - let mut errors = vecmap(lexer_errors, Into::into); - errors.extend(errs.into_iter().map(Into::into)); - - (opt, errors) - } - - fn parse_all(parser: P, programs: Vec<&str>) -> Vec - where - P: NoirParser, - { - vecmap(programs, move |program| { - let message = format!("Failed to parse:\n{program}"); - let (op_t, diagnostics) = parse_recover(&parser, program); - diagnostics.iter().for_each(|diagnostic| { - if diagnostic.is_error() { - panic!("{} with error {}", &message, diagnostic); - } - }); - op_t.expect(&message) - }) - } - - fn parse_all_failing(parser: P, programs: Vec<&str>) -> Vec - where - P: NoirParser, - T: std::fmt::Display, - { - programs - .into_iter() - .flat_map(|program| match parse_with(&parser, program) { - Ok(expr) => { - unreachable!( - "Expected this input to fail:\n{}\nYet it successfully parsed as:\n{}", - program, expr - ) - } - Err(diagnostics) => { - if diagnostics.iter().all(|diagnostic: &CustomDiagnostic| diagnostic.is_warning()) { - unreachable!( - "Expected at least one error when parsing:\n{}\nYet it successfully parsed without errors:\n", - program - ) - }; - diagnostics - } - }) - .collect() - } - - #[derive(Copy, Clone)] - struct Case { - source: &'static str, - errors: usize, - expect: &'static str, - } - - fn check_cases_with_errors(cases: &[Case], parser: P) - where - P: NoirParser + Clone, - T: std::fmt::Display, - { - let show_errors = |v| vecmap(&v, ToString::to_string).join("\n"); - - let results = vecmap(cases, |&case| { - let (opt, errors) = parse_recover(parser.clone(), case.source); - let actual = opt.map(|ast| ast.to_string()); - let actual = if let Some(s) = &actual { s.to_string() } else { "(none)".to_string() }; - - let result = ((errors.len(), actual.clone()), (case.errors, case.expect.to_string())); - if result.0 != result.1 { - let num_errors = errors.len(); - let shown_errors = show_errors(errors); - eprintln!( - concat!( - "\nExpected {expected_errors} error(s) and got {num_errors}:", - "\n\n{shown_errors}", - "\n\nFrom input: {src}", - "\nExpected AST: {expected_result}", - "\nActual AST: {actual}\n", - ), - expected_errors = case.errors, - num_errors = num_errors, - shown_errors = shown_errors, - src = case.source, - expected_result = case.expect, - actual = actual, - ); - } - result - }); - - assert_eq!(vecmap(&results, |t| t.0.clone()), vecmap(&results, |t| t.1.clone()),); - } - - #[test] - fn regression_skip_comment() { - parse_all( - function_definition(false), - vec![ - "fn main( - // This comment should be skipped - x : Field, - // And this one - y : Field, - ) { - }", - "fn main(x : Field, y : Field,) { - foo::bar( - // Comment for x argument - x, - // Comment for y argument - y - ) - }", - ], - ); - } - #[test] fn parse_infix() { let valid = vec!["x + 6", "x - k", "x + (x + a)", " x * (x + a) + (x - 4)"]; @@ -1966,142 +1320,6 @@ mod test { } } - /// Deprecated constrain usage test - #[test] - fn parse_constrain() { - let errors = parse_with(constrain(expression()), "constrain x == y").unwrap_err(); - assert_eq!(errors.len(), 1); - assert!(format!("{}", errors.first().unwrap()).contains("deprecated")); - - // Currently we disallow constrain statements where the outer infix operator - // produces a value. This would require an implicit `==` which - // may not be intuitive to the user. - // - // If this is deemed useful, one would either apply a transformation - // or interpret it with an `==` in the evaluator - let disallowed_operators = vec![ - BinaryOpKind::And, - BinaryOpKind::Subtract, - BinaryOpKind::Divide, - BinaryOpKind::Multiply, - BinaryOpKind::Or, - ]; - - for operator in disallowed_operators { - let src = format!("constrain x {} y;", operator.as_string()); - let errors = parse_with(constrain(expression()), &src).unwrap_err(); - assert_eq!(errors.len(), 2); - assert!(format!("{}", errors.first().unwrap()).contains("deprecated")); - } - - // These are general cases which should always work. - // - // The first case is the most noteworthy. It contains two `==` - // The first (inner) `==` is a predicate which returns 0/1 - // The outer layer is an infix `==` which is - // associated with the Constrain statement - let errors = parse_all_failing( - constrain(expression()), - vec![ - "constrain ((x + y) == k) + z == y", - "constrain (x + !y) == y", - "constrain (x ^ y) == y", - "constrain (x ^ y) == (y + m)", - "constrain x + x ^ x == y | m", - ], - ); - assert_eq!(errors.len(), 5); - assert!(errors - .iter() - .all(|err| { err.is_error() && err.to_string().contains("deprecated") })); - } - - /// This is the standard way to declare an assert statement - #[test] - fn parse_assert() { - parse_with(assertion(expression()), "assert(x == y)").unwrap(); - - // Currently we disallow constrain statements where the outer infix operator - // produces a value. This would require an implicit `==` which - // may not be intuitive to the user. - // - // If this is deemed useful, one would either apply a transformation - // or interpret it with an `==` in the evaluator - let disallowed_operators = vec![ - BinaryOpKind::And, - BinaryOpKind::Subtract, - BinaryOpKind::Divide, - BinaryOpKind::Multiply, - BinaryOpKind::Or, - ]; - - for operator in disallowed_operators { - let src = format!("assert(x {} y);", operator.as_string()); - parse_with(assertion(expression()), &src).unwrap_err(); - } - - // These are general cases which should always work. - // - // The first case is the most noteworthy. It contains two `==` - // The first (inner) `==` is a predicate which returns 0/1 - // The outer layer is an infix `==` which is - // associated with the Constrain statement - parse_all( - assertion(expression()), - vec![ - "assert(((x + y) == k) + z == y)", - "assert((x + !y) == y)", - "assert((x ^ y) == y)", - "assert((x ^ y) == (y + m))", - "assert(x + x ^ x == y | m)", - ], - ); - - match parse_with(assertion(expression()), "assert(x == y, \"assertion message\")").unwrap() - { - StatementKind::Constrain(ConstrainStatement(_, message, _)) => { - let message = message.unwrap(); - match message.kind { - ExpressionKind::Literal(Literal::Str(message_string)) => { - assert_eq!(message_string, "assertion message".to_owned()); - } - _ => unreachable!(), - } - } - _ => unreachable!(), - } - } - - /// This is the standard way to assert that two expressions are equivalent - #[test] - fn parse_assert_eq() { - parse_all( - assertion_eq(expression()), - vec![ - "assert_eq(x, y)", - "assert_eq(((x + y) == k) + z, y)", - "assert_eq(x + !y, y)", - "assert_eq(x ^ y, y)", - "assert_eq(x ^ y, y + m)", - "assert_eq(x + x ^ x, y | m)", - ], - ); - match parse_with(assertion_eq(expression()), "assert_eq(x, y, \"assertion message\")") - .unwrap() - { - StatementKind::Constrain(ConstrainStatement(_, message, _)) => { - let message = message.unwrap(); - match message.kind { - ExpressionKind::Literal(Literal::Str(message_string)) => { - assert_eq!(message_string, "assertion message".to_owned()); - } - _ => unreachable!(), - } - } - _ => unreachable!(), - } - } - #[test] fn parse_let() { // Why is it valid to specify a let declaration as having type u8? @@ -2135,84 +1353,6 @@ mod test { ); } - #[test] - fn parse_function() { - parse_all( - function_definition(false), - vec![ - "fn func_name() {}", - "fn f(foo: pub u8, y : pub Field) -> u8 { x + a }", - "fn f(f: pub Field, y : Field, z : Field) -> u8 { x + a }", - "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) {}", - "fn f(f: pub Field, y : Field, z : Field) -> u8 { x + a }", - "fn f(f: pub Field, y : T, z : Field) -> u8 { x + a }", - "fn func_name(x: [Field], y : [Field;2],y : pub [Field;2], z : pub [u8;5]) {}", - "fn main(x: pub u8, y: pub u8) -> distinct pub [u8; 2] { [x, y] }", - "fn f(f: pub Field, y : Field, z : comptime Field) -> u8 { x + a }", - "fn f(f: pub Field, y : T, z : comptime Field) -> u8 { x + a }", - "fn func_name(f: Field, y : T) where T: SomeTrait {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait, T: SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 + TraitY {}", - "fn func_name(f: Field, y : T, z : U) where SomeStruct: SomeTrait {}", - // 'where u32: SomeTrait' is allowed in Rust. - // It will result in compiler error in case SomeTrait isn't implemented for u32. - "fn func_name(f: Field, y : T) where u32: SomeTrait {}", - // A trailing plus is allowed by Rust, so we support it as well. - "fn func_name(f: Field, y : T) where T: SomeTrait + {}", - // The following should produce compile error on later stage. From the parser's perspective it's fine - "fn func_name(f: Field, y : Field, z : Field) where T: SomeTrait {}", - ], - ); - - parse_all_failing( - function_definition(false), - vec![ - "fn x2( f: []Field,,) {}", - "fn ( f: []Field) {}", - "fn ( f: []Field) {}", - // TODO: Check for more specific error messages - "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) where T: {}", - "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) where SomeTrait {}", - "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) SomeTrait {}", - // A leading plus is not allowed. - "fn func_name(f: Field, y : T) where T: + SomeTrait {}", - "fn func_name(f: Field, y : T) where T: TraitX + {}", - ], - ); - } - - #[test] - fn parse_trait() { - parse_all( - trait_definition(), - vec![ - // Empty traits are legal in Rust and sometimes used as a way to whitelist certain types - // for a particular operation. Also known as `tag` or `marker` traits: - // https://stackoverflow.com/questions/71895489/what-is-the-purpose-of-defining-empty-impl-in-rust - "trait Empty {}", - "trait TraitWithDefaultBody { fn foo(self) {} }", - "trait TraitAcceptingMutableRef { fn foo(&mut self); }", - "trait TraitWithTypeBoundOperation { fn identity() -> Self; }", - "trait TraitWithAssociatedType { type Element; fn item(self, index: Field) -> Self::Element; }", - "trait TraitWithAssociatedConstant { let Size: Field; }", - "trait TraitWithAssociatedConstantWithDefaultValue { let Size: Field = 10; }", - "trait GenericTrait { fn elem(&mut self, index: Field) -> T; }", - "trait GenericTraitWithConstraints where T: SomeTrait { fn elem(self, index: Field) -> T; }", - "trait TraitWithMultipleGenericParams where A: SomeTrait, B: AnotherTrait { let Size: Field; fn zero() -> Self; }", - ], - ); - - parse_all_failing( - trait_definition(), - vec!["trait MissingBody", "trait WrongDelimiter { fn foo() -> u8, fn bar() -> u8 }"], - ); - } - #[test] fn parse_parenthesized_expression() { parse_all( @@ -2243,104 +1383,12 @@ mod test { ); } - fn expr_to_lit(expr: ExpressionKind) -> Literal { - match expr { - ExpressionKind::Literal(literal) => literal, - _ => unreachable!("expected a literal"), - } - } - - #[test] - fn parse_int() { - let int = parse_with(literal(), "5").unwrap(); - let hex = parse_with(literal(), "0x05").unwrap(); - - match (expr_to_lit(int), expr_to_lit(hex)) { - (Literal::Integer(int, false), Literal::Integer(hex, false)) => assert_eq!(int, hex), - _ => unreachable!(), - } - } - - #[test] - fn parse_string() { - let expr = parse_with(literal(), r#""hello""#).unwrap(); - match expr_to_lit(expr) { - Literal::Str(s) => assert_eq!(s, "hello"), - _ => unreachable!(), - }; - } - - #[test] - fn parse_bool() { - let expr_true = parse_with(literal(), "true").unwrap(); - let expr_false = parse_with(literal(), "false").unwrap(); - - match (expr_to_lit(expr_true), expr_to_lit(expr_false)) { - (Literal::Bool(t), Literal::Bool(f)) => { - assert!(t); - assert!(!f); - } - _ => unreachable!(), - }; - } - #[test] fn parse_module_declaration() { parse_with(module_declaration(), "mod foo").unwrap(); parse_with(module_declaration(), "mod 1").unwrap_err(); } - #[test] - fn parse_path() { - let cases = vec![ - ("std", vec!["std"]), - ("std::hash", vec!["std", "hash"]), - ("std::hash::collections", vec!["std", "hash", "collections"]), - ("dep::foo::bar", vec!["foo", "bar"]), - ("crate::std::hash", vec!["std", "hash"]), - ]; - - for (src, expected_segments) in cases { - let path: Path = parse_with(path(), src).unwrap(); - for (segment, expected) in path.segments.into_iter().zip(expected_segments) { - assert_eq!(segment.0.contents, expected); - } - } - - parse_all_failing(path(), vec!["std::", "::std", "std::hash::", "foo::1"]); - } - - #[test] - fn parse_path_kinds() { - let cases = vec![ - ("std", PathKind::Plain), - ("dep::hash::collections", PathKind::Dep), - ("crate::std::hash", PathKind::Crate), - ]; - - for (src, expected_path_kind) in cases { - let path = parse_with(path(), src).unwrap(); - assert_eq!(path.kind, expected_path_kind); - } - - parse_all_failing( - path(), - vec!["dep", "crate", "crate::std::crate", "foo::bar::crate", "foo::dep"], - ); - } - - #[test] - fn parse_unary() { - parse_all( - term(expression(), expression_no_constructors(expression()), fresh_statement(), true), - vec!["!hello", "-hello", "--hello", "-!hello", "!-hello"], - ); - parse_all_failing( - term(expression(), expression_no_constructors(expression()), fresh_statement(), true), - vec!["+hello", "/hello"], - ); - } - #[test] fn parse_use() { parse_all( @@ -2372,26 +1420,6 @@ mod test { ); } - #[test] - fn parse_structs() { - let cases = vec![ - "struct Foo;", - "struct Foo { }", - "struct Bar { ident: Field, }", - "struct Baz { ident: Field, other: Field }", - "#[attribute] struct Baz { ident: Field, other: Field }", - ]; - parse_all(struct_definition(), cases); - - let failing = vec![ - "struct { }", - "struct Foo { bar: pub Field }", - "struct Foo { bar: pub Field }", - "#[oracle(some)] struct Foo { bar: Field }", - ]; - parse_all_failing(struct_definition(), failing); - } - #[test] fn parse_type_aliases() { let cases = vec!["type foo = u8", "type bar = String", "type baz = Vec"]; @@ -2563,79 +1591,4 @@ mod test { check_cases_with_errors(&cases[..], block(fresh_statement())); } - - #[test] - fn parse_raw_string_expr() { - let cases = vec![ - Case { source: r#" r"foo" "#, expect: r#"r"foo""#, errors: 0 }, - Case { source: r##" r#"foo"# "##, expect: r##"r#"foo"#"##, errors: 0 }, - // backslash - Case { source: r#" r"\\" "#, expect: r#"r"\\""#, errors: 0 }, - Case { source: r##" r#"\"# "##, expect: r##"r#"\"#"##, errors: 0 }, - Case { source: r##" r#"\\"# "##, expect: r##"r#"\\"#"##, errors: 0 }, - Case { source: r##" r#"\\\"# "##, expect: r##"r#"\\\"#"##, errors: 0 }, - // escape sequence - Case { - source: r##" r#"\t\n\\t\\n\\\t\\\n\\\\"# "##, - expect: r##"r#"\t\n\\t\\n\\\t\\\n\\\\"#"##, - errors: 0, - }, - Case { source: r##" r#"\\\\\\\\"# "##, expect: r##"r#"\\\\\\\\"#"##, errors: 0 }, - // mismatch - errors: - Case { source: r###" r#"foo"## "###, expect: r##"r#"foo"#"##, errors: 1 }, - Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 }, - // mismatch: short: - Case { source: r##" r"foo"# "##, expect: r#"r"foo""#, errors: 1 }, - Case { source: r#" r#"foo" "#, expect: "(none)", errors: 2 }, - // empty string - Case { source: r#"r"""#, expect: r#"r"""#, errors: 0 }, - Case { source: r####"r###""###"####, expect: r####"r###""###"####, errors: 0 }, - // miscellaneous - Case { source: r##" r#\"foo\"# "##, expect: "plain::r", errors: 2 }, - Case { source: r#" r\"foo\" "#, expect: "plain::r", errors: 1 }, - Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 }, - // missing 'r' letter - Case { source: r##" ##"foo"# "##, expect: r#""foo""#, errors: 2 }, - Case { source: r#" #"foo" "#, expect: "plain::foo", errors: 2 }, - // whitespace - Case { source: r##" r #"foo"# "##, expect: "plain::r", errors: 2 }, - Case { source: r##" r# "foo"# "##, expect: "plain::r", errors: 3 }, - Case { source: r#" r#"foo" # "#, expect: "(none)", errors: 2 }, - // after identifier - Case { source: r##" bar#"foo"# "##, expect: "plain::bar", errors: 2 }, - // nested - Case { - source: r###"r##"foo r#"bar"# r"baz" ### bye"##"###, - expect: r###"r##"foo r#"bar"# r"baz" ### bye"##"###, - errors: 0, - }, - ]; - - check_cases_with_errors(&cases[..], expression()); - } - - #[test] - fn parse_raw_string_lit() { - let lit_cases = vec![ - Case { source: r#" r"foo" "#, expect: r#"r"foo""#, errors: 0 }, - Case { source: r##" r#"foo"# "##, expect: r##"r#"foo"#"##, errors: 0 }, - // backslash - Case { source: r#" r"\\" "#, expect: r#"r"\\""#, errors: 0 }, - Case { source: r##" r#"\"# "##, expect: r##"r#"\"#"##, errors: 0 }, - Case { source: r##" r#"\\"# "##, expect: r##"r#"\\"#"##, errors: 0 }, - Case { source: r##" r#"\\\"# "##, expect: r##"r#"\\\"#"##, errors: 0 }, - // escape sequence - Case { - source: r##" r#"\t\n\\t\\n\\\t\\\n\\\\"# "##, - expect: r##"r#"\t\n\\t\\n\\\t\\\n\\\\"#"##, - errors: 0, - }, - Case { source: r##" r#"\\\\\\\\"# "##, expect: r##"r#"\\\\\\\\"#"##, errors: 0 }, - // mismatch - errors: - Case { source: r###" r#"foo"## "###, expect: r##"r#"foo"#"##, errors: 1 }, - Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 }, - ]; - - check_cases_with_errors(&lit_cases[..], literal()); - } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/assertion.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/assertion.rs new file mode 100644 index 00000000000..f9c8d7aa46b --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/assertion.rs @@ -0,0 +1,219 @@ +use crate::ast::{Expression, ExpressionKind, StatementKind}; +use crate::parser::{ + ignore_then_commit, labels::ParsingRuleLabel, parenthesized, ExprParser, NoirParser, + ParserError, ParserErrorReason, +}; + +use crate::token::{Keyword, Token}; +use crate::{BinaryOpKind, ConstrainKind, ConstrainStatement, InfixExpression, Recoverable}; + +use chumsky::prelude::*; +use noirc_errors::Spanned; + +use super::keyword; + +pub(super) fn constrain<'a, P>(expr_parser: P) -> impl NoirParser + 'a +where + P: ExprParser + 'a, +{ + ignore_then_commit( + keyword(Keyword::Constrain).labelled(ParsingRuleLabel::Statement), + expr_parser, + ) + .map(|expr| StatementKind::Constrain(ConstrainStatement(expr, None, ConstrainKind::Constrain))) + .validate(|expr, span, emit| { + emit(ParserError::with_reason(ParserErrorReason::ConstrainDeprecated, span)); + expr + }) +} + +pub(super) fn assertion<'a, P>(expr_parser: P) -> impl NoirParser + 'a +where + P: ExprParser + 'a, +{ + let argument_parser = + expr_parser.separated_by(just(Token::Comma)).allow_trailing().at_least(1).at_most(2); + + ignore_then_commit(keyword(Keyword::Assert), parenthesized(argument_parser)) + .labelled(ParsingRuleLabel::Statement) + .validate(|expressions, span, _| { + let condition = expressions.first().unwrap_or(&Expression::error(span)).clone(); + let message = expressions.get(1).cloned(); + StatementKind::Constrain(ConstrainStatement(condition, message, ConstrainKind::Assert)) + }) +} + +pub(super) fn assertion_eq<'a, P>(expr_parser: P) -> impl NoirParser + 'a +where + P: ExprParser + 'a, +{ + let argument_parser = + expr_parser.separated_by(just(Token::Comma)).allow_trailing().at_least(2).at_most(3); + + ignore_then_commit(keyword(Keyword::AssertEq), parenthesized(argument_parser)) + .labelled(ParsingRuleLabel::Statement) + .validate(|exprs: Vec, span, _| { + let predicate = Expression::new( + ExpressionKind::Infix(Box::new(InfixExpression { + lhs: exprs.first().unwrap_or(&Expression::error(span)).clone(), + rhs: exprs.get(1).unwrap_or(&Expression::error(span)).clone(), + operator: Spanned::from(span, BinaryOpKind::Equal), + })), + span, + ); + let message = exprs.get(2).cloned(); + StatementKind::Constrain(ConstrainStatement( + predicate, + message, + ConstrainKind::AssertEq, + )) + }) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + parser::parser::{ + expression, + test_helpers::{parse_all, parse_all_failing, parse_with}, + }, + Literal, + }; + + /// Deprecated constrain usage test + #[test] + fn parse_constrain() { + let errors = parse_with(constrain(expression()), "constrain x == y").unwrap_err(); + assert_eq!(errors.len(), 1); + assert!(format!("{}", errors.first().unwrap()).contains("deprecated")); + + // Currently we disallow constrain statements where the outer infix operator + // produces a value. This would require an implicit `==` which + // may not be intuitive to the user. + // + // If this is deemed useful, one would either apply a transformation + // or interpret it with an `==` in the evaluator + let disallowed_operators = vec![ + BinaryOpKind::And, + BinaryOpKind::Subtract, + BinaryOpKind::Divide, + BinaryOpKind::Multiply, + BinaryOpKind::Or, + ]; + + for operator in disallowed_operators { + let src = format!("constrain x {} y;", operator.as_string()); + let errors = parse_with(constrain(expression()), &src).unwrap_err(); + assert_eq!(errors.len(), 2); + assert!(format!("{}", errors.first().unwrap()).contains("deprecated")); + } + + // These are general cases which should always work. + // + // The first case is the most noteworthy. It contains two `==` + // The first (inner) `==` is a predicate which returns 0/1 + // The outer layer is an infix `==` which is + // associated with the Constrain statement + let errors = parse_all_failing( + constrain(expression()), + vec![ + "constrain ((x + y) == k) + z == y", + "constrain (x + !y) == y", + "constrain (x ^ y) == y", + "constrain (x ^ y) == (y + m)", + "constrain x + x ^ x == y | m", + ], + ); + assert_eq!(errors.len(), 5); + assert!(errors + .iter() + .all(|err| { err.is_error() && err.to_string().contains("deprecated") })); + } + + /// This is the standard way to declare an assert statement + #[test] + fn parse_assert() { + parse_with(assertion(expression()), "assert(x == y)").unwrap(); + + // Currently we disallow constrain statements where the outer infix operator + // produces a value. This would require an implicit `==` which + // may not be intuitive to the user. + // + // If this is deemed useful, one would either apply a transformation + // or interpret it with an `==` in the evaluator + let disallowed_operators = vec![ + BinaryOpKind::And, + BinaryOpKind::Subtract, + BinaryOpKind::Divide, + BinaryOpKind::Multiply, + BinaryOpKind::Or, + ]; + + for operator in disallowed_operators { + let src = format!("assert(x {} y);", operator.as_string()); + parse_with(assertion(expression()), &src).unwrap_err(); + } + + // These are general cases which should always work. + // + // The first case is the most noteworthy. It contains two `==` + // The first (inner) `==` is a predicate which returns 0/1 + // The outer layer is an infix `==` which is + // associated with the Constrain statement + parse_all( + assertion(expression()), + vec![ + "assert(((x + y) == k) + z == y)", + "assert((x + !y) == y)", + "assert((x ^ y) == y)", + "assert((x ^ y) == (y + m))", + "assert(x + x ^ x == y | m)", + ], + ); + + match parse_with(assertion(expression()), "assert(x == y, \"assertion message\")").unwrap() + { + StatementKind::Constrain(ConstrainStatement(_, message, _)) => { + let message = message.unwrap(); + match message.kind { + ExpressionKind::Literal(Literal::Str(message_string)) => { + assert_eq!(message_string, "assertion message".to_owned()); + } + _ => unreachable!(), + } + } + _ => unreachable!(), + } + } + + /// This is the standard way to assert that two expressions are equivalent + #[test] + fn parse_assert_eq() { + parse_all( + assertion_eq(expression()), + vec![ + "assert_eq(x, y)", + "assert_eq(((x + y) == k) + z, y)", + "assert_eq(x + !y, y)", + "assert_eq(x ^ y, y)", + "assert_eq(x ^ y, y + m)", + "assert_eq(x + x ^ x, y | m)", + ], + ); + match parse_with(assertion_eq(expression()), "assert_eq(x, y, \"assertion message\")") + .unwrap() + { + StatementKind::Constrain(ConstrainStatement(_, message, _)) => { + let message = message.unwrap(); + match message.kind { + ExpressionKind::Literal(Literal::Str(message_string)) => { + assert_eq!(message_string, "assertion message".to_owned()); + } + _ => unreachable!(), + } + } + _ => unreachable!(), + } + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/attributes.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/attributes.rs new file mode 100644 index 00000000000..4b256a95c8b --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -0,0 +1,46 @@ +use chumsky::Parser; +use noirc_errors::Span; + +use crate::{ + parser::{NoirParser, ParserError, ParserErrorReason}, + token::{Attribute, Attributes, Token, TokenKind}, +}; + +use super::primitives::token_kind; + +fn attribute() -> impl NoirParser { + token_kind(TokenKind::Attribute).map(|token| match token { + Token::Attribute(attribute) => attribute, + _ => unreachable!("Parser should have already errored due to token not being an attribute"), + }) +} + +pub(super) fn attributes() -> impl NoirParser> { + attribute().repeated() +} + +pub(super) fn validate_attributes( + attributes: Vec, + span: Span, + emit: &mut dyn FnMut(ParserError), +) -> Attributes { + let mut primary = None; + let mut secondary = Vec::new(); + + for attribute in attributes { + match attribute { + Attribute::Function(attr) => { + if primary.is_some() { + emit(ParserError::with_reason( + ParserErrorReason::MultipleFunctionAttributesFound, + span, + )); + } + primary = Some(attr); + } + Attribute::Secondary(attr) => secondary.push(attr), + } + } + + Attributes { function: primary, secondary } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs new file mode 100644 index 00000000000..7448d84cfe1 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs @@ -0,0 +1,217 @@ +use super::{ + attributes::{attributes, validate_attributes}, + block, fresh_statement, ident, keyword, nothing, optional_distinctness, optional_visibility, + parameter_name_recovery, parameter_recovery, parenthesized, parse_type, pattern, + self_parameter, where_clause, NoirParser, +}; +use crate::parser::labels::ParsingRuleLabel; +use crate::parser::spanned; +use crate::token::{Keyword, Token}; +use crate::{ + Distinctness, FunctionDefinition, FunctionReturnType, FunctionVisibility, Ident, NoirFunction, + Param, Visibility, +}; + +use chumsky::prelude::*; + +/// function_definition: attribute function_modifiers 'fn' ident generics '(' function_parameters ')' function_return_type block +/// function_modifiers 'fn' ident generics '(' function_parameters ')' function_return_type block +pub(super) fn function_definition(allow_self: bool) -> impl NoirParser { + attributes() + .then(function_modifiers()) + .then_ignore(keyword(Keyword::Fn)) + .then(ident()) + .then(generics()) + .then(parenthesized(function_parameters(allow_self))) + .then(function_return_type()) + .then(where_clause()) + .then(spanned(block(fresh_statement()))) + .validate(|(((args, ret), where_clause), (body, body_span)), span, emit| { + let ((((attributes, modifiers), name), generics), parameters) = args; + + // Validate collected attributes, filtering them into function and secondary variants + let attributes = validate_attributes(attributes, span, emit); + FunctionDefinition { + span: body_span, + name, + attributes, + is_unconstrained: modifiers.0, + is_open: modifiers.2, + // Whether a function is internal or not is now set through `aztec_macros` + is_internal: false, + visibility: modifiers.1, + generics, + parameters, + body, + where_clause, + return_type: ret.1, + return_visibility: ret.0 .1, + return_distinctness: ret.0 .0, + } + .into() + }) +} + +/// visibility_modifier: 'pub(crate)'? 'pub'? '' +fn visibility_modifier() -> impl NoirParser { + let is_pub_crate = (keyword(Keyword::Pub) + .then_ignore(just(Token::LeftParen)) + .then_ignore(keyword(Keyword::Crate)) + .then_ignore(just(Token::RightParen))) + .map(|_| FunctionVisibility::PublicCrate); + + let is_pub = keyword(Keyword::Pub).map(|_| FunctionVisibility::Public); + + let is_private = empty().map(|_| FunctionVisibility::Private); + + choice((is_pub_crate, is_pub, is_private)) +} + +/// function_modifiers: 'unconstrained'? (visibility)? 'open'? +/// +/// returns (is_unconstrained, visibility, is_open) for whether each keyword was present +fn function_modifiers() -> impl NoirParser<(bool, FunctionVisibility, bool)> { + keyword(Keyword::Unconstrained) + .or_not() + .then(visibility_modifier()) + .then(keyword(Keyword::Open).or_not()) + .map(|((unconstrained, visibility), open)| { + (unconstrained.is_some(), visibility, open.is_some()) + }) +} + +/// non_empty_ident_list: ident ',' non_empty_ident_list +/// | ident +/// +/// generics: '<' non_empty_ident_list '>' +/// | %empty +pub(super) fn generics() -> impl NoirParser> { + ident() + .separated_by(just(Token::Comma)) + .allow_trailing() + .at_least(1) + .delimited_by(just(Token::Less), just(Token::Greater)) + .or_not() + .map(|opt| opt.unwrap_or_default()) +} + +fn function_return_type() -> impl NoirParser<((Distinctness, Visibility), FunctionReturnType)> { + just(Token::Arrow) + .ignore_then(optional_distinctness()) + .then(optional_visibility()) + .then(spanned(parse_type())) + .or_not() + .map_with_span(|ret, span| match ret { + Some((head, (ty, _))) => (head, FunctionReturnType::Ty(ty)), + None => ( + (Distinctness::DuplicationAllowed, Visibility::Private), + FunctionReturnType::Default(span), + ), + }) +} + +fn function_parameters<'a>(allow_self: bool) -> impl NoirParser> + 'a { + let typ = parse_type().recover_via(parameter_recovery()); + + let full_parameter = pattern() + .recover_via(parameter_name_recovery()) + .then_ignore(just(Token::Colon)) + .then(optional_visibility()) + .then(typ) + .map_with_span(|((pattern, visibility), typ), span| Param { + visibility, + pattern, + typ, + span, + }); + + let self_parameter = if allow_self { self_parameter().boxed() } else { nothing().boxed() }; + + let parameter = full_parameter.or(self_parameter); + + parameter + .separated_by(just(Token::Comma)) + .allow_trailing() + .labelled(ParsingRuleLabel::Parameter) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::parser::parser::test_helpers::*; + + #[test] + fn regression_skip_comment() { + parse_all( + function_definition(false), + vec![ + "fn main( + // This comment should be skipped + x : Field, + // And this one + y : Field, + ) { + }", + "fn main(x : Field, y : Field,) { + foo::bar( + // Comment for x argument + x, + // Comment for y argument + y + ) + }", + ], + ); + } + + #[test] + fn parse_function() { + parse_all( + function_definition(false), + vec![ + "fn func_name() {}", + "fn f(foo: pub u8, y : pub Field) -> u8 { x + a }", + "fn f(f: pub Field, y : Field, z : Field) -> u8 { x + a }", + "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) {}", + "fn f(f: pub Field, y : Field, z : Field) -> u8 { x + a }", + "fn f(f: pub Field, y : T, z : Field) -> u8 { x + a }", + "fn func_name(x: [Field], y : [Field;2],y : pub [Field;2], z : pub [u8;5]) {}", + "fn main(x: pub u8, y: pub u8) -> distinct pub [u8; 2] { [x, y] }", + "fn f(f: pub Field, y : Field, z : comptime Field) -> u8 { x + a }", + "fn f(f: pub Field, y : T, z : comptime Field) -> u8 { x + a }", + "fn func_name(f: Field, y : T) where T: SomeTrait {}", + "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", + "fn func_name(f: Field, y : T) where T: SomeTrait, T: SomeTrait2 {}", + "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", + "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", + "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", + "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", + "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 + TraitY {}", + "fn func_name(f: Field, y : T, z : U) where SomeStruct: SomeTrait {}", + // 'where u32: SomeTrait' is allowed in Rust. + // It will result in compiler error in case SomeTrait isn't implemented for u32. + "fn func_name(f: Field, y : T) where u32: SomeTrait {}", + // A trailing plus is allowed by Rust, so we support it as well. + "fn func_name(f: Field, y : T) where T: SomeTrait + {}", + // The following should produce compile error on later stage. From the parser's perspective it's fine + "fn func_name(f: Field, y : Field, z : Field) where T: SomeTrait {}", + ], + ); + + parse_all_failing( + function_definition(false), + vec![ + "fn x2( f: []Field,,) {}", + "fn ( f: []Field) {}", + "fn ( f: []Field) {}", + // TODO: Check for more specific error messages + "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) where T: {}", + "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) where SomeTrait {}", + "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) SomeTrait {}", + // A leading plus is not allowed. + "fn func_name(f: Field, y : T) where T: + SomeTrait {}", + "fn func_name(f: Field, y : T) where T: TraitX + {}", + ], + ); + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambdas.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambdas.rs new file mode 100644 index 00000000000..48ddd41ab44 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambdas.rs @@ -0,0 +1,42 @@ +use chumsky::{primitive::just, Parser}; + +use crate::{ + parser::{labels::ParsingRuleLabel, parameter_name_recovery, parameter_recovery, NoirParser}, + token::Token, + Expression, ExpressionKind, Lambda, Pattern, UnresolvedType, +}; + +use super::{parse_type, pattern}; + +pub(super) fn lambda<'a>( + expr_parser: impl NoirParser + 'a, +) -> impl NoirParser + 'a { + lambda_parameters() + .delimited_by(just(Token::Pipe), just(Token::Pipe)) + .then(lambda_return_type()) + .then(expr_parser) + .map(|((parameters, return_type), body)| { + ExpressionKind::Lambda(Box::new(Lambda { parameters, return_type, body })) + }) +} + +fn lambda_parameters() -> impl NoirParser> { + let typ = parse_type().recover_via(parameter_recovery()); + let typ = just(Token::Colon).ignore_then(typ); + + let parameter = pattern() + .recover_via(parameter_name_recovery()) + .then(typ.or_not().map(|typ| typ.unwrap_or_else(UnresolvedType::unspecified))); + + parameter + .separated_by(just(Token::Comma)) + .allow_trailing() + .labelled(ParsingRuleLabel::Parameter) +} + +fn lambda_return_type() -> impl NoirParser { + just(Token::Arrow) + .ignore_then(parse_type()) + .or_not() + .map(|ret| ret.unwrap_or_else(UnresolvedType::unspecified)) +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/literals.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/literals.rs new file mode 100644 index 00000000000..32f4f03de2e --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/literals.rs @@ -0,0 +1,157 @@ +use chumsky::Parser; + +use crate::{ + parser::NoirParser, + token::{Token, TokenKind}, + ExpressionKind, +}; + +use super::primitives::token_kind; + +pub(super) fn literal() -> impl NoirParser { + token_kind(TokenKind::Literal).map(|token| match token { + Token::Int(x) => ExpressionKind::integer(x), + Token::Bool(b) => ExpressionKind::boolean(b), + Token::Str(s) => ExpressionKind::string(s), + Token::RawStr(s, hashes) => ExpressionKind::raw_string(s, hashes), + Token::FmtStr(s) => ExpressionKind::format_string(s), + unexpected => unreachable!("Non-literal {} parsed as a literal", unexpected), + }) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::parser::parser::{ + expression, expression_no_constructors, fresh_statement, term, test_helpers::*, + }; + use crate::Literal; + + fn expr_to_lit(expr: ExpressionKind) -> Literal { + match expr { + ExpressionKind::Literal(literal) => literal, + _ => unreachable!("expected a literal"), + } + } + + #[test] + fn parse_int() { + let int = parse_with(literal(), "5").unwrap(); + let hex = parse_with(literal(), "0x05").unwrap(); + + match (expr_to_lit(int), expr_to_lit(hex)) { + (Literal::Integer(int, false), Literal::Integer(hex, false)) => assert_eq!(int, hex), + _ => unreachable!(), + } + } + + #[test] + fn parse_string() { + let expr = parse_with(literal(), r#""hello""#).unwrap(); + match expr_to_lit(expr) { + Literal::Str(s) => assert_eq!(s, "hello"), + _ => unreachable!(), + }; + } + + #[test] + fn parse_bool() { + let expr_true = parse_with(literal(), "true").unwrap(); + let expr_false = parse_with(literal(), "false").unwrap(); + + match (expr_to_lit(expr_true), expr_to_lit(expr_false)) { + (Literal::Bool(t), Literal::Bool(f)) => { + assert!(t); + assert!(!f); + } + _ => unreachable!(), + }; + } + + #[test] + fn parse_unary() { + parse_all( + term(expression(), expression_no_constructors(expression()), fresh_statement(), true), + vec!["!hello", "-hello", "--hello", "-!hello", "!-hello"], + ); + parse_all_failing( + term(expression(), expression_no_constructors(expression()), fresh_statement(), true), + vec!["+hello", "/hello"], + ); + } + + #[test] + fn parse_raw_string_expr() { + let cases = vec![ + Case { source: r#" r"foo" "#, expect: r#"r"foo""#, errors: 0 }, + Case { source: r##" r#"foo"# "##, expect: r##"r#"foo"#"##, errors: 0 }, + // backslash + Case { source: r#" r"\\" "#, expect: r#"r"\\""#, errors: 0 }, + Case { source: r##" r#"\"# "##, expect: r##"r#"\"#"##, errors: 0 }, + Case { source: r##" r#"\\"# "##, expect: r##"r#"\\"#"##, errors: 0 }, + Case { source: r##" r#"\\\"# "##, expect: r##"r#"\\\"#"##, errors: 0 }, + // escape sequence + Case { + source: r##" r#"\t\n\\t\\n\\\t\\\n\\\\"# "##, + expect: r##"r#"\t\n\\t\\n\\\t\\\n\\\\"#"##, + errors: 0, + }, + Case { source: r##" r#"\\\\\\\\"# "##, expect: r##"r#"\\\\\\\\"#"##, errors: 0 }, + // mismatch - errors: + Case { source: r###" r#"foo"## "###, expect: r##"r#"foo"#"##, errors: 1 }, + Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 }, + // mismatch: short: + Case { source: r##" r"foo"# "##, expect: r#"r"foo""#, errors: 1 }, + Case { source: r#" r#"foo" "#, expect: "(none)", errors: 2 }, + // empty string + Case { source: r#"r"""#, expect: r#"r"""#, errors: 0 }, + Case { source: r####"r###""###"####, expect: r####"r###""###"####, errors: 0 }, + // miscellaneous + Case { source: r##" r#\"foo\"# "##, expect: "plain::r", errors: 2 }, + Case { source: r#" r\"foo\" "#, expect: "plain::r", errors: 1 }, + Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 }, + // missing 'r' letter + Case { source: r##" ##"foo"# "##, expect: r#""foo""#, errors: 2 }, + Case { source: r#" #"foo" "#, expect: "plain::foo", errors: 2 }, + // whitespace + Case { source: r##" r #"foo"# "##, expect: "plain::r", errors: 2 }, + Case { source: r##" r# "foo"# "##, expect: "plain::r", errors: 3 }, + Case { source: r#" r#"foo" # "#, expect: "(none)", errors: 2 }, + // after identifier + Case { source: r##" bar#"foo"# "##, expect: "plain::bar", errors: 2 }, + // nested + Case { + source: r###"r##"foo r#"bar"# r"baz" ### bye"##"###, + expect: r###"r##"foo r#"bar"# r"baz" ### bye"##"###, + errors: 0, + }, + ]; + + check_cases_with_errors(&cases[..], expression()); + } + + #[test] + fn parse_raw_string_lit() { + let lit_cases = vec![ + Case { source: r#" r"foo" "#, expect: r#"r"foo""#, errors: 0 }, + Case { source: r##" r#"foo"# "##, expect: r##"r#"foo"#"##, errors: 0 }, + // backslash + Case { source: r#" r"\\" "#, expect: r#"r"\\""#, errors: 0 }, + Case { source: r##" r#"\"# "##, expect: r##"r#"\"#"##, errors: 0 }, + Case { source: r##" r#"\\"# "##, expect: r##"r#"\\"#"##, errors: 0 }, + Case { source: r##" r#"\\\"# "##, expect: r##"r#"\\\"#"##, errors: 0 }, + // escape sequence + Case { + source: r##" r#"\t\n\\t\\n\\\t\\\n\\\\"# "##, + expect: r##"r#"\t\n\\t\\n\\\t\\\n\\\\"#"##, + errors: 0, + }, + Case { source: r##" r#"\\\\\\\\"# "##, expect: r##"r#"\\\\\\\\"#"##, errors: 0 }, + // mismatch - errors: + Case { source: r###" r#"foo"## "###, expect: r##"r#"foo"#"##, errors: 1 }, + Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 }, + ]; + + check_cases_with_errors(&lit_cases[..], literal()); + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/path.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/path.rs new file mode 100644 index 00000000000..ab812c07dce --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/path.rs @@ -0,0 +1,78 @@ +use crate::parser::NoirParser; +use crate::{Path, PathKind}; + +use crate::token::{Keyword, Token}; + +use chumsky::prelude::*; + +use super::{ident, keyword}; + +pub(super) fn path() -> impl NoirParser { + let idents = || ident().separated_by(just(Token::DoubleColon)).at_least(1); + let make_path = |kind| move |segments, span| Path { segments, kind, span }; + + let prefix = |key| keyword(key).ignore_then(just(Token::DoubleColon)); + let path_kind = |key, kind| prefix(key).ignore_then(idents()).map_with_span(make_path(kind)); + + choice(( + path_kind(Keyword::Crate, PathKind::Crate), + path_kind(Keyword::Dep, PathKind::Dep), + idents().map_with_span(make_path(PathKind::Plain)), + )) +} + +fn empty_path() -> impl NoirParser { + let make_path = |kind| move |_, span| Path { segments: Vec::new(), kind, span }; + let path_kind = |key, kind| keyword(key).map_with_span(make_path(kind)); + + choice((path_kind(Keyword::Crate, PathKind::Crate), path_kind(Keyword::Dep, PathKind::Dep))) +} + +pub(super) fn maybe_empty_path() -> impl NoirParser { + path().or(empty_path()) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::parser::parser::test_helpers::{parse_all_failing, parse_with}; + + #[test] + fn parse_path() { + let cases = vec![ + ("std", vec!["std"]), + ("std::hash", vec!["std", "hash"]), + ("std::hash::collections", vec!["std", "hash", "collections"]), + ("dep::foo::bar", vec!["foo", "bar"]), + ("crate::std::hash", vec!["std", "hash"]), + ]; + + for (src, expected_segments) in cases { + let path: Path = parse_with(path(), src).unwrap(); + for (segment, expected) in path.segments.into_iter().zip(expected_segments) { + assert_eq!(segment.0.contents, expected); + } + } + + parse_all_failing(path(), vec!["std::", "::std", "std::hash::", "foo::1"]); + } + + #[test] + fn parse_path_kinds() { + let cases = vec![ + ("std", PathKind::Plain), + ("dep::hash::collections", PathKind::Dep), + ("crate::std::hash", PathKind::Crate), + ]; + + for (src, expected_path_kind) in cases { + let path = parse_with(path(), src).unwrap(); + assert_eq!(path.kind, expected_path_kind); + } + + parse_all_failing( + path(), + vec!["dep", "crate", "crate::std::crate", "foo::bar::crate", "foo::dep"], + ); + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs new file mode 100644 index 00000000000..34927278038 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs @@ -0,0 +1,101 @@ +use chumsky::prelude::*; + +use crate::{ + parser::{labels::ParsingRuleLabel, ExprParser, NoirParser, ParserError}, + token::{Keyword, Token, TokenKind}, + ExpressionKind, Ident, UnaryOp, +}; + +use super::path; + +/// This parser always parses no input and fails +pub(super) fn nothing() -> impl NoirParser { + one_of([]).map(|_| unreachable!("parser should always error")) +} + +pub(super) fn keyword(keyword: Keyword) -> impl NoirParser { + just(Token::Keyword(keyword)) +} + +pub(super) fn token_kind(token_kind: TokenKind) -> impl NoirParser { + filter_map(move |span, found: Token| { + if found.kind() == token_kind { + Ok(found) + } else { + Err(ParserError::expected_label( + ParsingRuleLabel::TokenKind(token_kind.clone()), + found, + span, + )) + } + }) +} + +pub(super) fn ident() -> impl NoirParser { + token_kind(TokenKind::Ident).map_with_span(Ident::from_token) +} + +// Right-shift (>>) is issued as two separate > tokens by the lexer as this makes it easier +// to parse nested generic types. For normal expressions however, it means we have to manually +// parse two greater-than tokens as a single right-shift here. +pub(super) fn right_shift_operator() -> impl NoirParser { + just(Token::Greater).then(just(Token::Greater)).to(Token::ShiftRight) +} + +pub(super) fn not

(term_parser: P) -> impl NoirParser +where + P: ExprParser, +{ + just(Token::Bang).ignore_then(term_parser).map(|rhs| ExpressionKind::prefix(UnaryOp::Not, rhs)) +} + +pub(super) fn negation

(term_parser: P) -> impl NoirParser +where + P: ExprParser, +{ + just(Token::Minus) + .ignore_then(term_parser) + .map(|rhs| ExpressionKind::prefix(UnaryOp::Minus, rhs)) +} + +pub(super) fn mutable_reference

(term_parser: P) -> impl NoirParser +where + P: ExprParser, +{ + just(Token::Ampersand) + .ignore_then(keyword(Keyword::Mut)) + .ignore_then(term_parser) + .map(|rhs| ExpressionKind::prefix(UnaryOp::MutableReference, rhs)) +} + +pub(super) fn dereference

(term_parser: P) -> impl NoirParser +where + P: ExprParser, +{ + just(Token::Star) + .ignore_then(term_parser) + .map(|rhs| ExpressionKind::prefix(UnaryOp::Dereference { implicitly_added: false }, rhs)) +} + +pub(super) fn variable() -> impl NoirParser { + path().map(ExpressionKind::Variable) +} + +#[cfg(test)] +mod test { + use crate::parser::parser::{ + expression, expression_no_constructors, fresh_statement, term, test_helpers::*, + }; + + #[test] + fn parse_unary() { + parse_all( + term(expression(), expression_no_constructors(expression()), fresh_statement(), true), + vec!["!hello", "-hello", "--hello", "-!hello", "!-hello"], + ); + parse_all_failing( + term(expression(), expression_no_constructors(expression()), fresh_statement(), true), + vec!["+hello", "/hello"], + ); + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/structs.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/structs.rs new file mode 100644 index 00000000000..0212f56783f --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -0,0 +1,97 @@ +use chumsky::prelude::*; +use noirc_errors::Span; + +use crate::{ + macros_api::SecondaryAttribute, + parser::{ + parser::{ + attributes::attributes, + function, parse_type, + primitives::{ident, keyword}, + }, + NoirParser, ParserError, ParserErrorReason, TopLevelStatement, + }, + token::{Attribute, Keyword, Token}, + Ident, NoirStruct, UnresolvedType, +}; + +pub(super) fn struct_definition() -> impl NoirParser { + use self::Keyword::Struct; + use Token::*; + + let fields = struct_fields() + .delimited_by(just(LeftBrace), just(RightBrace)) + .recover_with(nested_delimiters( + LeftBrace, + RightBrace, + [(LeftParen, RightParen), (LeftBracket, RightBracket)], + |_| vec![], + )) + .or(just(Semicolon).to(Vec::new())); + + attributes() + .then_ignore(keyword(Struct)) + .then(ident()) + .then(function::generics()) + .then(fields) + .validate(|(((raw_attributes, name), generics), fields), span, emit| { + let attributes = validate_struct_attributes(raw_attributes, span, emit); + TopLevelStatement::Struct(NoirStruct { name, attributes, generics, fields, span }) + }) +} + +fn struct_fields() -> impl NoirParser> { + ident() + .then_ignore(just(Token::Colon)) + .then(parse_type()) + .separated_by(just(Token::Comma)) + .allow_trailing() +} + +fn validate_struct_attributes( + attributes: Vec, + span: Span, + emit: &mut dyn FnMut(ParserError), +) -> Vec { + let mut struct_attributes = vec![]; + + for attribute in attributes { + match attribute { + Attribute::Function(..) => { + emit(ParserError::with_reason( + ParserErrorReason::NoFunctionAttributesAllowedOnStruct, + span, + )); + } + Attribute::Secondary(attr) => struct_attributes.push(attr), + } + } + + struct_attributes +} + +#[cfg(test)] +mod test { + use super::*; + use crate::parser::parser::test_helpers::*; + + #[test] + fn parse_structs() { + let cases = vec![ + "struct Foo;", + "struct Foo { }", + "struct Bar { ident: Field, }", + "struct Baz { ident: Field, other: Field }", + "#[attribute] struct Baz { ident: Field, other: Field }", + ]; + parse_all(struct_definition(), cases); + + let failing = vec![ + "struct { }", + "struct Foo { bar: pub Field }", + "struct Foo { bar: pub Field }", + "#[oracle(some)] struct Foo { bar: Field }", + ]; + parse_all_failing(struct_definition(), failing); + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/test_helpers.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/test_helpers.rs new file mode 100644 index 00000000000..6b8cb80a0a0 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/test_helpers.rs @@ -0,0 +1,122 @@ +use chumsky::primitive::just; +use chumsky::Parser; +use iter_extended::vecmap; +use noirc_errors::CustomDiagnostic; + +use crate::{ + lexer::Lexer, + parser::{force, NoirParser}, + token::Token, +}; + +pub(crate) fn parse_with(parser: P, program: &str) -> Result> +where + P: NoirParser, +{ + let (tokens, lexer_errors) = Lexer::lex(program); + if !lexer_errors.is_empty() { + return Err(vecmap(lexer_errors, Into::into)); + } + parser.then_ignore(just(Token::EOF)).parse(tokens).map_err(|errors| vecmap(errors, Into::into)) +} + +pub(crate) fn parse_recover(parser: P, program: &str) -> (Option, Vec) +where + P: NoirParser, +{ + let (tokens, lexer_errors) = Lexer::lex(program); + let (opt, errs) = parser.then_ignore(force(just(Token::EOF))).parse_recovery(tokens); + + let mut errors = vecmap(lexer_errors, Into::into); + errors.extend(errs.into_iter().map(Into::into)); + + (opt, errors) +} + +pub(crate) fn parse_all(parser: P, programs: Vec<&str>) -> Vec +where + P: NoirParser, +{ + vecmap(programs, move |program| { + let message = format!("Failed to parse:\n{program}"); + let (op_t, diagnostics) = parse_recover(&parser, program); + diagnostics.iter().for_each(|diagnostic| { + if diagnostic.is_error() { + panic!("{} with error {}", &message, diagnostic); + } + }); + op_t.expect(&message) + }) +} + +pub(crate) fn parse_all_failing(parser: P, programs: Vec<&str>) -> Vec +where + P: NoirParser, + T: std::fmt::Display, +{ + programs + .into_iter() + .flat_map(|program| match parse_with(&parser, program) { + Ok(expr) => { + unreachable!( + "Expected this input to fail:\n{}\nYet it successfully parsed as:\n{}", + program, expr + ) + } + Err(diagnostics) => { + if diagnostics.iter().all(|diagnostic: &CustomDiagnostic| diagnostic.is_warning()) { + unreachable!( + "Expected at least one error when parsing:\n{}\nYet it successfully parsed without errors:\n", + program + ) + }; + diagnostics + } + }) + .collect() +} + +#[derive(Copy, Clone)] +pub(crate) struct Case { + pub(crate) source: &'static str, + pub(crate) errors: usize, + pub(crate) expect: &'static str, +} + +pub(crate) fn check_cases_with_errors(cases: &[Case], parser: P) +where + P: NoirParser + Clone, + T: std::fmt::Display, +{ + let show_errors = |v| vecmap(&v, ToString::to_string).join("\n"); + + let results = vecmap(cases, |&case| { + let (opt, errors) = parse_recover(parser.clone(), case.source); + let actual = opt.map(|ast| ast.to_string()); + let actual = if let Some(s) = &actual { s.to_string() } else { "(none)".to_string() }; + + let result = ((errors.len(), actual.clone()), (case.errors, case.expect.to_string())); + if result.0 != result.1 { + let num_errors = errors.len(); + let shown_errors = show_errors(errors); + eprintln!( + concat!( + "\nExpected {expected_errors} error(s) and got {num_errors}:", + "\n\n{shown_errors}", + "\n\nFrom input: {src}", + "\nExpected AST: {expected_result}", + "\nActual AST: {actual}\n", + ), + expected_errors = case.errors, + num_errors = num_errors, + shown_errors = shown_errors, + src = case.source, + expected_result = case.expect, + actual = actual, + ); + } + result + }); + + assert_eq!(vecmap(&results, |t| t.0.clone()), vecmap(&results, |t| t.1.clone()),); +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs new file mode 100644 index 00000000000..0d72fbd5303 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -0,0 +1,217 @@ +use chumsky::prelude::*; + +use super::{ + block, expression, fresh_statement, function, function_declaration_parameters, + function_return_type, +}; + +use crate::{ + parser::{ + ignore_then_commit, parenthesized, parser::primitives::keyword, NoirParser, ParserError, + ParserErrorReason, TopLevelStatement, + }, + token::{Keyword, Token}, + Expression, FunctionVisibility, NoirTrait, NoirTraitImpl, TraitBound, TraitImplItem, TraitItem, + UnresolvedTraitConstraint, UnresolvedType, +}; + +use super::{generic_type_args, parse_type, path, primitives::ident}; + +pub(super) fn trait_definition() -> impl NoirParser { + keyword(Keyword::Trait) + .ignore_then(ident()) + .then(function::generics()) + .then(where_clause()) + .then_ignore(just(Token::LeftBrace)) + .then(trait_body()) + .then_ignore(just(Token::RightBrace)) + .map_with_span(|(((name, generics), where_clause), items), span| { + TopLevelStatement::Trait(NoirTrait { name, generics, where_clause, span, items }) + }) +} + +fn trait_body() -> impl NoirParser> { + trait_function_declaration() + .or(trait_type_declaration()) + .or(trait_constant_declaration()) + .repeated() +} + +fn optional_default_value() -> impl NoirParser> { + ignore_then_commit(just(Token::Assign), expression()).or_not() +} + +fn trait_constant_declaration() -> impl NoirParser { + keyword(Keyword::Let) + .ignore_then(ident()) + .then_ignore(just(Token::Colon)) + .then(parse_type()) + .then(optional_default_value()) + .then_ignore(just(Token::Semicolon)) + .validate(|((name, typ), default_value), span, emit| { + emit(ParserError::with_reason( + ParserErrorReason::ExperimentalFeature("Associated constants"), + span, + )); + TraitItem::Constant { name, typ, default_value } + }) +} + +/// trait_function_declaration: 'fn' ident generics '(' declaration_parameters ')' function_return_type +fn trait_function_declaration() -> impl NoirParser { + let trait_function_body_or_semicolon = + block(fresh_statement()).map(Option::from).or(just(Token::Semicolon).to(Option::None)); + + keyword(Keyword::Fn) + .ignore_then(ident()) + .then(function::generics()) + .then(parenthesized(function_declaration_parameters())) + .then(function_return_type().map(|(_, typ)| typ)) + .then(where_clause()) + .then(trait_function_body_or_semicolon) + .map(|(((((name, generics), parameters), return_type), where_clause), body)| { + TraitItem::Function { name, generics, parameters, return_type, where_clause, body } + }) +} + +/// trait_type_declaration: 'type' ident generics +fn trait_type_declaration() -> impl NoirParser { + keyword(Keyword::Type).ignore_then(ident()).then_ignore(just(Token::Semicolon)).validate( + |name, span, emit| { + emit(ParserError::with_reason( + ParserErrorReason::ExperimentalFeature("Associated types"), + span, + )); + TraitItem::Type { name } + }, + ) +} + +/// Parses a trait implementation, implementing a particular trait for a type. +/// This has a similar syntax to `implementation`, but the `for type` clause is required, +/// and an optional `where` clause is also useable. +/// +/// trait_implementation: 'impl' generics ident generic_args for type '{' trait_implementation_body '}' +pub(super) fn trait_implementation() -> impl NoirParser { + keyword(Keyword::Impl) + .ignore_then(function::generics()) + .then(path()) + .then(generic_type_args(parse_type())) + .then_ignore(keyword(Keyword::For)) + .then(parse_type()) + .then(where_clause()) + .then_ignore(just(Token::LeftBrace)) + .then(trait_implementation_body()) + .then_ignore(just(Token::RightBrace)) + .map(|args| { + let ((other_args, where_clause), items) = args; + let (((impl_generics, trait_name), trait_generics), object_type) = other_args; + + TopLevelStatement::TraitImpl(NoirTraitImpl { + impl_generics, + trait_name, + trait_generics, + object_type, + items, + where_clause, + }) + }) +} + +fn trait_implementation_body() -> impl NoirParser> { + let function = function::function_definition(true).validate(|mut f, span, emit| { + if f.def().is_internal + || f.def().is_unconstrained + || f.def().is_open + || f.def().visibility != FunctionVisibility::Private + { + emit(ParserError::with_reason(ParserErrorReason::TraitImplFunctionModifiers, span)); + } + // Trait impl functions are always public + f.def_mut().visibility = FunctionVisibility::Public; + TraitImplItem::Function(f) + }); + + let alias = keyword(Keyword::Type) + .ignore_then(ident()) + .then_ignore(just(Token::Assign)) + .then(parse_type()) + .then_ignore(just(Token::Semicolon)) + .map(|(name, alias)| TraitImplItem::Type { name, alias }); + + function.or(alias).repeated() +} + +fn where_clause() -> impl NoirParser> { + struct MultiTraitConstraint { + typ: UnresolvedType, + trait_bounds: Vec, + } + + let constraints = parse_type() + .then_ignore(just(Token::Colon)) + .then(trait_bounds()) + .map(|(typ, trait_bounds)| MultiTraitConstraint { typ, trait_bounds }); + + keyword(Keyword::Where) + .ignore_then(constraints.separated_by(just(Token::Comma))) + .or_not() + .map(|option| option.unwrap_or_default()) + .map(|x: Vec| { + let mut result: Vec = Vec::new(); + for constraint in x { + for bound in constraint.trait_bounds { + result.push(UnresolvedTraitConstraint { + typ: constraint.typ.clone(), + trait_bound: bound, + }); + } + } + result + }) +} + +fn trait_bounds() -> impl NoirParser> { + trait_bound().separated_by(just(Token::Plus)).at_least(1).allow_trailing() +} + +fn trait_bound() -> impl NoirParser { + path().then(generic_type_args(parse_type())).map(|(trait_path, trait_generics)| TraitBound { + trait_path, + trait_generics, + trait_id: None, + }) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::parser::parser::test_helpers::*; + + #[test] + fn parse_trait() { + parse_all( + trait_definition(), + vec![ + // Empty traits are legal in Rust and sometimes used as a way to whitelist certain types + // for a particular operation. Also known as `tag` or `marker` traits: + // https://stackoverflow.com/questions/71895489/what-is-the-purpose-of-defining-empty-impl-in-rust + "trait Empty {}", + "trait TraitWithDefaultBody { fn foo(self) {} }", + "trait TraitAcceptingMutableRef { fn foo(&mut self); }", + "trait TraitWithTypeBoundOperation { fn identity() -> Self; }", + "trait TraitWithAssociatedType { type Element; fn item(self, index: Field) -> Self::Element; }", + "trait TraitWithAssociatedConstant { let Size: Field; }", + "trait TraitWithAssociatedConstantWithDefaultValue { let Size: Field = 10; }", + "trait GenericTrait { fn elem(&mut self, index: Field) -> T; }", + "trait GenericTraitWithConstraints where T: SomeTrait { fn elem(self, index: Field) -> T; }", + "trait TraitWithMultipleGenericParams where A: SomeTrait, B: AnotherTrait { let Size: Field; fn zero() -> Self; }", + ], + ); + + parse_all_failing( + trait_definition(), + vec!["trait MissingBody", "trait WrongDelimiter { fn foo() -> u8, fn bar() -> u8 }"], + ); + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs new file mode 100644 index 00000000000..572397d6527 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs @@ -0,0 +1,172 @@ +use super::{ + expression_with_precedence, keyword, nothing, parenthesized, NoirParser, ParserError, + ParserErrorReason, Precedence, +}; +use crate::ast::{UnresolvedType, UnresolvedTypeData}; + +use crate::parser::labels::ParsingRuleLabel; +use crate::token::{Keyword, Token}; +use crate::{Recoverable, UnresolvedTypeExpression}; + +use chumsky::prelude::*; +use noirc_errors::Span; + +fn maybe_comp_time() -> impl NoirParser<()> { + keyword(Keyword::CompTime).or_not().validate(|opt, span, emit| { + if opt.is_some() { + emit(ParserError::with_reason(ParserErrorReason::ComptimeDeprecated, span)); + } + }) +} + +pub(super) fn parenthesized_type( + recursive_type_parser: impl NoirParser, +) -> impl NoirParser { + recursive_type_parser + .delimited_by(just(Token::LeftParen), just(Token::RightParen)) + .map_with_span(|typ, span| UnresolvedType { + typ: UnresolvedTypeData::Parenthesized(Box::new(typ)), + span: span.into(), + }) +} + +pub(super) fn field_type() -> impl NoirParser { + maybe_comp_time() + .then_ignore(keyword(Keyword::Field)) + .map_with_span(|_, span| UnresolvedTypeData::FieldElement.with_span(span)) +} + +pub(super) fn bool_type() -> impl NoirParser { + maybe_comp_time() + .then_ignore(keyword(Keyword::Bool)) + .map_with_span(|_, span| UnresolvedTypeData::Bool.with_span(span)) +} + +pub(super) fn string_type() -> impl NoirParser { + keyword(Keyword::String) + .ignore_then( + type_expression().delimited_by(just(Token::Less), just(Token::Greater)).or_not(), + ) + .map_with_span(|expr, span| UnresolvedTypeData::String(expr).with_span(span)) +} + +pub(super) fn format_string_type( + type_parser: impl NoirParser, +) -> impl NoirParser { + keyword(Keyword::FormatString) + .ignore_then( + type_expression() + .then_ignore(just(Token::Comma)) + .then(type_parser) + .delimited_by(just(Token::Less), just(Token::Greater)), + ) + .map_with_span(|(size, fields), span| { + UnresolvedTypeData::FormatString(size, Box::new(fields)).with_span(span) + }) +} + +pub(super) fn int_type() -> impl NoirParser { + maybe_comp_time() + .then(filter_map(|span, token: Token| match token { + Token::IntType(int_type) => Ok(int_type), + unexpected => { + Err(ParserError::expected_label(ParsingRuleLabel::IntegerType, unexpected, span)) + } + })) + .validate(|(_, token), span, emit| { + UnresolvedTypeData::from_int_token(token) + .map(|data| data.with_span(span)) + .unwrap_or_else(|err| { + emit(ParserError::with_reason(ParserErrorReason::InvalidBitSize(err.0), span)); + UnresolvedType::error(span) + }) + }) +} + +pub(super) fn array_type( + type_parser: impl NoirParser, +) -> impl NoirParser { + just(Token::LeftBracket) + .ignore_then(type_parser) + .then(just(Token::Semicolon).ignore_then(type_expression()).or_not()) + .then_ignore(just(Token::RightBracket)) + .map_with_span(|(element_type, size), span| { + UnresolvedTypeData::Array(size, Box::new(element_type)).with_span(span) + }) +} + +pub(super) fn type_expression() -> impl NoirParser { + recursive(|expr| { + expression_with_precedence( + Precedence::lowest_type_precedence(), + expr, + nothing(), + nothing(), + true, + false, + ) + }) + .labelled(ParsingRuleLabel::TypeExpression) + .try_map(UnresolvedTypeExpression::from_expr) +} + +pub(super) fn tuple_type(type_parser: T) -> impl NoirParser +where + T: NoirParser, +{ + let fields = type_parser.separated_by(just(Token::Comma)).allow_trailing(); + parenthesized(fields).map_with_span(|fields, span| { + if fields.is_empty() { + UnresolvedTypeData::Unit.with_span(span) + } else { + UnresolvedTypeData::Tuple(fields).with_span(span) + } + }) +} + +pub(super) fn function_type(type_parser: T) -> impl NoirParser +where + T: NoirParser, +{ + let args = parenthesized(type_parser.clone().separated_by(just(Token::Comma)).allow_trailing()); + + let env = just(Token::LeftBracket) + .ignore_then(type_parser.clone()) + .then_ignore(just(Token::RightBracket)) + .or_not() + .map_with_span(|t, span| { + t.unwrap_or_else(|| UnresolvedTypeData::Unit.with_span(Span::empty(span.end()))) + }); + + keyword(Keyword::Fn) + .ignore_then(env) + .then(args) + .then_ignore(just(Token::Arrow)) + .then(type_parser) + .map_with_span(|((env, args), ret), span| { + UnresolvedTypeData::Function(args, Box::new(ret), Box::new(env)).with_span(span) + }) +} + +pub(super) fn mutable_reference_type(type_parser: T) -> impl NoirParser +where + T: NoirParser, +{ + just(Token::Ampersand) + .ignore_then(keyword(Keyword::Mut)) + .ignore_then(type_parser) + .map_with_span(|element, span| { + UnresolvedTypeData::MutableReference(Box::new(element)).with_span(span) + }) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::parser::parser::test_helpers::*; + + #[test] + fn parse_type_expression() { + parse_all(type_expression(), vec!["(123)", "123", "(1 + 1)", "(1 + (1))"]); + } +} diff --git a/noir/noir-repo/cspell.json b/noir/noir-repo/cspell.json index 23659b39c68..a96e3de901a 100644 --- a/noir/noir-repo/cspell.json +++ b/noir/noir-repo/cspell.json @@ -13,6 +13,8 @@ "arithmetization", "arity", "arkworks", + "backpropagate", + "Backpropagation", "barebones", "barretenberg", "bincode", diff --git a/noir/noir-repo/docs/docs/noir/standard_library/containers/boundedvec.md b/noir/noir-repo/docs/docs/noir/standard_library/containers/boundedvec.md new file mode 100644 index 00000000000..cd0f725f870 --- /dev/null +++ b/noir/noir-repo/docs/docs/noir/standard_library/containers/boundedvec.md @@ -0,0 +1,210 @@ +--- +title: Bounded Vectors +keywords: [noir, vector, bounded vector, slice] +sidebar_position: 1 +--- + +A `BoundedVec` is a growable storage similar to a `Vec` except that it +is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented +via slices and thus is not subject to the same restrictions slices are (notably, nested +slices - and thus nested vectors as well - are disallowed). + +Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by +pushing an additional element is also more efficient - the length only needs to be increased +by one. + +For these reasons `BoundedVec` should generally be preferred over `Vec` when there +is a reasonable maximum bound that can be placed on the vector. + +Example: + +```rust +let mut vector: BoundedVec = BoundedVec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +assert(vector.max_len() == 10); +``` + +## Methods + +### new + +```rust +pub fn new() -> Self +``` + +Creates a new, empty vector of length zero. + +Since this container is backed by an array internally, it still needs an initial value +to give each element. To resolve this, each element is zeroed internally. This value +is guaranteed to be inaccessible unless `get_unchecked` is used. + +Example: + +```rust +let empty_vector: BoundedVec = BoundedVec::new(); +assert(empty_vector.len() == 0); +``` + +Note that whenever calling `new` the maximum length of the vector should always be specified +via a type signature: + +#include_code new_example test_programs/noir_test_success/bounded_vec/src/main.nr rust + +This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions +but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to. + +### get + +```rust +pub fn get(mut self: Self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero. + +If the given index is equal to or greater than the length of the vector, this +will issue a constraint failure. + +Example: + +```rust +fn foo(v: BoundedVec) { + let first = v.get(0); + let last = v.get(v.len() - 1); + assert(first != last); +} +``` + +### get_unchecked + +```rust +pub fn get_unchecked(mut self: Self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero, without +performing a bounds check. + +Since this function does not perform a bounds check on length before accessing the element, +it is unsafe! Use at your own risk! + +Example: + +#include_code get_unchecked_example test_programs/noir_test_success/bounded_vec/src/main.nr rust + + +### push + +```rust +pub fn push(&mut self, elem: T) { +``` + +Pushes an element to the end of the vector. This increases the length +of the vector by one. + +Panics if the new length of the vector will be greater than the max length. + +Example: + +#include_code bounded-vec-push-example test_programs/noir_test_success/bounded_vec/src/main.nr rust + +### pop + +```rust +pub fn pop(&mut self) -> T +``` + +Pops the element at the end of the vector. This will decrease the length +of the vector by one. + +Panics if the vector is empty. + +Example: + +#include_code bounded-vec-pop-example test_programs/noir_test_success/bounded_vec/src/main.nr rust + +### len + +```rust +pub fn len(self) -> u64 { +``` + +Returns the current length of this vector + +Example: + +#include_code bounded-vec-len-example test_programs/noir_test_success/bounded_vec/src/main.nr rust + +### max_len + +```rust +pub fn max_len(_self: BoundedVec) -> u64 { +``` + +Returns the maximum length of this vector. This is always +equal to the `MaxLen` parameter this vector was initialized with. + +Example: + +#include_code bounded-vec-max-len-example test_programs/noir_test_success/bounded_vec/src/main.nr rust + +### storage + +```rust +pub fn storage(self) -> [T; MaxLen] { +``` + +Returns the internal array within this vector. +Since arrays in Noir are immutable, mutating the returned storage array will not mutate +the storage held internally by this vector. + +Note that uninitialized elements may be zeroed out! + +Example: + +#include_code bounded-vec-storage-example test_programs/noir_test_success/bounded_vec/src/main.nr rust + +### extend_from_array + +```rust +pub fn extend_from_array(&mut self, array: [T; Len]) +``` + +Pushes each element from the given array to this vector. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +#include_code bounded-vec-extend-from-array-example test_programs/noir_test_success/bounded_vec/src/main.nr rust + +### extend_from_bounded_vec + +```rust +pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) +``` + +Pushes each element from the other vector to this vector. The length of +the other vector is left unchanged. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +#include_code bounded-vec-extend-from-bounded-vec-example test_programs/noir_test_success/bounded_vec/src/main.nr rust + +### any + +```rust +pub fn any(self, predicate: fn[Env](T) -> bool) -> bool +``` + +Returns true if the given predicate returns true for any element +in this vector. + +Example: + +#include_code bounded-vec-any-example test_programs/noir_test_success/bounded_vec/src/main.nr rust diff --git a/noir/noir-repo/docs/docs/noir/standard_library/containers/index.md b/noir/noir-repo/docs/docs/noir/standard_library/containers/index.md new file mode 100644 index 00000000000..ea84c6d5c21 --- /dev/null +++ b/noir/noir-repo/docs/docs/noir/standard_library/containers/index.md @@ -0,0 +1,5 @@ +--- +title: Containers +description: Container types provided by Noir's standard library for storing and retrieving data +keywords: [containers, data types, vec, hashmap] +--- diff --git a/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx b/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx index a9c10da6c06..99b7f830a20 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ b/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx @@ -16,3 +16,14 @@ fn eddsa_poseidon_verify(public_key_x : Field, public_key_y : Field, signature_s ``` + +## eddsa::eddsa_to_pub + +Private to public key conversion. + +Returns `(pub_key_x, pub_key_y)` + +```rust +fn eddsa_to_pub(secret : Field) -> (Field, Field) +``` + diff --git a/noir/noir-repo/flake.nix b/noir/noir-repo/flake.nix index 4c5db8bfaae..5125dad06be 100644 --- a/noir/noir-repo/flake.nix +++ b/noir/noir-repo/flake.nix @@ -81,7 +81,7 @@ # Custom filter with various file extensions that we rely upon to build packages # Currently: `.nr`, `.sol`, `.sh`, `.json`, `.md` and `.wasm` filter = path: type: - (builtins.match ".*\.(nr|sol|sh|json|md|wasm)$" path != null) || (craneLib.filterCargoSources path type); + (builtins.match ".*\.(nr|sol|sh|json|md|wasm|txt)$" path != null) || (craneLib.filterCargoSources path type); }; # TODO(#1198): It'd be nice to include these flags when running `cargo clippy` in a devShell. diff --git a/noir/noir-repo/noir_stdlib/src/eddsa.nr b/noir/noir-repo/noir_stdlib/src/eddsa.nr index 657e791e9c7..966bc1da2a1 100644 --- a/noir/noir-repo/noir_stdlib/src/eddsa.nr +++ b/noir/noir-repo/noir_stdlib/src/eddsa.nr @@ -38,3 +38,10 @@ pub fn eddsa_poseidon_verify( left.eq(right) } + +// Returns the public key of the given secret key as (pub_key_x, pub_key_y) +pub fn eddsa_to_pub(secret: Field) -> (Field, Field) { + let bjj = baby_jubjub(); + let pub_key = bjj.curve.mul(secret, bjj.curve.gen); + (pub_key.x, pub_key.y) +} diff --git a/noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr b/noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr index 64c1876b4e2..40eea029e82 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr @@ -93,7 +93,8 @@ impl Poseidon2 { } fn hash_internal(input: [Field; N], in_len: u32, is_variable_length: bool) -> Field { - let iv : Field = (in_len as Field) * 18446744073709551616; + let two_pow_64 = 18446744073709551616; + let iv : Field = (in_len as Field) * two_pow_64; let mut sponge = Poseidon2::new(iv); for i in 0..input.len() { if i as u32 < in_len { diff --git a/noir/noir-repo/noir_stdlib/src/sha512.nr b/noir/noir-repo/noir_stdlib/src/sha512.nr index f3155dd7528..4dfe78308e2 100644 --- a/noir/noir-repo/noir_stdlib/src/sha512.nr +++ b/noir/noir-repo/noir_stdlib/src/sha512.nr @@ -136,7 +136,7 @@ pub fn digest(msg: [u8; N]) -> [u8; 64] { msg_block[i as Field] = 0; i += 1; } else if i < 128 { - let mut len = 8 * msg.len(); // u128 unsupported + let mut len = 8 * msg.len(); for j in 0..16 { msg_block[127 - j] = len as u8; len >>= 8; diff --git a/noir/noir-repo/package.json b/noir/noir-repo/package.json index 753a0400b0f..fcf36c9b969 100644 --- a/noir/noir-repo/package.json +++ b/noir/noir-repo/package.json @@ -28,7 +28,7 @@ "build:types": "yarn workspace @noir-lang/types run build", "build:backend_barretenberg": "yarn workspace @noir-lang/backend_barretenberg run build", "build:noir_js": "yarn workspace @noir-lang/noir_js run build", - "build:js:only": "yarn build:types && yarn build:backend_barretenberg && yarn build:noir_js", + "build:js:only": "yarn workspaces foreach -vtp --from \"{@noir-lang/types,@noir-lang/backend_barretenberg,@noir-lang/noir_js,@noir-lang/noir_codegen}\" run build", "prepare:publish": "yarn clean && yarn install:from:nix && yarn build:js:only", "nightly:version": "yarn workspaces foreach run nightly:version", "publish:all": "yarn install && yarn workspaces foreach run publish" diff --git a/noir/noir-repo/test_programs/.gitignore b/noir/noir-repo/test_programs/.gitignore index e98a2fb38b6..6da0100814a 100644 --- a/noir/noir-repo/test_programs/.gitignore +++ b/noir/noir-repo/test_programs/.gitignore @@ -1,3 +1,3 @@ acir_artifacts execution_success/**/crs -Nargo.toml +./Nargo.toml diff --git a/noir/noir-repo/test_programs/compile_success_contract/simple_contract/src/main.nr b/noir/noir-repo/test_programs/compile_success_contract/simple_contract/src/main.nr index fea0ae08c22..ed90ac8bd1d 100644 --- a/noir/noir-repo/test_programs/compile_success_contract/simple_contract/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_contract/simple_contract/src/main.nr @@ -5,10 +5,10 @@ contract Foo { fn triple(x: Field) -> pub Field { x * 3 } - internal fn quadruple(x: Field) -> pub Field { + fn quadruple(x: Field) -> pub Field { x * 4 } - open internal fn skibbidy(x: Field) -> pub Field { + open fn skibbidy(x: Field) -> pub Field { x * 5 } // Regression for issue #3344 diff --git a/noir/noir-repo/test_programs/execution_success/brillig_fns_as_values/Prover.toml b/noir/noir-repo/test_programs/execution_success/brillig_fns_as_values/Prover.toml index 11497a473bc..4dd6b405159 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_fns_as_values/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/brillig_fns_as_values/Prover.toml @@ -1 +1 @@ -x = "0" +x = "1" diff --git a/noir/noir-repo/test_programs/execution_success/brillig_fns_as_values/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_fns_as_values/src/main.nr index ea3148915b8..9248bff2f4c 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_fns_as_values/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_fns_as_values/src/main.nr @@ -7,9 +7,9 @@ struct MyStruct { fn main(x: u32) { assert(wrapper(increment, x) == x + 1); assert(wrapper(increment_acir, x) == x + 1); - assert(wrapper(decrement, x) == std::wrapping_sub(x, 1)); + assert(wrapper(decrement, x) == x - 1); assert(wrapper_with_struct(MyStruct { operation: increment }, x) == x + 1); - assert(wrapper_with_struct(MyStruct { operation: decrement }, x) == std::wrapping_sub(x, 1)); + assert(wrapper_with_struct(MyStruct { operation: decrement }, x) == x - 1); // https://github.com/noir-lang/noir/issues/1975 assert(increment(x) == x + 1); } diff --git a/noir/noir-repo/test_programs/execution_success/brillig_wrapping/Nargo.toml b/noir/noir-repo/test_programs/execution_success/brillig_wrapping/Nargo.toml new file mode 100644 index 00000000000..a52246ba908 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/brillig_wrapping/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "brillig_wrapping" +type = "bin" +authors = [""] + +[dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/brillig_wrapping/Prover.toml b/noir/noir-repo/test_programs/execution_success/brillig_wrapping/Prover.toml new file mode 100644 index 00000000000..346fd2764a7 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/brillig_wrapping/Prover.toml @@ -0,0 +1,2 @@ +x = 0 +y = 255 diff --git a/noir/noir-repo/test_programs/execution_success/brillig_wrapping/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_wrapping/src/main.nr new file mode 100644 index 00000000000..4153a466057 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/brillig_wrapping/src/main.nr @@ -0,0 +1,8 @@ +use dep::std; + +unconstrained fn main(x: u8, y: u8) { + assert(std::wrapping_sub(x, 1) == y); + assert(std::wrapping_add(y, 1) == x); + assert(std::wrapping_mul(y, y) == 1); +} + diff --git a/noir/noir-repo/test_programs/execution_success/eddsa/src/main.nr b/noir/noir-repo/test_programs/execution_success/eddsa/src/main.nr index 12e8ea92785..4404ffe75f7 100644 --- a/noir/noir-repo/test_programs/execution_success/eddsa/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/eddsa/src/main.nr @@ -1,14 +1,20 @@ use dep::std::compat; use dep::std::ec::consts::te::baby_jubjub; +use dep::std::ec::tecurve::affine::Point as TEPoint; use dep::std::hash; -use dep::std::eddsa::eddsa_poseidon_verify; +use dep::std::eddsa::{eddsa_to_pub, eddsa_poseidon_verify}; + fn main(msg: pub Field, _priv_key_a: Field, _priv_key_b: Field) { // Skip this test for non-bn254 backends if compat::is_bn254() { let bjj = baby_jubjub(); let pub_key_a = bjj.curve.mul(_priv_key_a, bjj.curve.gen); - // let pub_key_b = bjj.curve.mul(_priv_key_b, bjj.curve.gen); + let pub_key_b = bjj.curve.mul(_priv_key_b, bjj.curve.gen); + let (pub_key_a_x, pub_key_a_y) = eddsa_to_pub(_priv_key_a); + let (pub_key_b_x, pub_key_b_y) = eddsa_to_pub(_priv_key_b); + assert(TEPoint::new(pub_key_a_x, pub_key_a_y) == pub_key_a); + assert(TEPoint::new(pub_key_b_x, pub_key_b_y) == pub_key_b); // Manually computed as fields can't use modulo. Importantantly the commitment is within // the subgroup order. Note that choice of hash is flexible for this step. // let r_a = hash::pedersen_commitment([_priv_key_a, msg])[0] % bjj.suborder; // modulus computed manually diff --git a/noir/noir-repo/test_programs/execution_success/regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression/src/main.nr index 8dc42cb5f10..c56f3ef4190 100644 --- a/noir/noir-repo/test_programs/execution_success/regression/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/regression/src/main.nr @@ -21,7 +21,7 @@ impl Eq for U4 { } fn compact_decode(input: [u8; N], length: Field) -> ([U4; NIBBLE_LENGTH], Field) { - assert(2 * input.len() <= NIBBLE_LENGTH as u64); + assert(2 * input.len() <= NIBBLE_LENGTH); assert(length as u64 <= input.len()); let mut nibble = [U4::zero(); NIBBLE_LENGTH]; diff --git a/noir/noir-repo/test_programs/execution_success/regression_4436/Nargo.toml b/noir/noir-repo/test_programs/execution_success/regression_4436/Nargo.toml new file mode 100644 index 00000000000..0904d858596 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/regression_4436/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "regression_4436" +type = "bin" +authors = [""] +compiler_version = ">=0.22.0" diff --git a/noir/noir-repo/test_programs/execution_success/regression_4436/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression_4436/src/main.nr new file mode 100644 index 00000000000..834ea3250cc --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/regression_4436/src/main.nr @@ -0,0 +1,31 @@ +trait LibTrait { + fn broadcast(); + fn get_constant() -> Field; +} + +global STRUCT_A_LEN: Field = 3; +global STRUCT_B_LEN: Field = 5; + +struct StructA; +struct StructB; + +impl LibTrait for StructA { + fn broadcast() { + Self::get_constant(); + } + + fn get_constant() -> Field { + 1 + } +} +impl LibTrait for StructB { + fn broadcast() { + Self::get_constant(); + } + + fn get_constant() -> Field { + 1 + } +} + +fn main() {} diff --git a/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr index 0e2c89c9064..22ec291f9d6 100644 --- a/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr @@ -1,3 +1,31 @@ +#[test] +fn test_vec_new_foo() { + foo(); +} + +#[test(should_fail)] +fn test_vec_new_bad() { + bad(); +} + +// docs:start:new_example +fn foo() -> BoundedVec { + // Ok! MaxLen is specified with a type annotation + let v1: BoundedVec = BoundedVec::new(); + let v2 = BoundedVec::new(); + + // Ok! MaxLen is known from the type of foo's return value + v2 +} + +fn bad() { + let mut v3 = BoundedVec::new(); + + // Not Ok! We don't know if v3's MaxLen is at least 1, and the compiler often infers 0 by default. + v3.push(5); +} +// docs:end:new_example + #[test] fn test_vec_push_pop() { let mut vec: BoundedVec = BoundedVec::new(); @@ -15,13 +43,123 @@ fn test_vec_push_pop() { assert(vec.get(1) == 4); } +#[test] +fn test_vec_get_unchecked() { + let mut vec: BoundedVec = BoundedVec::new(); + vec.extend_from_array([1, 2, 3, 4]); + let sum = sum_of_first_three(vec); + assert_eq(sum, 6); +} + +// docs:start:get_unchecked_example +fn sum_of_first_three(v: BoundedVec) -> u32 { + // Always ensure the length is larger than the largest + // index passed to get_unchecked + assert(v.len() > 2); + let first = v.get_unchecked(0); + let second = v.get_unchecked(1); + let third = v.get_unchecked(2); + first + second + third +} +// docs:end:get_unchecked_example + +#[test(should_fail_with = "push out of bounds")] +fn push_docs_example() { + // docs:start:bounded-vec-push-example + let mut v: BoundedVec = BoundedVec::new(); + + v.push(1); + v.push(2); + + // Panics with failed assertion "push out of bounds" + v.push(3); + // docs:end:bounded-vec-push-example +} + +#[test] +fn pop_docs_example() { + // docs:start:bounded-vec-pop-example + let mut v: BoundedVec = BoundedVec::new(); + v.push(1); + v.push(2); + + let two = v.pop(); + let one = v.pop(); + + assert(two == 2); + assert(one == 1); + // error: cannot pop from an empty vector + // let _ = v.pop(); + // docs:end:bounded-vec-pop-example +} + +#[test] +fn len_docs_example() { + // docs:start:bounded-vec-len-example + let mut v: BoundedVec = BoundedVec::new(); + assert(v.len() == 0); + + v.push(100); + assert(v.len() == 1); + + v.push(200); + v.push(300); + v.push(400); + assert(v.len() == 4); + + let _ = v.pop(); + let _ = v.pop(); + assert(v.len() == 2); + // docs:end:bounded-vec-len-example +} + +#[test] +fn max_len_docs_example() { + // docs:start:bounded-vec-max-len-example + let mut v: BoundedVec = BoundedVec::new(); + + assert(v.max_len() == 5); + v.push(10); + assert(v.max_len() == 5); + // docs:end:bounded-vec-max-len-example +} + +#[test] +fn storage_docs_example() { + // docs:start:bounded-vec-storage-example + let mut v: BoundedVec = BoundedVec::new(); + + assert(v.storage() == [0, 0, 0, 0, 0]); + + v.push(57); + assert(v.storage() == [57, 0, 0, 0, 0]); + // docs:end:bounded-vec-storage-example +} + #[test] fn test_vec_extend_from_array() { + // docs:start:bounded-vec-extend-from-array-example let mut vec: BoundedVec = BoundedVec::new(); vec.extend_from_array([2, 4]); + assert(vec.len == 2); assert(vec.get(0) == 2); assert(vec.get(1) == 4); + // docs:end:bounded-vec-extend-from-array-example +} + +#[test] +fn test_vec_extend_from_bounded_vec() { + // docs:start:bounded-vec-extend-from-bounded-vec-example + let mut v1: BoundedVec = BoundedVec::new(); + let mut v2: BoundedVec = BoundedVec::new(); + + v2.extend_from_array([1, 2, 3]); + v1.extend_from_bounded_vec(v2); + + assert(v1.storage() == [1, 2, 3, 0, 0]); + assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); + // docs:end:bounded-vec-extend-from-bounded-vec-example } #[test(should_fail_with="extend_from_array out of bounds")] @@ -88,12 +226,13 @@ fn test_vec_extend_from_bounded_vec_twice_out_of_bound() { #[test] fn test_vec_any() { - let mut vec: BoundedVec = BoundedVec::new(); - 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); + // docs:start:bounded-vec-any-example + let mut v: BoundedVec = BoundedVec::new(); + v.extend_from_array([2, 4, 6]); + + let all_even = !v.any(|elem: u32| elem % 2 != 0); + assert(all_even); + // docs:end:bounded-vec-any-example } #[test] @@ -101,5 +240,6 @@ fn test_vec_any_not_default() { let default_value = 0; let mut vec: BoundedVec = BoundedVec::new(); vec.extend_from_array([2, 4]); - assert(vec.any(|v| v == default_value) == false); + assert(!vec.any(|v| v == default_value)); } + diff --git a/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/Nargo.toml b/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/Nargo.toml new file mode 100644 index 00000000000..b2d47d258ed --- /dev/null +++ b/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "brillig_overflow_checks" +type = "bin" +authors = [""] +[dependencies] diff --git a/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/Prover.toml b/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/Prover.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr new file mode 100644 index 00000000000..5d73ef96d49 --- /dev/null +++ b/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr @@ -0,0 +1,23 @@ +use dep::std::field::bn254::{TWO_POW_128, assert_gt}; + +#[test(should_fail_with = "attempt to add with overflow")] +unconstrained fn test_overflow_add() { + let a: u8 = 255; + let b: u8 = 1; + assert_eq(a + b, 0); +} + +#[test(should_fail_with = "attempt to subtract with overflow")] +unconstrained fn test_overflow_sub() { + let a: u8 = 0; + let b: u8 = 1; + assert_eq(a - b, 255); +} + +#[test(should_fail_with = "attempt to multiply with overflow")] +unconstrained fn test_overflow_mul() { + let a: u8 = 128; + let b: u8 = 2; + assert_eq(a * b, 0); +} + diff --git a/noir/noir-repo/tooling/lsp/src/requests/profile_run.rs b/noir/noir-repo/tooling/lsp/src/requests/profile_run.rs index 917c247410d..89719947689 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/profile_run.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/profile_run.rs @@ -5,7 +5,10 @@ use std::{ use acvm::acir::circuit::ExpressionWidth; use async_lsp::{ErrorCode, ResponseError}; -use nargo::{artifacts::debug::DebugArtifact, insert_all_files_for_workspace_into_file_manager}; +use nargo::{ + artifacts::debug::DebugArtifact, insert_all_files_for_workspace_into_file_manager, + ops::report_errors, +}; use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{ file_manager_with_stdlib, CompileOptions, DebugFile, NOIR_ARTIFACT_VERSION_STRING, @@ -60,11 +63,18 @@ fn on_profile_run_request_inner( Some(_package) => { let expression_width = ExpressionWidth::Bounded { width: 3 }; - let (compiled_programs, compiled_contracts) = nargo::ops::compile_workspace( + let compiled_workspace = nargo::ops::compile_workspace( &workspace_file_manager, &parsed_files, &workspace, &CompileOptions::default(), + ); + + let (compiled_programs, compiled_contracts) = report_errors( + compiled_workspace, + &workspace_file_manager, + CompileOptions::default().deny_warnings, + CompileOptions::default().silence_warnings, ) .map_err(|err| ResponseError::new(ErrorCode::REQUEST_FAILED, err))?; diff --git a/noir/noir-repo/tooling/nargo/src/ops/compile.rs b/noir/noir-repo/tooling/nargo/src/ops/compile.rs index bd1850649c4..d7c7cc2c123 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/compile.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/compile.rs @@ -21,7 +21,7 @@ pub fn compile_workspace( parsed_files: &ParsedFiles, workspace: &Workspace, compile_options: &CompileOptions, -) -> Result<(Vec, Vec), CompileError> { +) -> CompilationResult<(Vec, Vec)> { let (binary_packages, contract_packages): (Vec<_>, Vec<_>) = workspace .into_iter() .filter(|package| !package.is_library()) @@ -38,31 +38,20 @@ pub fn compile_workspace( .map(|package| compile_contract(file_manager, parsed_files, package, compile_options)) .collect(); - // Report any warnings/errors which were encountered during compilation. - let compiled_programs: Vec = program_results - .into_iter() - .map(|compilation_result| { - report_errors( - compilation_result, - file_manager, - compile_options.deny_warnings, - compile_options.silence_warnings, - ) - }) - .collect::>()?; - let compiled_contracts: Vec = contract_results - .into_iter() - .map(|compilation_result| { - report_errors( - compilation_result, - file_manager, - compile_options.deny_warnings, - compile_options.silence_warnings, - ) - }) - .collect::>()?; - - Ok((compiled_programs, compiled_contracts)) + // Collate any warnings/errors which were encountered during compilation. + let compiled_programs = collect_errors(program_results); + let compiled_contracts = collect_errors(contract_results); + + match (compiled_programs, compiled_contracts) { + (Ok((programs, program_warnings)), Ok((contracts, contract_warnings))) => { + let warnings = [program_warnings, contract_warnings].concat(); + Ok(((programs, contracts), warnings)) + } + (Err(program_errors), Err(contract_errors)) => { + Err([program_errors, contract_errors].concat()) + } + (Err(errors), _) | (_, Err(errors)) => Err(errors), + } } pub fn compile_program( @@ -107,7 +96,30 @@ pub fn compile_contract( noirc_driver::compile_contract(&mut context, crate_id, compile_options) } -pub(crate) fn report_errors( +/// Constructs a single `CompilationResult` for a collection of `CompilationResult`s, merging the set of warnings/errors. +pub fn collect_errors(results: Vec>) -> CompilationResult> { + let mut artifacts = Vec::new(); + let mut warnings = Vec::new(); + let mut errors = Vec::new(); + + for result in results { + match result { + Ok((new_artifact, new_warnings)) => { + artifacts.push(new_artifact); + warnings.extend(new_warnings); + } + Err(new_errors) => errors.extend(new_errors), + } + } + + if errors.is_empty() { + Ok((artifacts, warnings)) + } else { + Err(errors) + } +} + +pub fn report_errors( result: CompilationResult, file_manager: &FileManager, deny_warnings: bool, diff --git a/noir/noir-repo/tooling/nargo/src/ops/mod.rs b/noir/noir-repo/tooling/nargo/src/ops/mod.rs index 23dd0db15b9..55e9e927800 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/mod.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/mod.rs @@ -1,5 +1,6 @@ pub use self::compile::{ - compile_contract, compile_program, compile_program_with_debug_instrumenter, compile_workspace, + collect_errors, compile_contract, compile_program, compile_program_with_debug_instrumenter, + compile_workspace, report_errors, }; pub use self::execute::execute_circuit; pub use self::foreign_calls::{ diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs index 4da06d2536a..242a640e484 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs @@ -5,8 +5,8 @@ use clap::Args; use fm::FileManager; use iter_extended::btree_map; use nargo::{ - errors::CompileError, insert_all_files_for_workspace_into_file_manager, package::Package, - parse_all, prepare_package, + errors::CompileError, insert_all_files_for_workspace_into_file_manager, ops::report_errors, + package::Package, parse_all, prepare_package, }; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::{AbiParameter, AbiType, MAIN_RETURN_NAME}; @@ -152,12 +152,7 @@ pub(crate) fn check_crate_and_report_errors( silence_warnings: bool, ) -> Result<(), CompileError> { let result = check_crate(context, crate_id, deny_warnings, disable_macros); - super::compile_cmd::report_errors( - result, - &context.file_manager, - deny_warnings, - silence_warnings, - ) + report_errors(result, &context.file_manager, deny_warnings, silence_warnings) } #[cfg(test)] diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs index 63d27e30836..f0fe2e0ea78 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs @@ -1,11 +1,10 @@ use super::fs::{create_named_dir, write_to_file}; use super::NargoConfig; use crate::backends::Backend; -use crate::cli::compile_cmd::report_errors; use crate::errors::CliError; use clap::Args; -use nargo::ops::compile_program; +use nargo::ops::{compile_program, report_errors}; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{file_manager_with_stdlib, CompileOptions, NOIR_ARTIFACT_VERSION_STRING}; diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/compile_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/compile_cmd.rs index 34fb05249b5..4309f0db3ea 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/compile_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/compile_cmd.rs @@ -2,8 +2,7 @@ use std::path::Path; use fm::FileManager; use nargo::artifacts::program::ProgramArtifact; -use nargo::errors::CompileError; -use nargo::ops::{compile_contract, compile_program}; +use nargo::ops::{collect_errors, compile_contract, compile_program, report_errors}; use nargo::package::Package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; @@ -65,11 +64,18 @@ pub(crate) fn run( .compile_options .expression_width .unwrap_or_else(|| backend.get_backend_info_or_default()); - let (compiled_program, compiled_contracts) = compile_workspace( + let compiled_workspace = compile_workspace( &workspace_file_manager, &parsed_files, &workspace, &args.compile_options, + ); + + let (compiled_programs, compiled_contracts) = report_errors( + compiled_workspace, + &workspace_file_manager, + args.compile_options.deny_warnings, + args.compile_options.silence_warnings, )?; let (binary_packages, contract_packages): (Vec<_>, Vec<_>) = workspace @@ -80,7 +86,7 @@ pub(crate) fn run( // Save build artifacts to disk. let only_acir = args.compile_options.only_acir; - for (package, program) in binary_packages.into_iter().zip(compiled_program) { + for (package, program) in binary_packages.into_iter().zip(compiled_programs) { let program = nargo::ops::transform_program(program, expression_width); save_program(program.clone(), &package, &workspace.target_directory_path(), only_acir); } @@ -97,7 +103,7 @@ pub(super) fn compile_workspace( parsed_files: &ParsedFiles, workspace: &Workspace, compile_options: &CompileOptions, -) -> Result<(Vec, Vec), CliError> { +) -> CompilationResult<(Vec, Vec)> { let (binary_packages, contract_packages): (Vec<_>, Vec<_>) = workspace .into_iter() .filter(|package| !package.is_library()) @@ -123,31 +129,20 @@ pub(super) fn compile_workspace( .map(|package| compile_contract(file_manager, parsed_files, package, compile_options)) .collect(); - // Report any warnings/errors which were encountered during compilation. - let compiled_programs: Vec = program_results - .into_iter() - .map(|compilation_result| { - report_errors( - compilation_result, - file_manager, - compile_options.deny_warnings, - compile_options.silence_warnings, - ) - }) - .collect::>()?; - let compiled_contracts: Vec = contract_results - .into_iter() - .map(|compilation_result| { - report_errors( - compilation_result, - file_manager, - compile_options.deny_warnings, - compile_options.silence_warnings, - ) - }) - .collect::>()?; - - Ok((compiled_programs, compiled_contracts)) + // Collate any warnings/errors which were encountered during compilation. + let compiled_programs = collect_errors(program_results); + let compiled_contracts = collect_errors(contract_results); + + match (compiled_programs, compiled_contracts) { + (Ok((programs, program_warnings)), Ok((contracts, contract_warnings))) => { + let warnings = [program_warnings, contract_warnings].concat(); + Ok(((programs, contracts), warnings)) + } + (Err(program_errors), Err(contract_errors)) => { + Err([program_errors, contract_errors].concat()) + } + (Err(errors), _) | (_, Err(errors)) => Err(errors), + } } pub(super) fn save_program( @@ -172,30 +167,3 @@ fn save_contract(contract: CompiledContract, package: &Package, circuit_dir: &Pa circuit_dir, ); } - -/// Helper function for reporting any errors in a `CompilationResult` -/// structure that is commonly used as a return result in this file. -pub(crate) fn report_errors( - result: CompilationResult, - file_manager: &FileManager, - deny_warnings: bool, - silence_warnings: bool, -) -> Result { - let (t, warnings) = result.map_err(|errors| { - noirc_errors::reporter::report_all( - file_manager.as_file_map(), - &errors, - deny_warnings, - silence_warnings, - ) - })?; - - noirc_errors::reporter::report_all( - file_manager.as_file_map(), - &warnings, - deny_warnings, - silence_warnings, - ); - - Ok(t) -} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs index 130a07b5c90..2c4937b6f16 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -8,7 +8,7 @@ use fm::FileManager; use nargo::artifacts::debug::DebugArtifact; use nargo::constants::PROVER_INPUT_FILE; use nargo::errors::CompileError; -use nargo::ops::{compile_program, compile_program_with_debug_instrumenter}; +use nargo::ops::{compile_program, compile_program_with_debug_instrumenter, report_errors}; use nargo::package::Package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; @@ -22,7 +22,6 @@ use noirc_frontend::debug::DebugInstrumenter; use noirc_frontend::graph::CrateName; use noirc_frontend::hir::ParsedFiles; -use super::compile_cmd::report_errors; use super::fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}; use super::NargoConfig; use crate::backends::Backend; diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs index a3fcebab94f..85c0a4160a7 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -5,7 +5,7 @@ use clap::Args; use nargo::artifacts::debug::DebugArtifact; use nargo::constants::PROVER_INPUT_FILE; use nargo::errors::try_to_diagnose_runtime_error; -use nargo::ops::{compile_program, DefaultForeignCallExecutor}; +use nargo::ops::{compile_program, report_errors, DefaultForeignCallExecutor}; use nargo::package::Package; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; @@ -19,7 +19,6 @@ use noirc_frontend::graph::CrateName; use super::fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}; use super::NargoConfig; use crate::backends::Backend; -use crate::cli::compile_cmd::report_errors; use crate::errors::CliError; /// Executes a circuit to calculate its return value diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs index 96b24796a2b..044c2cb4ebb 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs @@ -1,4 +1,5 @@ use nargo::errors::CompileError; +use nargo::ops::report_errors; use noirc_errors::FileDiagnostic; use noirc_frontend::hir::ParsedFiles; use rayon::prelude::*; @@ -24,7 +25,6 @@ use crate::errors::CliError; use super::check_cmd::check_crate_and_report_errors; -use super::compile_cmd::report_errors; use super::fs::program::save_program_to_file; use super::NargoConfig; diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs index 0bd25a3a0ce..2e0ca5632f1 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs @@ -1,7 +1,7 @@ use std::{fs::DirEntry, path::Path}; use clap::Args; -use nargo::insert_all_files_for_workspace_into_file_manager; +use nargo::{insert_all_files_for_workspace_into_file_manager, ops::report_errors}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{file_manager_with_stdlib, NOIR_ARTIFACT_VERSION_STRING}; use noirc_errors::CustomDiagnostic; @@ -53,7 +53,7 @@ pub(crate) fn run(args: FormatCommand, config: NargoConfig) -> Result<(), CliErr }) .collect(); - let _ = super::compile_cmd::report_errors::<()>( + let _ = report_errors::<()>( Err(errors), &workspace_file_manager, false, diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs index ef0df0bf25b..300e1a35be2 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs @@ -6,7 +6,7 @@ use clap::Args; use iter_extended::vecmap; use nargo::{ artifacts::debug::DebugArtifact, insert_all_files_for_workspace_into_file_manager, - package::Package, parse_all, + ops::report_errors, package::Package, parse_all, }; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{ @@ -73,11 +73,18 @@ pub(crate) fn run( .compile_options .expression_width .unwrap_or_else(|| backend.get_backend_info_or_default()); - let (compiled_programs, compiled_contracts) = compile_workspace( + let compiled_workspace = compile_workspace( &workspace_file_manager, &parsed_files, &workspace, &args.compile_options, + ); + + let (compiled_programs, compiled_contracts) = report_errors( + compiled_workspace, + &workspace_file_manager, + args.compile_options.deny_warnings, + args.compile_options.silence_warnings, )?; let compiled_programs = vecmap(compiled_programs, |program| { diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/noir_template_files/contract.nr b/noir/noir-repo/tooling/nargo_cli/src/cli/noir_template_files/contract.nr index e126726393d..3cd3b5b3766 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/noir_template_files/contract.nr +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/noir_template_files/contract.nr @@ -1,5 +1,5 @@ contract Main { - internal fn double(x: Field) -> pub Field { x * 2 } + fn double(x: Field) -> pub Field { x * 2 } fn triple(x: Field) -> pub Field { x * 3 } fn quadruple(x: Field) -> pub Field { double(double(x)) } } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/prove_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/prove_cmd.rs index cc39b0535bc..f0a9b3185b9 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/prove_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/prove_cmd.rs @@ -1,6 +1,6 @@ use clap::Args; use nargo::constants::{PROVER_INPUT_FILE, VERIFIER_INPUT_FILE}; -use nargo::ops::compile_program; +use nargo::ops::{compile_program, report_errors}; use nargo::package::Package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; @@ -11,7 +11,6 @@ use noirc_driver::{ }; use noirc_frontend::graph::CrateName; -use super::compile_cmd::report_errors; use super::fs::{ inputs::{read_inputs_from_file, write_inputs_to_file}, proof::save_proof_to_dir, diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/verify_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/verify_cmd.rs index 66b88a22f2a..1063b50ab6c 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/verify_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/verify_cmd.rs @@ -1,11 +1,10 @@ -use super::compile_cmd::report_errors; use super::fs::{inputs::read_inputs_from_file, load_hex_data}; use super::NargoConfig; use crate::{backends::Backend, errors::CliError}; use clap::Args; use nargo::constants::{PROOF_EXT, VERIFIER_INPUT_FILE}; -use nargo::ops::compile_program; +use nargo::ops::{compile_program, report_errors}; use nargo::package::Package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/index.ts b/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/index.ts index d79b487c3cf..af03743eb2f 100644 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/index.ts +++ b/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/index.ts @@ -33,8 +33,18 @@ export class BarretenbergBackend implements Backend { /** @ignore */ async instantiate(): Promise { if (!this.api) { + if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) { + this.options.threads = navigator.hardwareConcurrency; + } else { + try { + const os = await import('os'); + this.options.threads = os.cpus().length; + } catch (e) { + console.log('Could not detect environment. Falling back to one thread.', e); + } + } const { Barretenberg, RawBuffer, Crs } = await import('@aztec/bb.js'); - const api = await Barretenberg.new({ threads: this.options.threads }); + const api = await Barretenberg.new(this.options); const [_exact, _total, subgroupSize] = await api.acirGetCircuitSizes(this.acirUncompressedBytecode); const crs = await Crs.new(subgroupSize + 1); diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/types.ts b/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/types.ts index 041e36fdf91..fac23030aad 100644 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/types.ts +++ b/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/types.ts @@ -5,4 +5,5 @@ export type BackendOptions = { /** @description Number of threads */ threads: number; + memory?: { maximum: number }; }; diff --git a/noir/noir-repo/yarn.lock b/noir/noir-repo/yarn.lock index ace7959279f..f5f3a29f08a 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -3951,6 +3951,13 @@ __metadata: languageName: node linkType: hard +"@import-maps/resolve@npm:^1.0.1": + version: 1.0.1 + resolution: "@import-maps/resolve@npm:1.0.1" + checksum: 17ee033e26a0fd82294de87eae76d32b553a130fdbf0fb8c70d39f2087a3e8a4a5908970a99aa32bd175153efe9b7dfee6b7f99df36f41abed08c1911dbdb19c + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -6677,6 +6684,20 @@ __metadata: languageName: node linkType: hard +"@web/dev-server-import-maps@npm:^0.2.0": + version: 0.2.0 + resolution: "@web/dev-server-import-maps@npm:0.2.0" + dependencies: + "@import-maps/resolve": ^1.0.1 + "@types/parse5": ^6.0.1 + "@web/dev-server-core": ^0.7.0 + "@web/parse5-utils": ^2.1.0 + parse5: ^6.0.1 + picomatch: ^2.2.2 + checksum: 15dabfa385f023bab70758b80cc09443455830799793c1a404a7230d90ebf60e40984a10d8a6ceea2afb8f057e90a9f7356a76f867d5e5a2eeacbc397e41535a + languageName: node + linkType: hard + "@web/dev-server-rollup@npm:^0.4.1": version: 0.4.1 resolution: "@web/dev-server-rollup@npm:0.4.1" @@ -13416,6 +13437,7 @@ __metadata: "@nomicfoundation/hardhat-chai-matchers": ^2.0.0 "@nomicfoundation/hardhat-ethers": ^3.0.0 "@web/dev-server-esbuild": ^0.3.6 + "@web/dev-server-import-maps": ^0.2.0 "@web/test-runner": ^0.15.3 "@web/test-runner-playwright": ^0.10.0 eslint: ^8.56.0 diff --git a/release-please-config.json b/release-please-config.json index 09f9e248f7e..7cf01cd694f 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -21,6 +21,14 @@ "component": "aztec-packages", "package-name": "aztec-packages" }, + "yarn-project/cli": { + "release-type": "node", + "component": "aztec-cli" + }, + "yarn-project/aztec": { + "release-type": "node", + "component": "aztec-package" + }, "barretenberg/ts": { "release-type": "node", "package-name": "barretenberg.js", @@ -34,10 +42,13 @@ } }, "plugins": [ + { + "type": "node-workspace" + }, { "type": "linked-versions", "groupName": "aztec-packages", - "components": ["barretenberg", "barretenberg.js", "aztec-packages"] + "components": ["barretenberg", "barretenberg.js", "aztec-packages", "aztec-package", "aztec-cli"] }, "sentence-case" ] diff --git a/yarn-project/.dockerignore b/yarn-project/.dockerignore deleted file mode 100644 index a3d11429d55..00000000000 --- a/yarn-project/.dockerignore +++ /dev/null @@ -1,17 +0,0 @@ -.yarn -!/.yarn/patches -!/.yarn/plugins -!/.yarn/releases -!/.yarn/sdks -!/.yarn/versions - -*/data* -**/dest -**/node_modules -**/Dockerfile* -**/*.tsbuildinfo - -./accounts/src/artifacts - -noir-contracts.js/src -noir-contracts.js/target diff --git a/yarn-project/Dockerfile b/yarn-project/Dockerfile index 9937be7aa9f..fcedd17ff18 100644 --- a/yarn-project/Dockerfile +++ b/yarn-project/Dockerfile @@ -1,13 +1,46 @@ -# This base dockerfile adds all the remaining source files and builds the project. -# See yarn-project-base/Dockerfile for why we have separate base Dockerfile. -FROM aztecprotocol/l1-contracts as contracts -FROM aztecprotocol/noir-projects as noir-projects -FROM aztecprotocol/boxes-files as boxes-files +FROM --platform=linux/amd64 aztecprotocol/bb.js as bb.js +FROM --platform=linux/amd64 aztecprotocol/noir-packages as noir-packages +FROM --platform=linux/amd64 aztecprotocol/l1-contracts as contracts +FROM --platform=linux/amd64 aztecprotocol/noir-projects as noir-projects -FROM aztecprotocol/yarn-project-base +FROM node:18.19.0 as builder +RUN apt update && apt install -y jq curl perl && rm -rf /var/lib/apt/lists/* && apt-get clean + +# Copy in portalled packages. +COPY --from=bb.js /usr/src/barretenberg/ts /usr/src/barretenberg/ts +COPY --from=noir-packages /usr/src/noir/packages /usr/src/noir/packages COPY --from=contracts /usr/src/l1-contracts /usr/src/l1-contracts COPY --from=noir-projects /usr/src/noir-projects /usr/src/noir-projects -COPY --from=boxes-files /usr/src/boxes /usr/src/boxes + +WORKDIR /usr/src/yarn-project COPY . . + +# 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 +# correctly for portalled packages, is to use --preserve-symlinks when running node. +# This does kind of work, but jest doesn't honor it correctly, so this seems like a neat workaround. +# Also, --preserve-symlinks causes duplication of portalled instances such as bb.js, and breaks the singleton logic +# by initialising the module more than once. So at present I don't see a viable alternative. +RUN ln -s /usr/src/yarn-project/node_modules /usr/src/node_modules + +# TODO: Replace puppeteer with puppeteer-core to avoid this. +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true + RUN ./bootstrap.sh +RUN yarn workspaces focus @aztec/cli @aztec/aztec --production && yarn cache clean + +# TODO: Use release-please to update package.json directly, and remove this! +# It's here to ensure the image rebuilds if the commit tag changes (as the content hash won't). +# ARG COMMIT_TAG="" +# RUN ./scripts/version_packages.sh + +# We no longer need nargo etc. +RUN rm -rf /usr/src/noir/noir-repo /usr/src/noir-projects /usr/src/l1-contracts + +# Create minimal size image. +FROM node:18.19.1-slim +ARG COMMIT_TAG="" +ENV COMMIT_TAG=$COMMIT_TAG +COPY --from=builder /usr/src /usr/src +WORKDIR /usr/src/yarn-project ENTRYPOINT ["yarn"] diff --git a/yarn-project/Dockerfile.prod b/yarn-project/Dockerfile.prod deleted file mode 100644 index bea13c08590..00000000000 --- a/yarn-project/Dockerfile.prod +++ /dev/null @@ -1,59 +0,0 @@ -# This productionifies the workspace, removing all developer dependencies and producing a final slim image from which -# we then generate downstream multiarch containers to execute the specific projects. -FROM aztecprotocol/yarn-project AS yarn-project - -# Need new arch specific image. -FROM node:18.19.0 AS builder -RUN apt update && apt install -y jq && rm -rf /var/lib/apt/lists/* && apt-get clean -COPY --from=yarn-project /usr/src /usr/src -WORKDIR /usr/src/yarn-project -# TODO: Use release-please to update package.json directly, and remove this! -ARG COMMIT_TAG="" -RUN ./scripts/version_packages.sh -# Productionify. See comment in yarn-project-base/Dockerfile. -RUN yarn workspaces focus @aztec/cli @aztec/aztec @aztec/aztec-faucet @aztec/aztec.js --production && yarn cache clean - -# We no longer need nargo. -RUN rm -rf /usr/src/noir/noir-repo/target - -# Create fresh minimal size image. -# Installs our specific version of node, stripping out the unnecessary. -# We could probably just apt install nodejs, but it's both a different version, and seemingly a bit slower. -# We could also use distroless, to get us about 20mb off, but meh. It's actually useful to shell into containers. -#FROM gcr.io/distroless/nodejs18-debian12 -FROM ubuntu:lunar -ARG COMMIT_TAG="" -ENV COMMIT_TAG=$COMMIT_TAG -# RUN apt update && apt install -y nodejs && rm -rf /var/lib/apt/lists/* && apt-get clean -RUN apt update && apt install -y curl && rm -rf /var/lib/apt/lists/* && apt-get clean -ENV NODE_VERSION=18.19.0 -RUN ARCH= && \ - dpkgArch="$(dpkg --print-architecture)" && \ - case "${dpkgArch##*-}" in \ - amd64) ARCH='x64';; \ - arm64) ARCH='arm64';; \ - *) echo "unsupported architecture"; exit 1 ;; \ - esac && \ - curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH.tar.gz" && \ - tar zxf "node-v$NODE_VERSION-linux-$ARCH.tar.gz" -C /usr --strip-components=1 --no-same-owner \ - --exclude "*/share/*" \ - --exclude "*/bin/corepack" \ - --exclude "*/bin/npx" \ - --exclude "*/bin/npm" \ - --exclude "*/corepack/*" \ - --exclude "*/npm/man/*" \ - --exclude "*/npm/docs/*" \ - --exclude "*/include/*" && \ - rm "node-v$NODE_VERSION-linux-$ARCH.tar.gz" && \ - node --version -# Yarn is used for unboxing. -ENV YARN_VERSION=1.22.19 -RUN curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" && \ - mkdir -p /opt && \ - tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ && \ - ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn && \ - ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg && \ - rm yarn-v$YARN_VERSION.tar.gz && \ - yarn --version -COPY --from=builder /usr/src /usr/src -ENTRYPOINT ["/usr/bin/node"] diff --git a/yarn-project/Dockerfile.test b/yarn-project/Dockerfile.test new file mode 100644 index 00000000000..6dfe4510760 --- /dev/null +++ b/yarn-project/Dockerfile.test @@ -0,0 +1,34 @@ +FROM --platform=linux/amd64 aztecprotocol/bb.js as bb.js +FROM --platform=linux/amd64 aztecprotocol/noir-packages as noir-packages +FROM --platform=linux/amd64 aztecprotocol/l1-contracts as contracts +FROM --platform=linux/amd64 aztecprotocol/noir-projects as noir-projects + +FROM node:18.19.0 as builder +RUN apt update && apt install -y jq curl perl && rm -rf /var/lib/apt/lists/* && apt-get clean + +# Copy in portalled packages. +COPY --from=bb.js /usr/src/barretenberg/ts /usr/src/barretenberg/ts +COPY --from=noir-packages /usr/src/noir/packages /usr/src/noir/packages +COPY --from=contracts /usr/src/l1-contracts /usr/src/l1-contracts +COPY --from=noir-projects /usr/src/noir-projects /usr/src/noir-projects + +WORKDIR /usr/src/yarn-project +COPY . . + +# 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 +# correctly for portalled packages, is to use --preserve-symlinks when running node. +# This does kind of work, but jest doesn't honor it correctly, so this seems like a neat workaround. +# Also, --preserve-symlinks causes duplication of portalled instances such as bb.js, and breaks the singleton logic +# by initialising the module more than once. So at present I don't see a viable alternative. +RUN ln -s /usr/src/yarn-project/node_modules /usr/src/node_modules + +# TODO: Replace puppeteer with puppeteer-core to avoid this. +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true + +RUN ./bootstrap.sh +RUN yarn prepare:check && yarn formatting && yarn test + +# Avoid pushing some huge container back to ecr. +FROM scratch +COPY --from=builder /usr/src/yarn-project/README.md /usr/src/yarn-project/README.md \ No newline at end of file diff --git a/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts index 62fc03b93c3..98261dfdd63 100644 --- a/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts +++ b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts @@ -216,7 +216,8 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch await expect(store.getPendingL1ToL2EntryKeys(1)).resolves.toEqual([message.entryKey!]); }); - it('returns messages ordered by fee', async () => { + // TODO(@spalladino): Fix and re-enable + it.skip('returns messages ordered by fee', async () => { const messages = Array.from({ length: 3 }, () => L1ToL2Message.random(Fr.random())); // add a duplicate message messages.push(messages[0]); 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 6b1bd3c47c5..7ea9fd320a7 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 @@ -53,9 +53,6 @@ export class BlockStore { }); block.getTxs().forEach((tx, i) => { - if (tx.txHash.isZero()) { - return; - } void this.#txIndex.set(tx.txHash.toString(), [block.number, i]); }); diff --git a/yarn-project/aztec-faucet/.dockerignore b/yarn-project/aztec-faucet/.dockerignore deleted file mode 100644 index 2b30eaf4896..00000000000 --- a/yarn-project/aztec-faucet/.dockerignore +++ /dev/null @@ -1,4 +0,0 @@ -data -dest -node_modules -Dockerfile \ No newline at end of file diff --git a/yarn-project/aztec-faucet/Dockerfile b/yarn-project/aztec-faucet/Dockerfile index 047f7cd0cac..f0043d91fca 100644 --- a/yarn-project/aztec-faucet/Dockerfile +++ b/yarn-project/aztec-faucet/Dockerfile @@ -1,4 +1,25 @@ -FROM aztecprotocol/yarn-project-prod +FROM --platform=linux/amd64 aztecprotocol/bb.js as bb.js +FROM --platform=linux/amd64 aztecprotocol/noir-packages as noir-packages + +FROM node:18.19.0 as builder + +# Copy in portalled packages. +# We need them because faucet is part of the workspace, and the workspace depends on them. 🤷‍♂️ +COPY --from=bb.js /usr/src/barretenberg/ts /usr/src/barretenberg/ts +COPY --from=noir-packages /usr/src/noir/packages /usr/src/noir/packages + +WORKDIR /usr/src/yarn-project +COPY . . +RUN yarn workspaces focus @aztec/aztec-faucet WORKDIR /usr/src/yarn-project/aztec-faucet +RUN yarn tsc -b +RUN yarn workspaces focus @aztec/aztec-faucet --production && yarn cache clean + +# Create minimal image. +FROM ubuntu:lunar +ARG COMMIT_TAG="" +ENV COMMIT_TAG=$COMMIT_TAG +RUN apt update && apt install -y nodejs && rm -rf /var/lib/apt/lists/* && apt-get clean +COPY --from=builder /usr/src/yarn-project /usr/src/yarn-project ENTRYPOINT ["node", "--no-warnings", "/usr/src/yarn-project/aztec-faucet/dest/bin/index.js"] EXPOSE 8080 diff --git a/yarn-project/aztec/Dockerfile b/yarn-project/aztec/Dockerfile index 13b2000744d..ae996a59dfb 100644 --- a/yarn-project/aztec/Dockerfile +++ b/yarn-project/aztec/Dockerfile @@ -1,7 +1,7 @@ -FROM aztecprotocol/yarn-project-prod AS yarn-project-prod +FROM aztecprotocol/yarn-project AS yarn-project ENTRYPOINT ["node", "--no-warnings", "/usr/src/yarn-project/aztec/dest/bin/index.js"] EXPOSE 8080 -# The version has been updated in yarn-project-prod. +# The version has been updated in yarn-project. # Adding COMMIT_TAG here to rebuild versioned image. ARG COMMIT_TAG="" \ No newline at end of file diff --git a/yarn-project/circuit-types/src/tx_effect.ts b/yarn-project/circuit-types/src/tx_effect.ts index f5dff9d4778..c67708dee9b 100644 --- a/yarn-project/circuit-types/src/tx_effect.ts +++ b/yarn-project/circuit-types/src/tx_effect.ts @@ -115,7 +115,6 @@ export class TxEffect { publicDataUpdateRequestsBuffer, this.contractLeaves[0].toBuffer(), this.contractData[0].contractAddress.toBuffer(), - // TODO(#3938): make portal address 20 bytes here when updating the hashing this.contractData[0].portalContractAddress.toBuffer32(), encryptedLogsHashKernel0, unencryptedLogsHashKernel0, diff --git a/yarn-project/cli/Dockerfile b/yarn-project/cli/Dockerfile index b782d46e801..c69606b278c 100644 --- a/yarn-project/cli/Dockerfile +++ b/yarn-project/cli/Dockerfile @@ -1,7 +1,7 @@ -FROM aztecprotocol/yarn-project-prod AS yarn-project-prod +FROM aztecprotocol/yarn-project AS yarn-project ENTRYPOINT ["node", "--no-warnings", "/usr/src/yarn-project/cli/dest/bin/index.js"] -# The version has been updated in yarn-project-prod. +# The version has been updated in yarn-project. # Adding COMMIT_TAG here to rebuild versioned image. ARG COMMIT_TAG="" diff --git a/yarn-project/deploy_npm.sh b/yarn-project/deploy_npm.sh index 04bf9c6e4d3..65ea41a7edd 100755 --- a/yarn-project/deploy_npm.sh +++ b/yarn-project/deploy_npm.sh @@ -8,7 +8,7 @@ if [ -z "$COMMIT_TAG" ]; then fi retry ecr_login -extract_repo yarn-project-prod /usr/src project +extract_repo yarn-project /usr/src project cd project/src/yarn-project echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >.npmrc diff --git a/yarn-project/end-to-end/Dockerfile b/yarn-project/end-to-end/Dockerfile index 49f5a14711d..e1934cff7b2 100644 --- a/yarn-project/end-to-end/Dockerfile +++ b/yarn-project/end-to-end/Dockerfile @@ -1,29 +1,49 @@ -FROM aztecprotocol/yarn-project AS builder +FROM --platform=linux/amd64 aztecprotocol/bb.js as bb.js +FROM --platform=linux/amd64 aztecprotocol/noir-packages as noir-packages +FROM --platform=linux/amd64 aztecprotocol/l1-contracts as contracts +FROM --platform=linux/amd64 aztecprotocol/noir-projects as noir-projects + +FROM node:18.19.0 as builder +RUN apt update && apt install -y jq curl perl && rm -rf /var/lib/apt/lists/* && apt-get clean + +# Copy in portalled packages. +COPY --from=bb.js /usr/src/barretenberg/ts /usr/src/barretenberg/ts +COPY --from=noir-packages /usr/src/noir/packages /usr/src/noir/packages +COPY --from=contracts /usr/src/l1-contracts /usr/src/l1-contracts +COPY --from=noir-projects /usr/src/noir-projects /usr/src/noir-projects + +WORKDIR /usr/src/yarn-project +COPY . . + +# 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 +# correctly for portalled packages, is to use --preserve-symlinks when running node. +# This does kind of work, but jest doesn't honor it correctly, so this seems like a neat workaround. +# Also, --preserve-symlinks causes duplication of portalled instances such as bb.js, and breaks the singleton logic +# by initialising the module more than once. So at present I don't see a viable alternative. +RUN ln -s /usr/src/yarn-project/node_modules /usr/src/node_modules + +# TODO: Replace puppeteer with puppeteer-core to avoid this. +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true + +RUN ./bootstrap.sh # Build web bundle for browser tests -WORKDIR /usr/src/yarn-project/end-to-end -RUN yarn build:web - -# Productionify. See comment in yarn-project-base/Dockerfile. -RUN yarn workspaces focus --production && yarn cache clean - -# Create final, minimal size image. -# TODO: Not very minimal as chromium adds about 500MB of bloat :/ Separate or install at test runtime? -FROM node:18.19.0-alpine -RUN apk update && apk add --no-cache \ - jq \ - bash \ - chromium \ - chromium-chromedriver \ - nss \ - freetype \ - harfbuzz \ - ca-certificates \ - ttf-freefont - -ENV CHROME_BIN="/usr/bin/chromium-browser" PUPPETEER_SKIP_CHROMIUM_DOWNLOAD="true" -COPY --from=builder /usr/src /usr/src +RUN yarn workspace @aztec/end-to-end run build:web +RUN yarn workspaces focus @aztec/end-to-end --production && yarn cache clean -WORKDIR /usr/src/yarn-project/end-to-end +# We no longer need nargo etc. +RUN rm -rf /usr/src/noir/noir-repo /usr/src/noir-projects /usr/src/l1-contracts -ENTRYPOINT ["yarn", "test"] +# Create minimal image. +FROM node:18.19.1-slim +RUN apt-get update && apt-get install jq gnupg wget -y && \ + wget --quiet --output-document=- https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor > /etc/apt/trusted.gpg.d/google-archive.gpg && \ + sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' && \ + apt-get update && \ + apt-get install google-chrome-stable -y --no-install-recommends && \ + rm -rf /var/lib/apt/lists/* +ENV CHROME_BIN="/usr/bin/google-chrome-stable" +COPY --from=builder /usr/src /usr/src +WORKDIR /usr/src/yarn-project/end-to-end +ENTRYPOINT ["yarn", "test"] \ No newline at end of file diff --git a/yarn-project/end-to-end/scripts/docker-compose-browser.yml b/yarn-project/end-to-end/scripts/docker-compose-browser.yml deleted file mode 100644 index f859de7de39..00000000000 --- a/yarn-project/end-to-end/scripts/docker-compose-browser.yml +++ /dev/null @@ -1,45 +0,0 @@ -version: '3' -services: - fork: - image: ghcr.io/foundry-rs/foundry:nightly-a44aa13cfc23491ba32aaedc093e9488c1a6db43 - entrypoint: > - sh -c ' - if [ -n "$FORK_BLOCK_NUMBER" ] && [ -n "$FORK_URL" ]; then - exec anvil -p 8545 --host 0.0.0.0 --chain-id 31337 --fork-url "$FORK_URL" --fork-block-number "$FORK_BLOCK_NUMBER" - else - exec anvil -p 8545 --host 0.0.0.0 --chain-id 31337 - fi' - ports: - - '8545:8545' - - sandbox: - image: aztecprotocol/aztec:latest - environment: - DEBUG: 'aztec:*' - DEBUG_COLORS: 1 - ETHEREUM_HOST: http://fork:8545 - CHAIN_ID: 31337 - ARCHIVER_POLLING_INTERVAL_MS: 50 - P2P_BLOCK_CHECK_INTERVAL_MS: 50 - SEQ_TX_POLLING_INTERVAL_MS: 50 - WS_BLOCK_CHECK_INTERVAL_MS: 50 - PXE_BLOCK_POLLING_INTERVAL_MS: 50 - ARCHIVER_VIEM_POLLING_INTERVAL_MS: 500 - ports: - - '8080:8080' - - end-to-end: - image: aztecprotocol/end-to-end:latest - environment: - BENCHMARK: 'true' - DEBUG: ${DEBUG:-'aztec:*'} - DEBUG_COLORS: 1 - ETHEREUM_HOST: http://fork:8545 - CHAIN_ID: 31337 - PXE_URL: http://sandbox:8080 - entrypoint: ['./scripts/start_e2e_ci_browser.sh', './src/e2e_aztec_js_browser.test.ts'] - volumes: - - ../log:/usr/src/yarn-project/end-to-end/log:rw - depends_on: - - sandbox - - fork diff --git a/yarn-project/end-to-end/scripts/start_e2e_ci_browser.sh b/yarn-project/end-to-end/scripts/start_e2e_ci_browser.sh deleted file mode 100755 index f73db85b3fe..00000000000 --- a/yarn-project/end-to-end/scripts/start_e2e_ci_browser.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -TEST=${1:-./src/e2e_aztec_js_browser.test.ts} - -apk add dbus - -# Create dbus dirs -mkdir -p /var/run/dbus - -# Change ownership and permissions if necessary -chown -R root:root /var/run/dbus -chmod -R 755 /var/run/dbus - -dbus-daemon --system --nofork & -yarn test $TEST diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts index 5dd25d224fc..99ff6665cd0 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts @@ -24,7 +24,7 @@ import { deployInstance, registerContractClass, } from '@aztec/aztec.js/deployment'; -import { ContractClassIdPreimage, Point, PublicKey } from '@aztec/circuits.js'; +import { ContractClassIdPreimage, Point } from '@aztec/circuits.js'; import { siloNullifier } from '@aztec/circuits.js/hash'; import { FunctionSelector, FunctionType } from '@aztec/foundation/abi'; import { StatefulTestContract } from '@aztec/noir-contracts.js'; @@ -258,10 +258,6 @@ describe('e2e_deploy_contract', () => { /nullifier witness not found/i, ); }); - - it('refuses to call a public function that requires initialization', async () => { - // TODO(@spalladino) - }); }); describe('registering a contract class', () => { @@ -309,16 +305,14 @@ describe('e2e_deploy_contract', () => { describe(`deploying a contract instance ${how}`, () => { let instance: ContractInstanceWithAddress; let initArgs: StatefulContractCtorArgs; - let publicKey: PublicKey; let contract: StatefulTestContract; - beforeAll(async () => { - initArgs = [accounts[0].address, 42]; + const deployInstance = async () => { + const initArgs = [accounts[0].address, 42] as StatefulContractCtorArgs; const salt = Fr.random(); const portalAddress = EthAddress.random(); - publicKey = Point.random(); - - instance = getContractInstanceFromDeployParams(artifact, initArgs, salt, publicKey, portalAddress); + const publicKey = Point.random(); + const instance = getContractInstanceFromDeployParams(artifact, initArgs, salt, publicKey, portalAddress); const { address, contractClassId } = instance; logger(`Deploying contract instance at ${address.toString()} class id ${contractClassId.toString()}`); await deployFn(instance); @@ -338,7 +332,12 @@ describe('e2e_deploy_contract', () => { publicKey, }); expect(registered.address).toEqual(instance.address); - contract = await StatefulTestContract.at(instance.address, wallet); + const contract = await StatefulTestContract.at(instance.address, wallet); + return { contract, initArgs, instance, publicKey }; + }; + + beforeAll(async () => { + ({ instance, initArgs, contract } = await deployInstance()); }, 60_000); it('stores contract instance in the aztec node', async () => { @@ -381,6 +380,32 @@ describe('e2e_deploy_contract', () => { const stored = await contract.methods.get_public_value(whom).view(); expect(stored).toEqual(10n); }, 30_000); + + it('refuses to reinitialize the contract', async () => { + await expect( + contract.methods + .public_constructor(...initArgs) + .send({ skipPublicSimulation: true }) + .wait(), + ).rejects.toThrow(/dropped/i); + }, 30_000); + + it('initializes a new instance of the contract via a public function', async () => { + const { contract, initArgs } = await deployInstance(); + const whom = initArgs[0]; + logger.info(`Initializing contract at ${contract.address} via a public function`); + await contract.methods + .public_constructor(...initArgs) + .send({ skipPublicSimulation: true }) + .wait(); + expect(await contract.methods.get_public_value(whom).view()).toEqual(42n); + logger.info(`Calling a public function that requires initialization on ${contract.address}`); + await contract.methods.increment_public_value(whom, 10).send().wait(); + expect(await contract.methods.get_public_value(whom).view()).toEqual(52n); + logger.info(`Calling a private function that requires initialization on ${contract.address}`); + await contract.methods.create_note(whom, 10).send().wait(); + expect(await contract.methods.summed_values(whom).view()).toEqual(10n); + }, 90_000); }); testDeployingAnInstance('from a wallet', async instance => { diff --git a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts index 2fec187d43d..9a520f6a210 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts @@ -68,9 +68,9 @@ describe('e2e_nested_contract', () => { it('fails simulation if calling a function not allowed to be called externally', async () => { await expect( parentContract.methods - .entryPoint(childContract.address, childContract.methods.valueInternal.selector) + .entryPoint(childContract.address, (childContract.methods as any).valueInternal.selector) .simulate(), - ).rejects.toThrowError('Assertion failed: Sender must be this contract'); + ).rejects.toThrow(/Assertion failed: Function valueInternal can only be called internally/); }, 100_000); it('performs public nested calls', async () => { @@ -91,9 +91,9 @@ describe('e2e_nested_contract', () => { it('fails simulation if calling a public function not allowed to be called externally', async () => { await expect( parentContract.methods - .enqueueCallToChild(childContract.address, childContract.methods.pubIncValueInternal.selector, 42n) + .enqueueCallToChild(childContract.address, (childContract.methods as any).pubIncValueInternal.selector, 42n) .simulate(), - ).rejects.toThrowError('Assertion failed: Sender must be this contract'); + ).rejects.toThrow(/Assertion failed: Function pubIncValueInternal can only be called internally/); }, 100_000); it('enqueues multiple public calls', async () => { diff --git a/yarn-project/end-to-end/src/e2e_static_calls.test.ts b/yarn-project/end-to-end/src/e2e_static_calls.test.ts index 1d635f51f8d..974d3ce3f45 100644 --- a/yarn-project/end-to-end/src/e2e_static_calls.test.ts +++ b/yarn-project/end-to-end/src/e2e_static_calls.test.ts @@ -31,16 +31,40 @@ describe('e2e_static_calls', () => { .wait(); }, 100_000); + it('performs legal (nested) private to private static calls', async () => { + await parentContract.methods + .privateStaticCallNested(childContract.address, childContract.methods.privateGetValue.selector, [ + 42n, + wallet.getCompleteAddress().address, + ]) + .send() + .wait(); + }, 100_000); + it('performs legal public to public static calls', async () => { await parentContract.methods - .enqueueStaticCallToPubFunction(childContract.address, childContract.methods.pubGetValue.selector, [42n]) + .publicStaticCall(childContract.address, childContract.methods.pubGetValue.selector, [42n]) + .send() + .wait(); + }, 100_000); + + it('performs legal (nested) public to public static calls', async () => { + await parentContract.methods + .publicNestedStaticCall(childContract.address, childContract.methods.pubGetValue.selector, [42n]) .send() .wait(); }, 100_000); it('performs legal enqueued public static calls', async () => { await parentContract.methods - .publicStaticCall(childContract.address, childContract.methods.pubGetValue.selector, [42n]) + .enqueueStaticCallToPubFunction(childContract.address, childContract.methods.pubGetValue.selector, [42n]) + .send() + .wait(); + }, 100_000); + + it('performs legal (nested) enqueued public static calls', async () => { + await parentContract.methods + .enqueueStaticNestedCallToPubFunction(childContract.address, childContract.methods.pubGetValue.selector, [42n]) .send() .wait(); }, 100_000); @@ -57,6 +81,18 @@ describe('e2e_static_calls', () => { ).rejects.toThrow('Static call cannot create new notes, emit L2->L1 messages or generate logs'); }, 100_000); + it('fails when performing illegal (nested) private to private static calls', async () => { + await expect( + parentContract.methods + .privateStaticCallNested(childContract.address, childContract.methods.privateSetValue.selector, [ + 42n, + wallet.getCompleteAddress().address, + ]) + .send() + .wait(), + ).rejects.toThrow('Static call cannot create new notes, emit L2->L1 messages or generate logs'); + }, 100_000); + it('fails when performing illegal public to public static calls', async () => { await expect( parentContract.methods @@ -66,6 +102,15 @@ describe('e2e_static_calls', () => { ).rejects.toThrow('Static call cannot update the state, emit L2->L1 messages or generate logs'); }, 100_000); + it('fails when performing illegal (nested) public to public static calls', async () => { + await expect( + parentContract.methods + .publicNestedStaticCall(childContract.address, childContract.methods.pubSetValue.selector, [42n]) + .send() + .wait(), + ).rejects.toThrow('Static call cannot update the state, emit L2->L1 messages or generate logs'); + }, 100_000); + it('fails when performing illegal enqueued public static calls', async () => { await expect( parentContract.methods @@ -74,5 +119,16 @@ describe('e2e_static_calls', () => { .wait(), ).rejects.toThrow('Static call cannot update the state, emit L2->L1 messages or generate logs'); }, 100_000); + + it('fails when performing illegal (nested) enqueued public static calls', async () => { + await expect( + parentContract.methods + .enqueueStaticNestedCallToPubFunction(childContract.address, childContract.methods.pubSetValue.selector, [ + 42n, + ]) + .send() + .wait(), + ).rejects.toThrow('Static call cannot update the state, emit L2->L1 messages or generate logs'); + }, 100_000); }); }); diff --git a/yarn-project/foundation/src/eth-address/index.ts b/yarn-project/foundation/src/eth-address/index.ts index 0e69bf445d8..f61c1f75b4f 100644 --- a/yarn-project/foundation/src/eth-address/index.ts +++ b/yarn-project/foundation/src/eth-address/index.ts @@ -190,7 +190,6 @@ export class EthAddress { * * @returns A 32-byte Buffer containing the padded Ethereum address. */ - // TODO(#3938): nuke this public toBuffer32() { const buffer = Buffer.alloc(32); this.buffer.copy(buffer, 12); diff --git a/yarn-project/merkle-tree/src/index.ts b/yarn-project/merkle-tree/src/index.ts index 45ae1771bbf..ca08070bc8e 100644 --- a/yarn-project/merkle-tree/src/index.ts +++ b/yarn-project/merkle-tree/src/index.ts @@ -5,6 +5,7 @@ export * from './interfaces/update_only_tree.js'; export * from './pedersen.js'; export * from './sparse_tree/sparse_tree.js'; export { StandardIndexedTree } from './standard_indexed_tree/standard_indexed_tree.js'; +export { StandardIndexedTreeWithAppend } from './standard_indexed_tree/test/standard_indexed_tree_with_append.js'; export * from './standard_tree/standard_tree.js'; export { INITIAL_LEAF, getTreeMeta } from './tree_base.js'; export { newTree } from './new_tree.js'; diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts index 60ff4a9ecca..a772f1fc5fb 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts @@ -1,4 +1,4 @@ -import { StandardIndexedTree } from '../../index.js'; +import { StandardIndexedTree } from '../standard_indexed_tree.js'; /** * A testing utility which is here to store the original implementation of StandardIndexedTree.appendLeaves method diff --git a/yarn-project/package.json b/yarn-project/package.json index ba737a9dd77..c2cb5c60bc3 100644 --- a/yarn-project/package.json +++ b/yarn-project/package.json @@ -3,14 +3,14 @@ "packageManager": "yarn@3.6.3", "private": true, "scripts": { - "prepare": "node ./yarn-project-base/scripts/update_package_jsons.mjs && yarn workspaces foreach run prepare && workspaces-to-typescript-project-references --tsconfigPath tsconfig.json && prettier -w */tsconfig.json", - "prepare:check": "node ./yarn-project-base/scripts/update_package_jsons.mjs --check && workspaces-to-typescript-project-references --check --tsconfigPath tsconfig.json", + "prepare": "node ./scripts/update_package_jsons.mjs && yarn workspaces foreach run prepare && workspaces-to-typescript-project-references --tsconfigPath tsconfig.json && prettier -w */tsconfig.json", + "prepare:check": "node ./scripts/update_package_jsons.mjs --check && workspaces-to-typescript-project-references --check --tsconfigPath tsconfig.json", "docs": "typedoc --out docs/dist && cd docs && yarn serve", - "formatting": "FORCE_COLOR=true yarn workspaces foreach -p -j unlimited -v run formatting", + "formatting": "FORCE_COLOR=true yarn workspaces foreach -p -v run formatting", "formatting:fix": "FORCE_COLOR=true yarn workspaces foreach -p -v run formatting:fix", "lint": "yarn eslint --cache --ignore-pattern l1-artifacts .", "format": "yarn prettier --cache -w .", - "test": "FORCE_COLOR=true yarn workspaces foreach --exclude @aztec/aztec3-packages --exclude @aztec/end-to-end -p -j ${JOBS:-unlimited} -v run test", + "test": "FORCE_COLOR=true yarn workspaces foreach --exclude @aztec/aztec3-packages --exclude @aztec/end-to-end -p -v run test", "build": "FORCE_COLOR=true yarn workspaces foreach --parallel --topological-dev --verbose --exclude @aztec/aztec3-packages --exclude @aztec/docs run build", "build:fast": "yarn generate && tsc -b", "build:dev": "tsc -b tsconfig.json --watch", diff --git a/yarn-project/yarn-project-base/scripts/update_package_jsons.mjs b/yarn-project/scripts/update_package_jsons.mjs similarity index 100% rename from yarn-project/yarn-project-base/scripts/update_package_jsons.mjs rename to yarn-project/scripts/update_package_jsons.mjs 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 0e0230791cf..6e939636be1 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 @@ -107,7 +107,6 @@ export class SoloBlockBuilder implements BlockBuilder { // Collect all new nullifiers, commitments, and contracts from all txs in this block const txEffects: TxEffect[] = txs.map( tx => - // TODO(#4720): Combined data should most likely contain the tx effect directly new TxEffect( tx.data.combinedData.newNoteHashes.map((c: SideEffect) => c.value) as Tuple< Fr, diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index ebeb17c3936..cc756380c20 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -343,6 +343,8 @@ export class ClientExecutionContext extends ViewDataOracle { `Calling private function ${this.contractAddress}:${functionSelector} from ${this.callContext.storageContractAddress}`, ); + isStaticCall = isStaticCall || this.callContext.isStaticCall; + const targetArtifact = await this.db.getFunctionArtifact(targetContractAddress, functionSelector); const targetFunctionData = FunctionData.fromAbi(targetArtifact); @@ -412,6 +414,8 @@ export class ClientExecutionContext extends ViewDataOracle { isStaticCall: boolean, isDelegateCall: boolean, ): Promise { + isStaticCall = isStaticCall || this.callContext.isStaticCall; + const targetArtifact = await this.db.getFunctionArtifact(targetContractAddress, functionSelector); const derivedCallContext = await this.deriveCallContext( targetContractAddress, diff --git a/yarn-project/simulator/src/public/index.test.ts b/yarn-project/simulator/src/public/index.test.ts index 810c0745c01..3c8396547c9 100644 --- a/yarn-project/simulator/src/public/index.test.ts +++ b/yarn-project/simulator/src/public/index.test.ts @@ -1,4 +1,4 @@ -import { L1ToL2Message, SiblingPath } from '@aztec/circuit-types'; +import { L1ToL2Message, NullifierMembershipWitness, SiblingPath } from '@aztec/circuit-types'; import { AppendOnlyTreeSnapshot, CallContext, @@ -7,13 +7,19 @@ import { Header, L1_TO_L2_MSG_TREE_HEIGHT, L2ToL1Message, + NULLIFIER_TREE_HEIGHT, + NullifierLeaf, + NullifierLeafPreimage, } from '@aztec/circuits.js'; +import { siloNullifier } from '@aztec/circuits.js/hash'; import { makeHeader } from '@aztec/circuits.js/testing'; import { FunctionArtifact, FunctionSelector, encodeArguments } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { pedersenHash } from '@aztec/foundation/crypto'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; +import { openTmpStore } from '@aztec/kv-store/utils'; +import { Pedersen, StandardIndexedTreeWithAppend } from '@aztec/merkle-tree'; import { ChildContractArtifact } from '@aztec/noir-contracts.js/Child'; import { ParentContractArtifact } from '@aztec/noir-contracts.js/Parent'; import { TestContractArtifact } from '@aztec/noir-contracts.js/Test'; @@ -50,16 +56,46 @@ describe('ACIR public execution simulator', () => { executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); }, 10000); + const mockInitializationNullifierCallback = async (contractAddress: AztecAddress) => { + // We create a nullifier tree just to get the membership witness for the token contract + // initialization nullifier, which is checked by all of the Token contract functions. + const nullifierTree = new StandardIndexedTreeWithAppend( + openTmpStore(), + new Pedersen(), + 'nullifier', + NULLIFIER_TREE_HEIGHT, + 0n, + NullifierLeafPreimage, + NullifierLeaf, + ); + await nullifierTree.init(1); + const initializationNullifier = siloNullifier(contractAddress, contractAddress.toField()); + await nullifierTree.appendLeaves([initializationNullifier.toBuffer()]); + header.state.partial.nullifierTree.root = Fr.fromBuffer(nullifierTree.getRoot(true)); + commitmentsDb.getNullifierMembershipWitnessAtLatestBlock.mockImplementation(async nullifier => { + if (nullifier.equals(initializationNullifier)) { + const index = 1n; + const preimage = nullifierTree.getLatestLeafPreimageCopy(index, true); + const siblingPath = await nullifierTree.getSiblingPath(index, true); + return new NullifierMembershipWitness(index, preimage as NullifierLeafPreimage, siblingPath); + } else { + throw new Error(`Unexpected nullifier witness request for ${nullifier}`); + } + }); + }; + describe('Token contract', () => { let recipient: AztecAddress; + let contractAddress: AztecAddress; - beforeEach(() => { + beforeEach(async () => { recipient = AztecAddress.random(); + contractAddress = AztecAddress.random(); + await mockInitializationNullifierCallback(contractAddress); }); describe('mint', () => { it('should run the mint_public function', async () => { - const contractAddress = AztecAddress.random(); const mintArtifact = TokenContractArtifact.functions.find(f => f.name === 'mint_public')!; const functionData = FunctionData.fromAbi(mintArtifact); @@ -126,7 +162,6 @@ describe('ACIR public execution simulator', () => { }); describe('transfer', () => { - let contractAddress: AztecAddress; let transferArtifact: FunctionArtifact; let functionData: FunctionData; let args: Fr[]; @@ -137,7 +172,6 @@ describe('ACIR public execution simulator', () => { let execution: PublicExecution; beforeEach(() => { - contractAddress = AztecAddress.random(); transferArtifact = TokenContractArtifact.functions.find(f => f.name === 'transfer_public')!; functionData = new FunctionData(FunctionSelector.empty(), false, false, false); sender = AztecAddress.random(); @@ -295,8 +329,9 @@ describe('ACIR public execution simulator', () => { let amount: Fr; let params: Fr[]; - beforeEach(() => { + beforeEach(async () => { contractAddress = AztecAddress.random(); + await mockInitializationNullifierCallback(contractAddress); functionData = new FunctionData(FunctionSelector.empty(), false, false, false); amount = new Fr(1); params = [amount, new Fr(1)]; diff --git a/yarn-project/simulator/src/public/public_execution_context.ts b/yarn-project/simulator/src/public/public_execution_context.ts index 70a8f6ceafd..3b53fd2490b 100644 --- a/yarn-project/simulator/src/public/public_execution_context.ts +++ b/yarn-project/simulator/src/public/public_execution_context.ts @@ -163,6 +163,8 @@ export class PublicExecutionContext extends TypedOracle { isStaticCall: boolean, isDelegateCall: boolean, ) { + isStaticCall = isStaticCall || this.execution.callContext.isStaticCall; + const args = this.packedArgsCache.unpack(argsHash); this.log(`Public function call: addr=${targetContractAddress} selector=${functionSelector} args=${args.join(',')}`); diff --git a/yarn-project/yarn-project-base/Dockerfile b/yarn-project/yarn-project-base/Dockerfile deleted file mode 100644 index 62c4b55fb42..00000000000 --- a/yarn-project/yarn-project-base/Dockerfile +++ /dev/null @@ -1,82 +0,0 @@ -# This base Dockerfile is for: -# - Caching the workspace dependencies. -# - Running workspace checks (package.json inheritance, tsconfig.json project references). -# - Bringing in any upstream resources such as L1 contract artifacts and bb.js. -# - Performing any code generation that doesn't require the workspace code to do so (generate L1 artifacts). -# -# When installing workspace dependencies, there are issues with doing this naively: -# - We only ever want to re-download and install workspace dependencies in the event that they change. -# If a developer only changes some code, we want this step of the build to become a noop and to reuse existing image. -# Dockerfile.dockerignore is tailored to specifically only bring in files needed to install the dependencies. -# NOTE: For this dockerignore file to be used, you MUST be using BUILDKIT in more recent versions of Docker. -# Best to make sure you're on docker >= 24. On mainframe run `restart_docker`, it should auto-upgrade. -# - We want to disable yarn from accessing the net after initial package download as it prevents a class of build bugs. -# - We want to prune dev dependencies as this can significantly reduce the final container size even further. -# Yarn devs won't provide the ability to prune dev dependencies from the local project cache: -# https://github.com/yarnpkg/berry/issues/1789 -# This means we need a global cache, so we can clean the cache and reinstall prod modules without re-downloading. -# - The default global cache and local cache are simply copies, not hardlinks, thus doubling the storage of modules. -# To work around, we will construct a global cache from the local cache using hard links (requires a hacky rename). -# We do this in the same layer the original file is created, otherwise overlayfs creates a copy anyway. -# At time of writing this shaves off around 150MB. Not a big deal but a harmless trick for now. -# -# So, here in the base image we yarn install. -# - /root/.yarn/berry/cache (global cache) is populated with zipped packages. -# - /usr/src/yarn-project/.yarn/cache (project local cache) is populated with copy of zipped packages. -# - Packages are unzipped into various node_modules folders. -# - We then erase the global cache, and recreate each file as a hard link, reducing the zipped package storage by half. -# -# That's all we want to do here r.e. dependency installation. In yarn-project we will: -# - Copy in the rest of the workspace files. -# - Perform any code generation steps that require the workspace code (generate L2 contract wrappers). -# - Build all the TypeScript. -# -# When we build a downstream docker image, we: -# - FROM yarn-project as builder, to get the entire built project. -# - WORKDIR into the relevant project. -# - Do any project specific build work. -# - Do a `yarn workspaces focus --production` to install production dependencies from the global. -# - Erase the local cache with a `yarn cache clean`. -# - Destroy the yarn-project /src folders to save additional space. -# - The above can be done with: -# RUN yarn workspaces focus --production && yarn cache clean && rm -rf ../**/src -# - Create final slim image by copying needed dirs into a fresh image. -# -FROM aztecprotocol/bb.js as bb.js -FROM aztecprotocol/noir-packages as noir-packages - -FROM node:18.19.0 -RUN apt update && apt install -y jq curl perl && rm -rf /var/lib/apt/lists/* && apt-get clean - -# Copy in portalled packages. -COPY --from=bb.js /usr/src/barretenberg/ts /usr/src/barretenberg/ts -COPY --from=noir-packages /usr/src/noir/packages /usr/src/noir/packages - -# 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 -# correctly for portalled packages, is to use --preserve-symlinks when running node. -# This does kind of work, but jest doesn't honor it correctly, so this seems like a neat workaround. -# Also, --preserve-symlinks causes duplication of portalled instances such as bb.js, and breaks the singleton logic -# by initialising the module more than once. So at present I don't see a viable alternative. -RUN ln -s /usr/src/yarn-project/node_modules /usr/src/node_modules - -WORKDIR /usr/src/yarn-project -# The dockerignore file ensures the context only contains package.json and tsconfig.json files. -COPY . . - -# List all included files and hash for debugging. -RUN echo "Context files: " && find . -type f | sort && \ - echo -n "Context hash: " && find . -type f -print0 | sort -z | xargs -0 sha256sum | sha256sum - -# Install packages and rebuild the global cache with hard links. -# TODO: Puppeteer is adding ~300MB to this image due to chrome download (part of e2e). -# Switch to using puppeteer-core then it won't download chrome. For now just erase. -RUN yarn --immutable && rm -rf /root/.cache/puppeteer && /bin/bash -c '\ - rm -rf /root/.yarn/berry/cache/* && \ - cd .yarn/cache && \ - for F in *; do \ - [[ $F =~ (.*-) ]] && ln $F /root/.yarn/berry/cache/${BASH_REMATCH[1]}8.zip; \ - done' - -# Check package.json inheritance and tsconfig project references. -RUN yarn prepare:check \ No newline at end of file diff --git a/yarn-project/yarn-project-base/Dockerfile.dockerignore b/yarn-project/yarn-project-base/Dockerfile.dockerignore deleted file mode 100644 index b388fe69533..00000000000 --- a/yarn-project/yarn-project-base/Dockerfile.dockerignore +++ /dev/null @@ -1,29 +0,0 @@ -# The aim here is to not have to maintain a list of projects in either the Dockerfile or this ignore file. -# We need to be careful not to exclude the project directories, as you cannot then use a wildcard in a negation -# to re-include needed files. Here we use */* which doesn't exclude the directories themselves, but rather -# the files within those directories. As we haven't excluded anything in the root, we manually exclude root files. -*/* -.* -README.md -bootstrap.sh -Dockerfile* -*.tsbuildinfo -node_modules - -# Unexclude package.json and yarn.lock files, for detecting any dependency changes. -!*/package.json -!*/package.*.json -!yarn.lock - -# Unexclude parts of yarn related config as this also affects how dependencies are installed. -!.yarnrc.yml -!.yarn/plugins -!.yarn/releases -!.yarn/patches - -# Unexclude tsconfig files for running project reference checks. -!*/tsconfig.json - -# Unexclude scripts we use in the Dockerfile. -!yarn-project-base/scripts -!l1-artifacts/scripts \ No newline at end of file diff --git a/yarn-project/yarn-project-base/Dockerfile.dockerignore.v24 b/yarn-project/yarn-project-base/Dockerfile.dockerignore.v24 deleted file mode 100644 index 0627db619ac..00000000000 --- a/yarn-project/yarn-project-base/Dockerfile.dockerignore.v24 +++ /dev/null @@ -1,37 +0,0 @@ -# TODO/WARNING: ADOPT THIS WHEN WE HAVE DOCKER >= 24 IN NEXT UBUNTU LTS RELEASE. -# -# The aim here is to not have to maintain a list of projects in either the Dockerfile or this ignore file. -# This context should only contain precisely what's needed. -# -# This took a fair bit of trial and error to get the right result. If you need to meddle, try: -# - Commenting out everything after the COPY . . in the dockerfile. -# - ONLY_TARGET=1 ../bootstrap_docker.sh yarn-project-base && docker run -ti --rm aztecprotocol/yarn-project-base:latest sh -c 'du -ha .' - -# Exclude everything to start. -* - -# Unexclude package.json, tsconfig.json and yarn.lock files. -!package.json -!package.common.json -!*/package.json -!*/package.local.json -!*/*/package.json -!yarn.lock -!tsconfig.json -!*/tsconfig.json -!*/*/tsconfig.json - -# Unexclude parts of yarn related config as this also affects how dependencies are installed. -!.yarnrc.yml -!.yarn/plugins -!.yarn/releases -!.yarn/patches - -# Unexclude scripts we use in the Dockerfile. -!yarn-project-base/scripts -!l1-artifacts/scripts - -# Re-exclude any node_modules stuff (matters when building locally when you have a node_modules). -# Yes, we need to explicitly exclude what we unexcluded above. Just putting node_modules doesn't help here. -node_modules/*/package.json -node_modules/*/tsconfig.json diff --git a/yarn-project/yarn-project-base/README.md b/yarn-project/yarn-project-base/README.md deleted file mode 100644 index 2ed7ea6ef87..00000000000 --- a/yarn-project/yarn-project-base/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# yarn-project-base - -## Why? - -If you want to rebuild a docker container for a project in the workspace, you don't want to have to be waiting -to download the entire set of workspace dependencies just because you changed a line of code. The way docker caches -and reuses layers is very powerful. We build this base image in order to: - -1. Encapsulate the downloading of all workspace dependencies. -1. Check our package.json files have inherited from the common package.json. -1. Check out tsconfig project references are all correct. -1. Generate L1 contract ABIs. - -The root project Dockerfile `yarn-project` then: - -1. Generates Noir contract ABIs. -1. Builds the entire project. -1. Checks all formatting is correct. -1. Runs all workspace unit tests. - -Downstream projects are then just about containerizing what's needed to produce executable containers for e2e testing or -deployments. - -## Do we care about docker layer caching, when build-system rebuild patterns only trigger on yarn.lock changes? - -Enough. When building the containers locally for development or debugging purposes, you can't use the content hash -to skip parts of the build, as content hashes require everything to have been committed to git. This is usually -is not the case during development. diff --git a/yarn-project/yarn-project-base/package.json b/yarn-project/yarn-project-base/package.json deleted file mode 100644 index d0615316a9d..00000000000 --- a/yarn-project/yarn-project-base/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "@aztec/yarn-project-base", - "version": "0.0.0", - "type": "module" -} diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 14e47df91ba..ba211032989 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -946,12 +946,6 @@ __metadata: languageName: unknown linkType: soft -"@aztec/yarn-project-base@workspace:yarn-project-base": - version: 0.0.0-use.local - resolution: "@aztec/yarn-project-base@workspace:yarn-project-base" - languageName: unknown - linkType: soft - "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5": version: 7.23.5 resolution: "@babel/code-frame@npm:7.23.5" diff --git a/yellow-paper/docs/calls/static-calls.md b/yellow-paper/docs/calls/static-calls.md index cb1b2c789c6..8df2dd7c87a 100644 --- a/yellow-paper/docs/calls/static-calls.md +++ b/yellow-paper/docs/calls/static-calls.md @@ -5,13 +5,6 @@ In particular, the following fields of the returned `CallStackItem` must be zero or empty in a static call: - - `new_note_hashes` - `new_nullifiers` @@ -22,6 +15,8 @@ Thoughts? Ethereum does the latter. We should write about whichever we choose in - `encrypted_log_preimages_length` - `unencrypted_log_preimages_length` +From the moment a static call is made, every subsequent nested call is forced to be static by setting a flag in the derived `CallContext`, which propagates through the call stack. + At the protocol level, a static call is identified by a `is_static_call` flag in the `CircuitPublicInputs` of the `CallStackItem`. The kernel is responsible for asserting that the call and all nested calls do not emit any forbidden side effects. At the contract level, a caller can initiate a static call via a `staticCallPrivateFunction` or `staticCallPublicFunction` oracle call. The caller is responsible for asserting that the returned `CallStackItem` has the `is_static_call` flag correctly set.