From 524cecf40a6148515d92b193024d2ce587e3e5a8 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Fri, 27 Oct 2023 18:42:36 +0100 Subject: [PATCH] feat: compile noir contracts with noir_wasm (#2737) This PR adds support for compiling contracts with `noir_wasm` instead of `nargo`. Fix #2644 Requires: - [x] #2728 - [x] noir-lang/noir#3036 - [x] noir-lang/noir#3049 - [x] noir-lang/noir#3091 - [x] noir-lang/noir#3213 - [x] updates to `noir-contracts` to add `pub` modifiers to return types (TBD) e.g. `getThisAddress() -> pub Field` here https://github.com/AztecProtocol/aztec-packages/blob/4881414ceb30590674c244ef9bc4c8416eacd6bc/yarn-project/noir-contracts/src/contracts/test_contract/src/main.nr#L45 - [x] build `noir_wasm` with `aztec` feature enabled (based on noir-lang/noir#3049) - [x] output compilation errors (based on noir-lang/noir#3091) # Checklist: Remove the checklist to signal you've completed it. Enable auto-merge if the PR is ready to merge. - [ ] If the pull request requires a cryptography review (e.g. cryptographic algorithm implementations) I have added the 'crypto' tag. - [x] I have reviewed my diff in github, line by line and removed unexpected formatting changes, testing logs, or commented-out code. - [x] Every change is related to the PR description. - [x] I have [linked](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) this pull request to relevant issues (if any exist). --------- Co-authored-by: Santiago Palladino --- .../ts-jest-npm-29.1.1-04e888e48e.patch | 16 ++ yarn-project/acir-simulator/package.json | 2 +- yarn-project/archiver/package.json | 2 +- yarn-project/aztec-faucet/package.json | 2 +- yarn-project/aztec-node/package.json | 2 +- yarn-project/aztec-sandbox/package.json | 2 +- yarn-project/aztec-sandbox/src/bin/index.ts | 2 +- yarn-project/aztec.js/package.json | 2 +- yarn-project/bootstrap.sh | 2 - yarn-project/circuits.js/package.json | 2 +- yarn-project/cli/package.json | 2 +- yarn-project/cli/src/unbox.ts | 2 +- yarn-project/ethereum/package.json | 2 +- yarn-project/foundation/.eslintrc.cjs | 2 +- yarn-project/foundation/package.json | 2 +- yarn-project/key-store/package.json | 2 +- yarn-project/merkle-tree/package.json | 2 +- yarn-project/noir-compiler/package.json | 19 +- .../src/__snapshots__/index.test.ts.snap | 243 +++++++++++++++++- .../noir-compiler/src/cli/contract.ts | 17 +- .../dependencies/dependency-manager.test.ts | 94 +++++++ .../noir/dependencies/dependency-manager.ts | 112 ++++++++ .../noir/dependencies/dependency-resolver.ts | 14 + .../github-dependency-resolver.test.ts | 127 +++++++++ .../github-dependency-resolver.ts | 141 ++++++++++ .../local-dependency-resolver.test.ts | 52 ++++ .../dependencies/local-dependency-resolver.ts | 25 ++ .../noir/file-manager/file-manager.test.ts | 86 +++++++ .../compile/noir/file-manager/file-manager.ts | 129 ++++++++++ .../noir/file-manager/memfs-file-manager.ts | 21 ++ .../noir/noir-source-resolver.shim.cts | 13 + .../src/compile/noir/noir-wasm-compiler.ts | 133 ++++++++++ .../src/compile/noir/package-config.ts | 47 ++++ .../noir-compiler/src/compile/noir/package.ts | 90 +++++++ .../noir-compiler/src/fixtures/test_lib.zip | Bin 0 -> 1198 bytes yarn-project/noir-compiler/src/index.test.ts | 39 ++- yarn-project/noir-compiler/src/index.ts | 27 +- .../noir-compiler/src/noir-version.ts | 13 +- yarn-project/noir-contracts/package.json | 3 +- .../noir-contracts/scripts/compile.sh | 22 ++ .../noir-contracts/scripts/compile_all.sh | 3 +- .../noir-contracts/scripts/types_all.sh | 2 +- .../noir-contracts/src/scripts/copy_output.ts | 37 ++- yarn-project/noir-private-kernel/package.json | 2 +- yarn-project/p2p-bootstrap/package.json | 2 +- yarn-project/p2p/package.json | 2 +- yarn-project/package.common.json | 2 +- yarn-project/package.json | 4 + yarn-project/prover-client/package.json | 2 +- yarn-project/pxe/package.json | 2 +- .../pxe/src/pxe_service/pxe_service.ts | 2 +- yarn-project/scripts/package.json | 2 +- yarn-project/sequencer-client/package.json | 2 +- yarn-project/types/package.json | 2 +- yarn-project/world-state/package.json | 2 +- .../yarn-project-base/Dockerfile.dockerignore | 3 +- .../Dockerfile.dockerignore.v24 | 3 +- yarn-project/yarn.lock | 132 +++++++++- 58 files changed, 1650 insertions(+), 71 deletions(-) create mode 100644 yarn-project/.yarn/patches/ts-jest-npm-29.1.1-04e888e48e.patch create mode 100644 yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.test.ts create mode 100644 yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.ts create mode 100644 yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-resolver.ts create mode 100644 yarn-project/noir-compiler/src/compile/noir/dependencies/github-dependency-resolver.test.ts create mode 100644 yarn-project/noir-compiler/src/compile/noir/dependencies/github-dependency-resolver.ts create mode 100644 yarn-project/noir-compiler/src/compile/noir/dependencies/local-dependency-resolver.test.ts create mode 100644 yarn-project/noir-compiler/src/compile/noir/dependencies/local-dependency-resolver.ts create mode 100644 yarn-project/noir-compiler/src/compile/noir/file-manager/file-manager.test.ts create mode 100644 yarn-project/noir-compiler/src/compile/noir/file-manager/file-manager.ts create mode 100644 yarn-project/noir-compiler/src/compile/noir/file-manager/memfs-file-manager.ts create mode 100644 yarn-project/noir-compiler/src/compile/noir/noir-source-resolver.shim.cts create mode 100644 yarn-project/noir-compiler/src/compile/noir/noir-wasm-compiler.ts create mode 100644 yarn-project/noir-compiler/src/compile/noir/package-config.ts create mode 100644 yarn-project/noir-compiler/src/compile/noir/package.ts create mode 100644 yarn-project/noir-compiler/src/fixtures/test_lib.zip create mode 100755 yarn-project/noir-contracts/scripts/compile.sh diff --git a/yarn-project/.yarn/patches/ts-jest-npm-29.1.1-04e888e48e.patch b/yarn-project/.yarn/patches/ts-jest-npm-29.1.1-04e888e48e.patch new file mode 100644 index 00000000000..3699cc127c1 --- /dev/null +++ b/yarn-project/.yarn/patches/ts-jest-npm-29.1.1-04e888e48e.patch @@ -0,0 +1,16 @@ +diff --git a/dist/constants.js b/dist/constants.js +index 15a900a5ea36e38e7344d5713cdffa1edbdd539d..850352d4b76ac7518c3cd46010023dee0dd8cca0 100644 +--- a/dist/constants.js ++++ b/dist/constants.js +@@ -4,8 +4,9 @@ exports.DEFAULT_JEST_TEST_MATCH = exports.JS_EXT_TO_TREAT_AS_ESM = exports.TS_EX + exports.LINE_FEED = '\n'; + exports.DECLARATION_TYPE_EXT = '.d.ts'; + exports.JS_JSX_EXTENSIONS = ['.js', '.jsx']; +-exports.TS_TSX_REGEX = /\.m?tsx?$/; +-exports.JS_JSX_REGEX = /\.m?jsx?$/; ++// Remove this patch when this issue is fixed https://github.com/kulshekhar/ts-jest/issues/3996 ++exports.TS_TSX_REGEX = /\.[cm]?tsx?$/; ++exports.JS_JSX_REGEX = /\.[cm]?jsx?$/; + // `extensionsToTreatAsEsm` will throw error with `.mjs` + exports.TS_EXT_TO_TREAT_AS_ESM = ['.ts', '.tsx', '.mts']; + exports.JS_EXT_TO_TREAT_AS_ESM = ['.jsx']; diff --git a/yarn-project/acir-simulator/package.json b/yarn-project/acir-simulator/package.json index 4dab11711ea..74ff55000ce 100644 --- a/yarn-project/acir-simulator/package.json +++ b/yarn-project/acir-simulator/package.json @@ -24,7 +24,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/archiver/package.json b/yarn-project/archiver/package.json index c2eef3a26ce..368309c1bb7 100644 --- a/yarn-project/archiver/package.json +++ b/yarn-project/archiver/package.json @@ -28,7 +28,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/aztec-faucet/package.json b/yarn-project/aztec-faucet/package.json index df0541d920c..fcf3526dbd5 100644 --- a/yarn-project/aztec-faucet/package.json +++ b/yarn-project/aztec-faucet/package.json @@ -26,7 +26,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/aztec-node/package.json b/yarn-project/aztec-node/package.json index 0ae9b833431..7e0fed0de53 100644 --- a/yarn-project/aztec-node/package.json +++ b/yarn-project/aztec-node/package.json @@ -27,7 +27,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/aztec-sandbox/package.json b/yarn-project/aztec-sandbox/package.json index 1427b98fe91..8e11e03d62a 100644 --- a/yarn-project/aztec-sandbox/package.json +++ b/yarn-project/aztec-sandbox/package.json @@ -62,7 +62,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/aztec-sandbox/src/bin/index.ts b/yarn-project/aztec-sandbox/src/bin/index.ts index ca1aa4d16b9..463a2b65b35 100644 --- a/yarn-project/aztec-sandbox/src/bin/index.ts +++ b/yarn-project/aztec-sandbox/src/bin/index.ts @@ -3,7 +3,7 @@ import { createAztecNodeRpcServer } from '@aztec/aztec-node'; import { deployInitialSandboxAccounts } from '@aztec/aztec.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { fileURLToPath } from '@aztec/foundation/url'; -import NoirVersion from '@aztec/noir-compiler/noir-version'; +import { NoirVersion } from '@aztec/noir-compiler/noir-version'; import { createPXERpcServer } from '@aztec/pxe'; import { readFileSync } from 'fs'; diff --git a/yarn-project/aztec.js/package.json b/yarn-project/aztec.js/package.json index cf560b1b957..c0008cd00c4 100644 --- a/yarn-project/aztec.js/package.json +++ b/yarn-project/aztec.js/package.json @@ -32,7 +32,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/bootstrap.sh b/yarn-project/bootstrap.sh index a5fff3dedbe..e5bc73c47de 100755 --- a/yarn-project/bootstrap.sh +++ b/yarn-project/bootstrap.sh @@ -32,8 +32,6 @@ yarn --cwd circuits.js remake-constants (cd boxes && ./bootstrap.sh) (cd .. && l1-contracts/bootstrap.sh) -# Until we push .yarn/cache, we still need to install. -yarn # We do not need to build individual packages, yarn build will build the root tsconfig.json yarn build diff --git a/yarn-project/circuits.js/package.json b/yarn-project/circuits.js/package.json index 1dba13567b9..120d66964e5 100644 --- a/yarn-project/circuits.js/package.json +++ b/yarn-project/circuits.js/package.json @@ -33,7 +33,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/cli/package.json b/yarn-project/cli/package.json index a2041b081ea..21630e4a2c0 100644 --- a/yarn-project/cli/package.json +++ b/yarn-project/cli/package.json @@ -28,7 +28,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/cli/src/unbox.ts b/yarn-project/cli/src/unbox.ts index ea5e59bfec6..07fcdd21983 100644 --- a/yarn-project/cli/src/unbox.ts +++ b/yarn-project/cli/src/unbox.ts @@ -320,7 +320,7 @@ export async function unboxContract( if (!contractNames.includes(contractName)) { log( - `The noir contract named "${contractName}" was not found in "@aztec/boxes" package. Valid options are: + `The noir contract named "${contractName}" was not found in "@aztec/boxes" package. Valid options are: ${contractNames.join('\n\t')} We recommend "token" as a default.`, ); diff --git a/yarn-project/ethereum/package.json b/yarn-project/ethereum/package.json index 26fec484fa2..30600a60103 100644 --- a/yarn-project/ethereum/package.json +++ b/yarn-project/ethereum/package.json @@ -48,7 +48,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/foundation/.eslintrc.cjs b/yarn-project/foundation/.eslintrc.cjs index 76ca51878ba..e4369eb7e6d 100644 --- a/yarn-project/foundation/.eslintrc.cjs +++ b/yarn-project/foundation/.eslintrc.cjs @@ -55,7 +55,7 @@ module.exports = { plugins: ['@typescript-eslint', 'eslint-plugin-tsdoc', 'jsdoc', 'no-only-tests'], overrides: [ { - files: ['*.ts', '*.tsx'], + files: ['*.cts', '*.mts', '*.ts', '*.tsx'], parserOptions: { // hacky workaround for CI not having the same tsconfig setup project: true, diff --git a/yarn-project/foundation/package.json b/yarn-project/foundation/package.json index 207c7d668a7..a041f33f7c3 100644 --- a/yarn-project/foundation/package.json +++ b/yarn-project/foundation/package.json @@ -49,7 +49,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/key-store/package.json b/yarn-project/key-store/package.json index d43c019eef9..231b98260dd 100644 --- a/yarn-project/key-store/package.json +++ b/yarn-project/key-store/package.json @@ -24,7 +24,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/merkle-tree/package.json b/yarn-project/merkle-tree/package.json index d551bb84868..6378561fc38 100644 --- a/yarn-project/merkle-tree/package.json +++ b/yarn-project/merkle-tree/package.json @@ -25,7 +25,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src", diff --git a/yarn-project/noir-compiler/package.json b/yarn-project/noir-compiler/package.json index d7b01d1fafc..03a4837ebd3 100644 --- a/yarn-project/noir-compiler/package.json +++ b/yarn-project/noir-compiler/package.json @@ -31,13 +31,23 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, + "moduleFileExtensions": [ + "js", + "ts", + "cts" + ], "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", - "rootDir": "./src" + "rootDir": "./src", + "transform": { + "^.+\\.cts$": "ts-jest" + } }, "dependencies": { "@aztec/foundation": "workspace:^", + "@noir-lang/noir_wasm": "0.18.0-3919619.aztec", + "@noir-lang/source-resolver": "0.18.0-3919619.aztec", "base64-js": "^1.5.1", "commander": "^9.0.0", "fs-extra": "^11.1.1", @@ -46,9 +56,12 @@ "lodash.compact": "^3.0.1", "lodash.times": "^4.3.2", "lodash.upperfirst": "^4.3.1", + "memfs": "^4.6.0", "pako": "^2.1.0", "toml": "^3.0.0", - "tslib": "^2.4.0" + "tslib": "^2.4.0", + "unzipit": "^1.4.3", + "zod": "^3.22.4" }, "devDependencies": { "@jest/globals": "^29.5.0", diff --git a/yarn-project/noir-compiler/src/__snapshots__/index.test.ts.snap b/yarn-project/noir-compiler/src/__snapshots__/index.test.ts.snap index cb79e5eada2..30ce21b864f 100644 --- a/yarn-project/noir-compiler/src/__snapshots__/index.test.ts.snap +++ b/yarn-project/noir-compiler/src/__snapshots__/index.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`noir-compiler using nargo binary compiles the test contract 1`] = ` +exports[`noir-compiler using nargo compiles the test contract 1`] = ` [ { "debug": { @@ -9,8 +9,24 @@ exports[`noir-compiler using nargo binary compiles the test contract 1`] = ` "eJyrVsrJT04syczPK1ayqq6tBQAz9wY7", ], "fileMap": { + "1": { + "path": "/home/ubuntu/host/aztec-packages/yarn-project/noir-compiler/src/fixtures/test_contract/src/main.nr", + "source": "contract TestContract { + use dep::test::module::foo; + + fn constructor(param: Field, pub_param: pub Field) -> pub [Field; 2] { + [foo::bar(param), param + pub_param] + } + + open fn openFunction() -> pub Field { + 42 + } + +} +", + }, "3": { - "path": "std/hash", + "path": "std/hash.nr", "source": "mod poseidon; #[foreign(sha256)] @@ -26,6 +42,13 @@ pub fn pedersen(input : [Field; N]) -> [Field; 2] { #[foreign(pedersen)] pub fn pedersen_with_separator(_input : [Field; N], _separator : u32) -> [Field; 2] {} +pub fn pedersen_hash(input : [Field; N]) -> Field { + pedersen_hash_with_separator(input, 0) +} + +#[foreign(pedersen_hash)] +pub fn pedersen_hash_with_separator(_input : [Field; N], _separator : u32) -> Field {} + #[foreign(hash_to_field_128_security)] pub fn hash_to_field(_input : [Field; N]) -> Field {} @@ -159,12 +182,211 @@ pub fn mimc_bn254(array: [Field; N]) -> Field { } ", }, + "35": { + "path": "/home/ubuntu/host/aztec-packages/yarn-project/noir-compiler/src/fixtures/test_lib/src/module/foo.nr", + "source": "fn bar(param: Field) -> Field { + dep::std::hash::pedersen([param])[0] +}", + }, }, }, "events": [], "functions": [ { - "bytecode": "H4sIAAAAAAAA/61RQQ6DMAwLZfCepElocttXVq38/wXTQASpnMGSZediRfYMAAkODBvn0PP+hR83vuCKMfQdivdAw4NZqctiXERayY2YPpi9mqJoXYyM1PSbjbmZWPHqBZ2EG63qvEbY9Nxf2Hd7+tRtsHc6dR3/AZK4+6agAQAA", + "bytecode": "H4sIAAAAAAAA/61RQQ7DMAij6bL3QIAGbvvKoqX/f8G0TqVSem4tWTYXC9kZABLsmDY+Q4/7G37e+IAz5tBXKF4DTTdmpSGLcRHptXRiemPxZoqibTEyUtNPMeZuYtWbV3QS7rSq8xph+b6/cOz28GnY4N9pHjr+AWqna3ugAQAA", + "functionType": "secret", + "isInternal": false, + "name": "constructor", + "parameters": [ + { + "name": "pub_param", + "type": { + "kind": "field", + }, + "visibility": "public", + }, + ], + "returnTypes": [], + "verificationKey": "0000000200000800000000740000000f00000003515f3109623eb3c25aa5b16a1a79fd558bac7a7ce62c4560a8c537c77ce80dd339128d1d37b6582ee9e6df9567efb64313471dfa18f520f9ce53161b50dbf7731bc5f900000003515f322bc4cce83a486a92c92fd59bd84e0f92595baa639fc2ed86b00ffa0dfded2a092a669a3bdb7a273a015eda494457cc7ed5236f26cee330c290d45a33b9daa94800000003515f332729426c008c085a81bd34d8ef12dd31e80130339ef99d50013a89e4558eee6d0fa4ffe2ee7b7b62eb92608b2251ac31396a718f9b34978888789042b790a30100000003515f342be6b6824a913eb7a57b03cb1ee7bfb4de02f2f65fe8a4e97baa7766ddb353a82a8a25c49dc63778cd9fe96173f12a2bc77f3682f4c4448f98f1df82c75234a100000003515f351f85760d6ab567465aadc2f180af9eae3800e6958fec96aef53fd8a7b195d7c000c6267a0dd5cfc22b3fe804f53e266069c0e36f51885baec1e7e67650c62e170000000c515f41524954484d455449430d9d0f8ece2aa12012fa21e6e5c859e97bd5704e5c122064a66051294bc5e04213f61f54a0ebdf6fee4d4a6ecf693478191de0c2899bcd8e86a636c8d3eff43400000003515f43224a99d02c86336737c8dd5b746c40d2be6aead8393889a76a18d664029096e90f7fe81adcc92a74350eada9622ac453f49ebac24a066a1f83b394df54dfa0130000000c515f46495845445f42415345060e8a013ed289c2f9fd7473b04f6594b138ddb4b4cf6b901622a14088f04b8d2c83ff74fce56e3d5573b99c7b26d85d5046ce0c6559506acb7a675e7713eb3a00000007515f4c4f4749430721a91cb8da4b917e054f72147e1760cfe0ef3d45090ac0f4961d84ec1996961a25e787b26bd8b50b1a99450f77a424a83513c2b33af268cd253b0587ff50c700000003515f4d05dbd8623b8652511e1eb38d38887a69eceb082f807514f09e127237c5213b401b9325b48c6c225968002318095f89d0ef9cf629b2b7f0172e03bc39aacf6ed800000007515f52414e474504b57a3805e41df328f5ca9aefa40fad5917391543b7b65c6476e60b8f72e9ad07c92f3b3e11c8feae96dedc4b14a6226ef3201244f37cfc1ee5b96781f48d2b000000075349474d415f3125001d1954a18571eaa007144c5a567bb0d2be4def08a8be918b8c05e3b27d312c59ed41e09e144eab5de77ca89a2fd783be702a47c951d3112e3de02ce6e47c000000075349474d415f3223994e6a23618e60fa01c449a7ab88378709197e186d48d604bfb6931ffb15ad11c5ec7a0700570f80088fd5198ab5d5c227f2ad2a455a6edeec024156bb7beb000000075349474d415f3300cda5845f23468a13275d18bddae27c6bb189cf9aa95b6a03a0cb6688c7e8d829639b45cf8607c525cc400b55ebf90205f2f378626dc3406cc59b2d1b474fba000000075349474d415f342d299e7928496ea2d37f10b43afd6a80c90a33b483090d18069ffa275eedb2fc2f82121e8de43dc036d99b478b6227ceef34248939987a19011f065d8b5cef5c0000000010000000000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f", + }, + { + "bytecode": "H4sIAAAAAAAA/6WPuwnAMAxE5Y30taUuq9hE3n+EFEnAEFL5NQdXHPcKABT48nbHk4JVNRsnCXXkGG6oNqqTk7md7CLp6i1GNAxSSZoWMvGmLFu4Bfe/r6vHBVdaDl3YAAAA", + "functionType": "open", + "isInternal": false, + "name": "openFunction", + "parameters": [], + "returnTypes": [ + { + "kind": "field", + }, + ], + "verificationKey": "0000000200000800000000740000000f00000003515f3109623eb3c25aa5b16a1a79fd558bac7a7ce62c4560a8c537c77ce80dd339128d1d37b6582ee9e6df9567efb64313471dfa18f520f9ce53161b50dbf7731bc5f900000003515f322bc4cce83a486a92c92fd59bd84e0f92595baa639fc2ed86b00ffa0dfded2a092a669a3bdb7a273a015eda494457cc7ed5236f26cee330c290d45a33b9daa94800000003515f332729426c008c085a81bd34d8ef12dd31e80130339ef99d50013a89e4558eee6d0fa4ffe2ee7b7b62eb92608b2251ac31396a718f9b34978888789042b790a30100000003515f342be6b6824a913eb7a57b03cb1ee7bfb4de02f2f65fe8a4e97baa7766ddb353a82a8a25c49dc63778cd9fe96173f12a2bc77f3682f4c4448f98f1df82c75234a100000003515f351f85760d6ab567465aadc2f180af9eae3800e6958fec96aef53fd8a7b195d7c000c6267a0dd5cfc22b3fe804f53e266069c0e36f51885baec1e7e67650c62e170000000c515f41524954484d455449430d9d0f8ece2aa12012fa21e6e5c859e97bd5704e5c122064a66051294bc5e04213f61f54a0ebdf6fee4d4a6ecf693478191de0c2899bcd8e86a636c8d3eff43400000003515f43224a99d02c86336737c8dd5b746c40d2be6aead8393889a76a18d664029096e90f7fe81adcc92a74350eada9622ac453f49ebac24a066a1f83b394df54dfa0130000000c515f46495845445f42415345060e8a013ed289c2f9fd7473b04f6594b138ddb4b4cf6b901622a14088f04b8d2c83ff74fce56e3d5573b99c7b26d85d5046ce0c6559506acb7a675e7713eb3a00000007515f4c4f4749430721a91cb8da4b917e054f72147e1760cfe0ef3d45090ac0f4961d84ec1996961a25e787b26bd8b50b1a99450f77a424a83513c2b33af268cd253b0587ff50c700000003515f4d05dbd8623b8652511e1eb38d38887a69eceb082f807514f09e127237c5213b401b9325b48c6c225968002318095f89d0ef9cf629b2b7f0172e03bc39aacf6ed800000007515f52414e474504b57a3805e41df328f5ca9aefa40fad5917391543b7b65c6476e60b8f72e9ad07c92f3b3e11c8feae96dedc4b14a6226ef3201244f37cfc1ee5b96781f48d2b000000075349474d415f3125001d1954a18571eaa007144c5a567bb0d2be4def08a8be918b8c05e3b27d312c59ed41e09e144eab5de77ca89a2fd783be702a47c951d3112e3de02ce6e47c000000075349474d415f3223994e6a23618e60fa01c449a7ab88378709197e186d48d604bfb6931ffb15ad11c5ec7a0700570f80088fd5198ab5d5c227f2ad2a455a6edeec024156bb7beb000000075349474d415f3300cda5845f23468a13275d18bddae27c6bb189cf9aa95b6a03a0cb6688c7e8d829639b45cf8607c525cc400b55ebf90205f2f378626dc3406cc59b2d1b474fba000000075349474d415f342d299e7928496ea2d37f10b43afd6a80c90a33b483090d18069ffa275eedb2fc2f82121e8de43dc036d99b478b6227ceef34248939987a19011f065d8b5cef5c0000000010000000000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f", + }, + ], + "name": "TestContract", + }, +] +`; + +exports[`noir-compiler using nargo generates Aztec.nr external interface 1`] = ` +"/* Autogenerated file, do not edit! */ + +use dep::std; +use dep::aztec::context::{ PrivateContext, PublicContext }; +use dep::aztec::constants_gen::RETURN_VALUES_LENGTH; + + + +// Interface for calling TestContract functions from a private context +struct TestContractPrivateContextInterface { + address: Field, +} + +impl TestContractPrivateContextInterface { + pub fn at(address: Field) -> Self { + Self { + address, + } + } + + pub fn openFunction( + self, + context: &mut PrivateContext + ) { + let mut serialized_args = [0; 0]; + + context.call_public_function(self.address, 0x46be982e, serialized_args) + } + +} + + + + +// Interface for calling TestContract functions from a public context +struct TestContractPublicContextInterface { + address: Field, +} + +impl TestContractPublicContextInterface { + pub fn at(address: Field) -> Self { + Self { + address, + } + } + + pub fn openFunction( + self, + context: PublicContext + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 0]; + + context.call_public_function(self.address, 0x46be982e, serialized_args) + } + +} + + +" +`; + +exports[`noir-compiler using nargo generates typescript interface 1`] = ` +" +/* Autogenerated file, do not edit! */ + +/* eslint-disable */ +import { + AztecAddress, + AztecAddressLike, + CompleteAddress, + Contract, + ContractArtifact, + ContractBase, + ContractFunctionInteraction, + ContractMethod, + DeployMethod, + EthAddress, + EthAddressLike, + FieldLike, + Fr, + Point, + PublicKey, + Wallet, +} from '@aztec/aztec.js'; +import TestContractContractArtifactJson from '../target/test.json' assert { type: 'json' }; +export const TestContractContractArtifact = TestContractContractArtifactJson as ContractArtifact; + +/** + * Type-safe interface for contract TestContract; + */ +export class TestContractContract extends ContractBase { + + private constructor( + completeAddress: CompleteAddress, + wallet: Wallet, + portalContract = EthAddress.ZERO + ) { + super(completeAddress, TestContractContractArtifact, wallet, portalContract); + } + + + + /** + * Creates a contract instance. + * @param address - The deployed contract's address. + * @param wallet - The wallet to use when interacting with the contract. + * @returns A promise that resolves to a new Contract instance. + */ + public static async at( + address: AztecAddress, + wallet: Wallet, + ) { + return Contract.at(address, TestContractContract.artifact, wallet) as Promise; + } + + + /** + * Creates a tx to deploy a new instance of this contract. + */ + public static deploy(wallet: Wallet, pub_param: FieldLike) { + return new DeployMethod(Point.ZERO, wallet, TestContractContractArtifact, Array.from(arguments).slice(1)); + } + + /** + * Creates a tx to deploy a new instance of this contract using the specified public key to derive the address. + */ + public static deployWithPublicKey(publicKey: PublicKey, wallet: Wallet, pub_param: FieldLike) { + return new DeployMethod(publicKey, wallet, TestContractContractArtifact, Array.from(arguments).slice(2)); + } + + + + /** + * Returns this contract's artifact. + */ + public static get artifact(): ContractArtifact { + return TestContractContractArtifact; + } + + + /** Type-safe wrappers for the public methods exposed by the contract. */ + public methods!: { + + /** openFunction() */ + openFunction: (() => ContractFunctionInteraction) & Pick; + }; +} +" +`; + +exports[`noir-compiler using wasm binary compiles the test contract 1`] = ` +[ + { + "debug": undefined, + "events": [], + "functions": [ + { + "bytecode": "H4sIAAAAAAAA/61RQQ7DMAij6bL3QIAGbvvKoqX/f8G0TqVSem4tWTYXC9kZABLsmDY+Q4/7G37e+IAz5tBXKF4DTTdmpSGLcRHptXRiemPxZoqibTEyUtNPMeZuYtWbV3QS7rSq8xph+b6/cOz28GnY4N9pHjr+AWqna3ugAQAA", "functionType": "secret", "isInternal": false, "name": "constructor", @@ -181,7 +403,7 @@ pub fn mimc_bn254(array: [Field; N]) -> Field { "verificationKey": "0000000200000800000000740000000f00000003515f3109623eb3c25aa5b16a1a79fd558bac7a7ce62c4560a8c537c77ce80dd339128d1d37b6582ee9e6df9567efb64313471dfa18f520f9ce53161b50dbf7731bc5f900000003515f322bc4cce83a486a92c92fd59bd84e0f92595baa639fc2ed86b00ffa0dfded2a092a669a3bdb7a273a015eda494457cc7ed5236f26cee330c290d45a33b9daa94800000003515f332729426c008c085a81bd34d8ef12dd31e80130339ef99d50013a89e4558eee6d0fa4ffe2ee7b7b62eb92608b2251ac31396a718f9b34978888789042b790a30100000003515f342be6b6824a913eb7a57b03cb1ee7bfb4de02f2f65fe8a4e97baa7766ddb353a82a8a25c49dc63778cd9fe96173f12a2bc77f3682f4c4448f98f1df82c75234a100000003515f351f85760d6ab567465aadc2f180af9eae3800e6958fec96aef53fd8a7b195d7c000c6267a0dd5cfc22b3fe804f53e266069c0e36f51885baec1e7e67650c62e170000000c515f41524954484d455449430d9d0f8ece2aa12012fa21e6e5c859e97bd5704e5c122064a66051294bc5e04213f61f54a0ebdf6fee4d4a6ecf693478191de0c2899bcd8e86a636c8d3eff43400000003515f43224a99d02c86336737c8dd5b746c40d2be6aead8393889a76a18d664029096e90f7fe81adcc92a74350eada9622ac453f49ebac24a066a1f83b394df54dfa0130000000c515f46495845445f42415345060e8a013ed289c2f9fd7473b04f6594b138ddb4b4cf6b901622a14088f04b8d2c83ff74fce56e3d5573b99c7b26d85d5046ce0c6559506acb7a675e7713eb3a00000007515f4c4f4749430721a91cb8da4b917e054f72147e1760cfe0ef3d45090ac0f4961d84ec1996961a25e787b26bd8b50b1a99450f77a424a83513c2b33af268cd253b0587ff50c700000003515f4d05dbd8623b8652511e1eb38d38887a69eceb082f807514f09e127237c5213b401b9325b48c6c225968002318095f89d0ef9cf629b2b7f0172e03bc39aacf6ed800000007515f52414e474504b57a3805e41df328f5ca9aefa40fad5917391543b7b65c6476e60b8f72e9ad07c92f3b3e11c8feae96dedc4b14a6226ef3201244f37cfc1ee5b96781f48d2b000000075349474d415f3125001d1954a18571eaa007144c5a567bb0d2be4def08a8be918b8c05e3b27d312c59ed41e09e144eab5de77ca89a2fd783be702a47c951d3112e3de02ce6e47c000000075349474d415f3223994e6a23618e60fa01c449a7ab88378709197e186d48d604bfb6931ffb15ad11c5ec7a0700570f80088fd5198ab5d5c227f2ad2a455a6edeec024156bb7beb000000075349474d415f3300cda5845f23468a13275d18bddae27c6bb189cf9aa95b6a03a0cb6688c7e8d829639b45cf8607c525cc400b55ebf90205f2f378626dc3406cc59b2d1b474fba000000075349474d415f342d299e7928496ea2d37f10b43afd6a80c90a33b483090d18069ffa275eedb2fc2f82121e8de43dc036d99b478b6227ceef34248939987a19011f065d8b5cef5c0000000010000000000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f", }, { - "bytecode": "H4sIAAAAAAAA/6WPuw2AMAxEHSbyN7E7VkmEs/8IFIAUCVHxmpOuON3bAKDAm6fb7xSsqtk4Sagjx3BDtVGdnMztYBdJV28xomGQStK0kIkXZdnCX3D/+rp6nNHQ/4XYAAAA", + "bytecode": "H4sIAAAAAAAA/6WPuwnAMAxE5Y30taUuq9hE3n+EFEnAEFL5NQdXHPcKABT48nbHk4JVNRsnCXXkGG6oNqqTk7md7CLp6i1GNAxSSZoWMvGmLFu4Bfe/r6vHBVdaDl3YAAAA", "functionType": "open", "isInternal": false, "name": "openFunction", @@ -199,7 +421,7 @@ pub fn mimc_bn254(array: [Field; N]) -> Field { ] `; -exports[`noir-compiler using nargo binary generates Aztec.nr external interface 1`] = ` +exports[`noir-compiler using wasm binary generates Aztec.nr external interface 1`] = ` "/* Autogenerated file, do not edit! */ use dep::std; @@ -261,7 +483,7 @@ impl TestContractPublicContextInterface { " `; -exports[`noir-compiler using nargo binary generates typescript interface 1`] = ` +exports[`noir-compiler using wasm binary generates typescript interface 1`] = ` " /* Autogenerated file, do not edit! */ @@ -280,7 +502,6 @@ import { EthAddressLike, FieldLike, Fr, - PXE, Point, PublicKey, Wallet, @@ -320,15 +541,15 @@ export class TestContractContract extends ContractBase { /** * Creates a tx to deploy a new instance of this contract. */ - public static deploy(pxe: PXE, pub_param: FieldLike) { - return new DeployMethod(Point.ZERO, pxe, TestContractContractArtifact, Array.from(arguments).slice(1)); + public static deploy(wallet: Wallet, pub_param: FieldLike) { + return new DeployMethod(Point.ZERO, wallet, TestContractContractArtifact, Array.from(arguments).slice(1)); } /** * Creates a tx to deploy a new instance of this contract using the specified public key to derive the address. */ - public static deployWithPublicKey(pxe: PXE, publicKey: PublicKey, pub_param: FieldLike) { - return new DeployMethod(publicKey, pxe, TestContractContractArtifact, Array.from(arguments).slice(2)); + public static deployWithPublicKey(publicKey: PublicKey, wallet: Wallet, pub_param: FieldLike) { + return new DeployMethod(publicKey, wallet, TestContractContractArtifact, Array.from(arguments).slice(2)); } diff --git a/yarn-project/noir-compiler/src/cli/contract.ts b/yarn-project/noir-compiler/src/cli/contract.ts index b2a81e3406a..6174d49f856 100644 --- a/yarn-project/noir-compiler/src/cli/contract.ts +++ b/yarn-project/noir-compiler/src/cli/contract.ts @@ -1,11 +1,16 @@ import { LogFn } from '@aztec/foundation/log'; import { Command } from 'commander'; -import { writeFileSync } from 'fs'; +import { mkdirSync, writeFileSync } from 'fs'; import { mkdirpSync } from 'fs-extra'; import path, { resolve } from 'path'; -import { compileUsingNargo, generateNoirContractInterface, generateTypescriptContractInterface } from '../index.js'; +import { + compileUsingNargo, + compileUsingNoirWasm, + generateNoirContractInterface, + generateTypescriptContractInterface, +} from '../index.js'; /** * Registers a 'contract' command on the given commander program that compiles an Aztec.nr contract project. @@ -20,6 +25,7 @@ export function compileContract(program: Command, name = 'contract', log: LogFn .option('-o, --outdir ', 'Output folder for the binary artifacts, relative to the project path', 'target') .option('-ts, --typescript ', 'Optional output folder for generating typescript wrappers', undefined) .option('-i, --interface ', 'Optional output folder for generating an Aztec.nr contract interface', undefined) + .option('-c --compiler ', 'Which compiler to use. Either nargo or wasm. Defaults to nargo', 'nargo') .description('Compiles the contracts in the target project') .action( @@ -30,20 +36,23 @@ export function compileContract(program: Command, name = 'contract', log: LogFn outdir: string; typescript: string | undefined; interface: string | undefined; + compiler: string | undefined; }, /* eslint-enable jsdoc/require-jsdoc */ ) => { - const { outdir, typescript, interface: noirInterface } = options; + const { outdir, typescript, interface: noirInterface, compiler = 'nargo' } = options; if (typeof projectPath !== 'string') throw new Error(`Missing project path argument`); + if (compiler !== 'nargo' && compiler !== 'wasm') throw new Error(`Invalid compiler: ${compiler}`); const currentDir = process.cwd(); - const compile = compileUsingNargo; + const compile = compiler === 'wasm' ? compileUsingNoirWasm : compileUsingNargo; log(`Compiling contracts...`); const result = await compile(projectPath, { log }); for (const contract of result) { const artifactPath = resolve(projectPath, outdir, `${contract.name}.json`); log(`Writing ${contract.name} artifact to ${path.relative(currentDir, artifactPath)}`); + mkdirSync(path.dirname(artifactPath), { recursive: true }); writeFileSync(artifactPath, JSON.stringify(contract, null, 2)); if (noirInterface) { diff --git a/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.test.ts b/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.test.ts new file mode 100644 index 00000000000..42b53732733 --- /dev/null +++ b/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.test.ts @@ -0,0 +1,94 @@ +import { NoirDependencyConfig } from '../package-config.js'; +import { NoirPackage } from '../package.js'; +import { NoirDependencyManager } from './dependency-manager.js'; +import { DependencyResolver } from './dependency-resolver.js'; + +describe('DependencyManager', () => { + let manager: NoirDependencyManager; + + beforeEach(() => { + manager = new NoirDependencyManager( + [new TestDependencyResolver()], + new NoirPackage('/test_contract', '/test_contract/src', { + dependencies: { + lib1: { + path: '/lib1', + }, + lib2: { + path: '/lib2', + }, + }, + package: { + name: 'test_contract', + type: 'contract', + }, + }), + ); + }); + + it('successfully resolves dependencies', async () => { + await expect(manager.resolveDependencies()).resolves.toBeUndefined(); + }); + + it('resolves all libraries', async () => { + await manager.resolveDependencies(); + expect(manager.getPackageNames()).toEqual(['lib1', 'lib2', 'lib3']); + }); + + it('resolves root dependencies', async () => { + await manager.resolveDependencies(); + expect(manager.getEntrypointDependencies()).toEqual(['lib1', 'lib2']); + }); + + it('resolves library dependencies', async () => { + await manager.resolveDependencies(); + expect(manager.getLibraryDependencies()).toEqual({ + lib2: ['lib3'], + }); + }); +}); + +class TestDependencyResolver implements DependencyResolver { + // eslint-disable-next-line require-await + public async resolveDependency(pkg: NoirPackage, dep: NoirDependencyConfig): Promise { + if (!('path' in dep)) { + return null; + } + + switch (dep.path) { + case '/lib1': + return new NoirPackage('/lib1', '/lib1/src', { + dependencies: {}, + package: { + name: 'lib1', + type: 'lib', + }, + }); + + case '/lib2': + return new NoirPackage('/lib2', '/lib2/src', { + dependencies: { + lib3: { + path: '/lib3', + }, + }, + package: { + name: 'lib2', + type: 'lib', + }, + }); + + case '/lib3': + return new NoirPackage('/lib3', '/lib3/src', { + dependencies: {}, + package: { + name: 'lib3', + type: 'lib', + }, + }); + + default: + throw new Error(); + } + } +} diff --git a/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.ts b/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.ts new file mode 100644 index 00000000000..1be0002cb95 --- /dev/null +++ b/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.ts @@ -0,0 +1,112 @@ +import { LogFn, createDebugOnlyLogger } from '@aztec/foundation/log'; + +import { join } from 'node:path'; + +import { NoirDependencyConfig } from '../package-config.js'; +import { NoirPackage } from '../package.js'; +import { DependencyResolver } from './dependency-resolver.js'; + +/** + * Noir Dependency Resolver + */ +export class NoirDependencyManager { + #entryPoint: NoirPackage; + #libraries = new Map(); + #dependencies = new Map(); + #log: LogFn; + #resolvers: readonly DependencyResolver[]; + + /** + * Creates a new dependency resolver + * @param resolvers - A list of dependency resolvers to use + * @param entryPoint - The entry point of the project + */ + constructor(resolvers: readonly DependencyResolver[] = [], entryPoint: NoirPackage) { + this.#log = createDebugOnlyLogger('noir:dependency-resolver'); + this.#resolvers = resolvers; + this.#entryPoint = entryPoint; + } + + /** + * Gets dependencies for the entry point + */ + public getEntrypointDependencies() { + return this.#dependencies.get('') ?? []; + } + + /** + * A map of library dependencies + */ + public getLibraryDependencies() { + const entries = Array.from(this.#dependencies.entries()); + return Object.fromEntries(entries.filter(([name]) => name !== '')); + } + + /** + * Resolves dependencies for a package. + */ + public async resolveDependencies(): Promise { + await this.#recursivelyResolveDependencies('', this.#entryPoint); + } + + async #recursivelyResolveDependencies(packageName: string, noirPackage: NoirPackage): Promise { + for (const [name, config] of Object.entries(noirPackage.getDependencies())) { + // TODO what happens if more than one package has the same name but different versions? + if (this.#libraries.has(name)) { + this.#log(`skipping already resolved dependency ${name}`); + this.#dependencies.set(packageName, [...(this.#dependencies.get(packageName) ?? []), name]); + + continue; + } + + const dependency = await this.#resolveDependency(noirPackage, config); + if (dependency.getType() !== 'lib') { + this.#log(`Non-library package ${name}`, config); + throw new Error(`Dependency ${name} is not a library`); + } + + this.#libraries.set(name, dependency); + this.#dependencies.set(packageName, [...(this.#dependencies.get(packageName) ?? []), name]); + + await this.#recursivelyResolveDependencies(name, dependency); + } + } + + async #resolveDependency(pkg: NoirPackage, config: NoirDependencyConfig) { + let dependency: NoirPackage | null = null; + for (const resolver of this.#resolvers) { + dependency = await resolver.resolveDependency(pkg, config); + if (dependency) { + break; + } + } + + if (!dependency) { + throw new Error('Dependency not resolved'); + } + + return dependency; + } + + /** + * Gets the names of the crates in this dependency list + */ + public getPackageNames() { + return [...this.#libraries.keys()]; + } + + /** + * Looks up a dependency + * @param sourceId - The source being resolved + * @returns The path to the resolved file + */ + public findFile(sourceId: string): string | null { + const [lib, ...path] = sourceId.split('/').filter(x => x); + const pkg = this.#libraries.get(lib); + if (pkg) { + return join(pkg.getSrcPath(), ...path); + } else { + return null; + } + } +} diff --git a/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-resolver.ts b/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-resolver.ts new file mode 100644 index 00000000000..af55dc21a2d --- /dev/null +++ b/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-resolver.ts @@ -0,0 +1,14 @@ +import { NoirDependencyConfig } from '../package-config.js'; +import { NoirPackage } from '../package.js'; + +/** + * Resolves a dependency for a package. + */ +export interface DependencyResolver { + /** + * Resolve a dependency for a package. + * @param pkg - The package to resolve dependencies for + * @param dep - The dependency config to resolve + */ + resolveDependency(pkg: NoirPackage, dep: NoirDependencyConfig): Promise; +} diff --git a/yarn-project/noir-compiler/src/compile/noir/dependencies/github-dependency-resolver.test.ts b/yarn-project/noir-compiler/src/compile/noir/dependencies/github-dependency-resolver.test.ts new file mode 100644 index 00000000000..e369c63f5ed --- /dev/null +++ b/yarn-project/noir-compiler/src/compile/noir/dependencies/github-dependency-resolver.test.ts @@ -0,0 +1,127 @@ +import { fileURLToPath } from '@aztec/foundation/url'; + +import { jest } from '@jest/globals'; +import { Volume, createFsFromVolume } from 'memfs'; +import { readFile } from 'node:fs/promises'; +import { dirname, join } from 'node:path'; + +import { FileManager } from '../file-manager/file-manager.js'; +import { createMemFSFileManager } from '../file-manager/memfs-file-manager.js'; +import { NoirGitDependencyConfig } from '../package-config.js'; +import { NoirPackage } from '../package.js'; +import { DependencyResolver } from './dependency-resolver.js'; +import { GithubDependencyResolver, resolveGithubCodeArchive, safeFilename } from './github-dependency-resolver.js'; + +const fixtures = join(dirname(fileURLToPath(import.meta.url)), '../../../fixtures'); + +describe('GithubDependencyResolver', () => { + let resolver: DependencyResolver; + let fm: FileManager; + let pkg: NoirPackage; + let libDependency: NoirGitDependencyConfig; + let fetchMock: jest.SpiedFunction; + + beforeEach(() => { + fm = createMemFSFileManager(createFsFromVolume(new Volume()), '/'); + + libDependency = { + git: 'https://github.com/example/repo', + tag: 'v1.0.0', + }; + + pkg = new NoirPackage('/test_contract', '/test_contract/src', { + dependencies: { + // eslint-disable-next-line camelcase + test_lib: libDependency, + }, + package: { + name: 'test_contract', + type: 'contract', + }, + }); + + resolver = new GithubDependencyResolver(fm); + + // cut off outside access + fetchMock = jest.spyOn(globalThis, 'fetch').mockImplementation(() => { + throw new Error(); + }); + }); + + afterEach(() => { + fetchMock.mockRestore(); + }); + + it("returns null if it can't resolve a dependency", async () => { + const dep = await resolver.resolveDependency(pkg, { + path: '/test_lib', + }); + + expect(dep).toBeNull(); + }); + + it('resolves Github dependency', async () => { + fetchMock.mockResolvedValueOnce(new Response(await readFile(join(fixtures, 'test_lib.zip')), { status: 200 })); + const libPkg = await resolver.resolveDependency(pkg, libDependency); + expect(libPkg).toBeDefined(); + expect(fm.hasFileSync(libPkg!.getEntryPointPath())).toBe(true); + }); + + it.each<[NoirGitDependencyConfig, 'zip' | 'tar', string]>([ + [ + { + git: 'https://github.com/example/lib.nr', + tag: 'v1.0.0', + }, + 'zip', + 'https://github.com/example/lib.nr/archive/v1.0.0.zip', + ], + [ + { + git: 'https://github.com/example/lib.nr', + tag: 'v1.0.0', + }, + 'tar', + 'https://github.com/example/lib.nr/archive/v1.0.0.tar.gz', + ], + [ + { + git: 'https://github.com/example/lib.nr', + }, + 'zip', + 'https://github.com/example/lib.nr/archive/HEAD.zip', + ], + [ + { + git: 'https://github.com/example/lib.nr', + }, + 'tar', + 'https://github.com/example/lib.nr/archive/HEAD.tar.gz', + ], + ])('resolves to the correct code archive URL', (dep, format, href) => { + const archiveUrl = resolveGithubCodeArchive(dep, format); + expect(archiveUrl.href).toEqual(href); + }); + + it.each([{ git: 'https://github.com/' }, { git: 'https://github.com/foo' }, { git: 'https://example.com' }])( + 'throws if the Github URL is invalid', + dep => { + expect(() => resolveGithubCodeArchive(dep, 'zip')).toThrow(); + }, + ); + + it.each([ + ['main', 'main'], + ['v1.0.0', 'v1.0.0'], + ['../../../etc/passwd', '.._.._.._etc_passwd'], + ['/etc/passwd', 'etc_passwd'], + ['/SomeOrg/some-repo@v1.0.0', 'SomeOrg_some-repo@v1.0.0'], + ['SomeOrg/some-repo@v1.0.0', 'SomeOrg_some-repo@v1.0.0'], + ])('generates safe file names', (value, expected) => { + expect(safeFilename(value)).toEqual(expected); + }); + + it.each([''])('rejects invalid values', value => { + expect(() => safeFilename(value)).toThrow(); + }); +}); diff --git a/yarn-project/noir-compiler/src/compile/noir/dependencies/github-dependency-resolver.ts b/yarn-project/noir-compiler/src/compile/noir/dependencies/github-dependency-resolver.ts new file mode 100644 index 00000000000..5079fe4f766 --- /dev/null +++ b/yarn-project/noir-compiler/src/compile/noir/dependencies/github-dependency-resolver.ts @@ -0,0 +1,141 @@ +import { createDebugOnlyLogger } from '@aztec/foundation/log'; + +import { delimiter, join, sep } from 'node:path'; +import { unzip } from 'unzipit'; + +import { FileManager } from '../file-manager/file-manager.js'; +import { NoirDependencyConfig, NoirGitDependencyConfig } from '../package-config.js'; +import { NoirPackage } from '../package.js'; +import { DependencyResolver } from './dependency-resolver.js'; + +/** + * Downloads dependencies from github + */ +export class GithubDependencyResolver implements DependencyResolver { + #fm: FileManager; + #log = createDebugOnlyLogger('aztec:compile:github-dependency-resolver'); + + constructor(fm: FileManager) { + this.#fm = fm; + } + + /** + * Resolves a dependency from github. Returns null if URL is for a different website. + * @param _pkg - The package to resolve the dependency for + * @param dependency - The dependency configuration + * @returns asd + */ + async resolveDependency(_pkg: NoirPackage, dependency: NoirDependencyConfig): Promise { + // TODO accept ssh urls? + // TODO github authentication? + if (!('git' in dependency) || !dependency.git.startsWith('https://github.com')) { + return null; + } + + const archivePath = await this.#fetchZipFromGithub(dependency); + const libPath = await this.#extractZip(dependency, archivePath); + return NoirPackage.open(libPath, this.#fm); + } + + async #fetchZipFromGithub(dependency: Pick): Promise { + if (!dependency.git.startsWith('https://github.com')) { + throw new Error('Only github dependencies are supported'); + } + + const url = resolveGithubCodeArchive(dependency, 'zip'); + const localArchivePath = join('archives', safeFilename(url.pathname)); + + // TODO should check signature before accepting any file + if (this.#fm.hasFileSync(localArchivePath)) { + this.#log('using cached archive', { url: url.href, path: localArchivePath }); + return localArchivePath; + } + + const response = await fetch(url, { + method: 'GET', + }); + + if (!response.ok || !response.body) { + throw new Error(`Failed to fetch ${url}: ${response.statusText}`); + } + + const tmpFile = localArchivePath + '.tmp'; + await this.#fm.writeFile(tmpFile, response.body); + this.#fm.moveFileSync(tmpFile, localArchivePath); + + return localArchivePath; + } + + async #extractZip(dependency: NoirGitDependencyConfig, archivePath: string): Promise { + const gitUrl = new URL(dependency.git); + const extractLocation = join('libs', safeFilename(gitUrl.pathname + '@' + (dependency.tag ?? 'HEAD'))); + const tmpExtractLocation = extractLocation + '.tmp'; + const packagePath = join(extractLocation, dependency.directory ?? ''); + + if (this.#fm.hasFileSync(packagePath)) { + this.#log(`Using existing package at ${packagePath}`); + return packagePath; + } + + const { entries } = await unzip(this.#fm.readFileSync(archivePath)); + + for (const entry of Object.values(entries)) { + if (entry.isDirectory) { + continue; + } + + const name = stripSegments(entry.name, 1); + if (dependency.directory && !name.startsWith(dependency.directory)) { + continue; + } + const path = join(tmpExtractLocation, name); + await this.#fm.writeFile(path, (await entry.blob()).stream()); + } + + if (dependency.directory) { + this.#fm.moveFileSync(join(tmpExtractLocation, dependency.directory), packagePath); + } else { + this.#fm.moveFileSync(tmpExtractLocation, packagePath); + } + + return packagePath; + } +} + +/** + * Strips the first n segments from a path + */ +function stripSegments(path: string, count: number): string { + const segments = path.split(sep).filter(Boolean); + return segments.slice(count).join(sep); +} + +/** + * Returns a safe filename for a value + * @param val - The value to convert + */ +export function safeFilename(val: string): string { + if (!val) { + throw new Error('invalid value'); + } + + return val.replaceAll(sep, '_').replaceAll(delimiter, '_').replace(/^_+/, ''); +} + +/** + * Resolves a dependency's archive URL. + * @param dependency - The dependency configuration + * @returns The URL to the library archive + */ +export function resolveGithubCodeArchive(dependency: NoirGitDependencyConfig, format: 'zip' | 'tar'): URL { + const gitUrl = new URL(dependency.git); + const [owner, repo] = gitUrl.pathname.slice(1).split('/'); + const ref = dependency.tag ?? 'HEAD'; + const extension = format === 'zip' ? 'zip' : 'tar.gz'; + + if (!owner || !repo || gitUrl.hostname !== 'github.com') { + throw new Error('Invalid Github repository URL'); + } + + return new URL(`https://github.com/${owner}/${repo}/archive/${ref}.${extension}`); +} diff --git a/yarn-project/noir-compiler/src/compile/noir/dependencies/local-dependency-resolver.test.ts b/yarn-project/noir-compiler/src/compile/noir/dependencies/local-dependency-resolver.test.ts new file mode 100644 index 00000000000..11f75194caa --- /dev/null +++ b/yarn-project/noir-compiler/src/compile/noir/dependencies/local-dependency-resolver.test.ts @@ -0,0 +1,52 @@ +import { fileURLToPath } from '@aztec/foundation/url'; + +import { createFsFromVolume } from 'memfs'; +import { Volume } from 'memfs/lib/volume.js'; +import { readFile } from 'node:fs/promises'; +import { dirname, join } from 'node:path'; + +import { FileManager } from '../file-manager/file-manager.js'; +import { createMemFSFileManager } from '../file-manager/memfs-file-manager.js'; +import { NoirPackage } from '../package.js'; +import { DependencyResolver } from './dependency-resolver.js'; +import { LocalDependencyResolver } from './local-dependency-resolver.js'; + +describe('DependencyResolver', () => { + let resolver: DependencyResolver; + let fm: FileManager; + let pkg: NoirPackage; + + beforeEach(async () => { + const fixtures = join(dirname(fileURLToPath(import.meta.url)), '../../../fixtures'); + const memFS = createFsFromVolume(new Volume()); + memFS.mkdirSync('/test_contract/src', { recursive: true }); + memFS.mkdirSync('/test_lib/src', { recursive: true }); + memFS.writeFileSync('/test_contract/Nargo.toml', await readFile(join(fixtures, 'test_contract/Nargo.toml'))); + memFS.writeFileSync('/test_contract/src/main.nr', await readFile(join(fixtures, 'test_contract/src/main.nr'))); + memFS.writeFileSync('/test_lib/Nargo.toml', await readFile(join(fixtures, 'test_lib/Nargo.toml'))); + memFS.writeFileSync('/test_lib/src/lib.nr', await readFile(join(fixtures, 'test_lib/src/lib.nr'))); + + fm = createMemFSFileManager(memFS, '/'); + + pkg = NoirPackage.open('/test_contract', fm); + resolver = new LocalDependencyResolver(fm); + }); + + it("returns null if it can't resolve a dependency", async () => { + const dep = await resolver.resolveDependency(pkg, { + git: 'git@some-git-host', + directory: '/', + tag: 'v1.0.0', + }); + + expect(dep).toBeNull(); + }); + + it.each(['../test_contract', '/test_contract'])('resolves a known dependency', async path => { + const libPkg = await resolver.resolveDependency(pkg, { + path, + }); + expect(libPkg).toBeDefined(); + expect(fm.hasFileSync(libPkg!.getEntryPointPath())).toBe(true); + }); +}); diff --git a/yarn-project/noir-compiler/src/compile/noir/dependencies/local-dependency-resolver.ts b/yarn-project/noir-compiler/src/compile/noir/dependencies/local-dependency-resolver.ts new file mode 100644 index 00000000000..481c338a416 --- /dev/null +++ b/yarn-project/noir-compiler/src/compile/noir/dependencies/local-dependency-resolver.ts @@ -0,0 +1,25 @@ +import { resolve } from 'path'; + +import { FileManager } from '../file-manager/file-manager.js'; +import { NoirDependencyConfig } from '../package-config.js'; +import { NoirPackage } from '../package.js'; +import { DependencyResolver } from './dependency-resolver.js'; + +/** + * Resolves dependencies on-disk, relative to current package + */ +export class LocalDependencyResolver implements DependencyResolver { + #fm: FileManager; + + constructor(fm: FileManager) { + this.#fm = fm; + } + + resolveDependency(pkg: NoirPackage, config: NoirDependencyConfig): Promise { + if ('path' in config) { + return Promise.resolve(NoirPackage.open(resolve(pkg.getPackagePath(), config.path), this.#fm)); + } else { + return Promise.resolve(null); + } + } +} diff --git a/yarn-project/noir-compiler/src/compile/noir/file-manager/file-manager.test.ts b/yarn-project/noir-compiler/src/compile/noir/file-manager/file-manager.test.ts new file mode 100644 index 00000000000..e0290a5c3b4 --- /dev/null +++ b/yarn-project/noir-compiler/src/compile/noir/file-manager/file-manager.test.ts @@ -0,0 +1,86 @@ +import { Volume, createFsFromVolume } from 'memfs'; +import * as fs from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; + +import { FileManager, FileSystem } from './file-manager.js'; +import { createMemFSFileManager } from './memfs-file-manager.js'; + +const memFS = (): { fm: FileManager; teardown: () => void } => { + const fm = createMemFSFileManager(createFsFromVolume(new Volume()), '/'); + return { + fm, + // no-op, it's all in memory + teardown: () => {}, + }; +}; + +const nodeFM = (): { fm: FileManager; teardown: () => void } => { + const fileSystem: FileSystem = { + existsSync: fs.existsSync, + mkdirSync: fs.mkdirSync, + writeFileSync: fs.writeFileSync, + readFileSync: fs.readFileSync, + renameSync: fs.renameSync, + }; + + const dir = fs.mkdtempSync(join(tmpdir(), 'noir-compiler-test')); + const fm = new FileManager(fileSystem, dir); + + return { + fm, + teardown: () => { + fs.rmSync(dir, { + recursive: true, + }); + }, + }; +}; + +/** + * Declare the default test suite for a file manager + * @param setup - Function to setup a file manager + * @param teardown - Optional function to call at the end of the test + */ +describe.each([memFS, nodeFM])('FileManager', setup => { + let fm: FileManager; + let testFileContent: string; + let testFileBytes: Uint8Array; + let teardown: () => void; + + beforeEach(() => { + ({ fm, teardown } = setup()); + testFileContent = 'foo'; + testFileBytes = new TextEncoder().encode(testFileContent); + }); + + afterEach(() => { + return teardown?.(); + }); + + it('saves files and correctly reads bytes back', async () => { + await fm.writeFile('test.txt', new Blob([testFileBytes]).stream()); + expect(fm.readFileSync('test.txt')).toEqual(testFileBytes); + }); + + it('saves files and correctly reads UTF-8 string back', async () => { + await fm.writeFile('test.txt', new Blob([testFileBytes]).stream()); + expect(fm.readFileSync('test.txt', 'utf-8')).toEqual(testFileContent); + }); + + it('correctly checks if file exists or not', async () => { + expect(fm.hasFileSync('test.txt')).toBe(false); + await fm.writeFile('test.txt', new Blob([testFileBytes]).stream()); + expect(fm.hasFileSync('test.txt')).toBe(true); + }); + + it('moves files', async () => { + await fm.writeFile('test.txt.tmp', new Blob([testFileBytes]).stream()); + expect(fm.hasFileSync('test.txt.tmp')).toBe(true); + + fm.moveFileSync('test.txt.tmp', 'test.txt'); + + expect(fm.hasFileSync('test.txt.tmp')).toBe(false); + expect(fm.hasFileSync('test.txt')).toBe(true); + }); +}); diff --git a/yarn-project/noir-compiler/src/compile/noir/file-manager/file-manager.ts b/yarn-project/noir-compiler/src/compile/noir/file-manager/file-manager.ts new file mode 100644 index 00000000000..a05b6479d3f --- /dev/null +++ b/yarn-project/noir-compiler/src/compile/noir/file-manager/file-manager.ts @@ -0,0 +1,129 @@ +import { dirname, isAbsolute, join } from 'path'; + +/** + * A file system interface that matches the node fs module. + */ +export interface FileSystem { + /** Checks if the file exists */ + existsSync: (path: string) => boolean; + /** Creates a directory structure */ + mkdirSync: ( + dir: string, + opts?: { + /** Create parent directories as needed */ + recursive: boolean; + }, + ) => void; + /** Writes a file */ + writeFileSync: (path: string, data: Uint8Array) => void; + /** Reads a file */ + readFileSync: (path: string, encoding?: 'utf-8') => Uint8Array | string; + /** Renames a file */ + renameSync: (oldPath: string, newPath: string) => void; +} + +/** + * A file manager that writes file to a specific directory but reads globally. + */ +export class FileManager { + #fs: FileSystem; + #dataDir: string; + + constructor(fs: FileSystem, dataDir: string) { + this.#fs = fs; + this.#dataDir = dataDir; + } + + /** + * Saves a file to the data directory. + * @param name - File to save + * @param stream - File contents + */ + public async writeFile(name: string, stream: ReadableStream): Promise { + if (isAbsolute(name)) { + throw new Error("can't write absolute path"); + } + + const path = this.#getPath(name); + const chunks: Uint8Array[] = []; + const reader = stream.getReader(); + + while (true) { + const { done, value } = await reader.read(); + if (done) { + break; + } + + chunks.push(value); + } + + const file = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0)); + let offset = 0; + for (const chunk of chunks) { + file.set(chunk, offset); + offset += chunk.length; + } + + this.#fs.mkdirSync(dirname(path), { recursive: true }); + this.#fs.writeFileSync(this.#getPath(path), file); + } + + /** + * Reads a file from the filesystem and returns a buffer + * Saves a file to the data directory. + * @param oldName - File to save + * @param newName - File contents + */ + moveFileSync(oldName: string, newName: string) { + if (isAbsolute(oldName) || isAbsolute(newName)) { + throw new Error("can't move absolute path"); + } + + const oldPath = this.#getPath(oldName); + const newPath = this.#getPath(newName); + + this.#fs.mkdirSync(dirname(newPath), { recursive: true }); + this.#fs.renameSync(oldPath, newPath); + } + + /** + * Reads a file from the disk and returns a buffer + * @param name - File to read + */ + public readFileSync(name: string): Uint8Array; + /** + * Reads a file from the filesystem as a string + * @param name - File to read + * @param encoding - Encoding to use + */ + public readFileSync(name: string, encoding: 'utf-8'): string; + /** + * Reads a file from the filesystem + * @param name - File to read + * @param encoding - Encoding to use + */ + public readFileSync(name: string, encoding?: 'utf-8'): string | Uint8Array { + const path = this.#getPath(name); + const data = this.#fs.readFileSync(path, encoding); + + if (!encoding) { + return typeof data === 'string' + ? new TextEncoder().encode(data) // this branch shouldn't be hit, but just in case + : new Uint8Array(data.buffer, data.byteOffset, data.byteLength / Uint8Array.BYTES_PER_ELEMENT); + } + + return data; + } + + /** + * Checks if a file exists and is accessible + * @param name - File to check + */ + public hasFileSync(name: string): boolean { + return this.#fs.existsSync(this.#getPath(name)); + } + + #getPath(name: string) { + return isAbsolute(name) ? name : join(this.#dataDir, name); + } +} diff --git a/yarn-project/noir-compiler/src/compile/noir/file-manager/memfs-file-manager.ts b/yarn-project/noir-compiler/src/compile/noir/file-manager/memfs-file-manager.ts new file mode 100644 index 00000000000..20df80837b8 --- /dev/null +++ b/yarn-project/noir-compiler/src/compile/noir/file-manager/memfs-file-manager.ts @@ -0,0 +1,21 @@ +import { IFs, fs } from 'memfs'; + +import { FileManager } from './file-manager.js'; + +/** + * Creates a new FileManager instance based on a MemFS instance + * @param memFS - the memfs backing instance + * @param dataDir - where to store files + */ +export function createMemFSFileManager(memFS: IFs = fs, dataDir = '/'): FileManager { + return new FileManager( + { + existsSync: memFS.existsSync.bind(memFS), + mkdirSync: memFS.mkdirSync.bind(memFS), + writeFileSync: memFS.writeFileSync.bind(memFS), + renameSync: memFS.renameSync.bind(memFS), + readFileSync: memFS.readFileSync.bind(memFS), + }, + dataDir, + ); +} diff --git a/yarn-project/noir-compiler/src/compile/noir/noir-source-resolver.shim.cts b/yarn-project/noir-compiler/src/compile/noir/noir-source-resolver.shim.cts new file mode 100644 index 00000000000..3bc79cdd664 --- /dev/null +++ b/yarn-project/noir-compiler/src/compile/noir/noir-source-resolver.shim.cts @@ -0,0 +1,13 @@ +// Shim module to force the use of the CJS build of source-resolver & noir_wasm +/** + * Source resolver module + */ +type SourceResolver = { + /** Sets up a function to provide file contents */ + initializeResolver: (resolver: (source_id: string) => string) => void; +}; + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const sourceResolver: SourceResolver = require('@noir-lang/source-resolver'); + +export const initializeResolver = sourceResolver.initializeResolver; diff --git a/yarn-project/noir-compiler/src/compile/noir/noir-wasm-compiler.ts b/yarn-project/noir-compiler/src/compile/noir/noir-wasm-compiler.ts new file mode 100644 index 00000000000..cd077a4034e --- /dev/null +++ b/yarn-project/noir-compiler/src/compile/noir/noir-wasm-compiler.ts @@ -0,0 +1,133 @@ +import { LogFn, createDebugLogger } from '@aztec/foundation/log'; + +import { CompileError, compile } from '@noir-lang/noir_wasm'; +import { isAbsolute } from 'node:path'; + +import { NoirCompilationArtifacts } from '../../noir_artifact.js'; +import { NoirDependencyManager } from './dependencies/dependency-manager.js'; +import { GithubDependencyResolver as GithubCodeArchiveDependencyResolver } from './dependencies/github-dependency-resolver.js'; +import { LocalDependencyResolver } from './dependencies/local-dependency-resolver.js'; +import { FileManager } from './file-manager/file-manager.js'; +import { initializeResolver } from './noir-source-resolver.shim.cjs'; +import { NoirPackage } from './package.js'; + +/** Compilation options */ +export type NoirWasmCompileOptions = { + /** Logging function */ + log: LogFn; + /** Log debugging information through this function */ + debugLog?: LogFn; +}; + +/** + * Noir Package Compiler + */ +export class NoirWasmContractCompiler { + #log: LogFn; + #debugLog: LogFn; + #package: NoirPackage; + #fm: FileManager; + #dependencyManager: NoirDependencyManager; + + private constructor( + entrypoint: NoirPackage, + dependencyManager: NoirDependencyManager, + fileManager: FileManager, + opts: NoirWasmCompileOptions, + ) { + this.#log = opts.log; + this.#debugLog = opts.debugLog ?? createDebugLogger('aztec:noir-compiler:wasm'); + this.#package = entrypoint; + this.#fm = fileManager; + this.#dependencyManager = dependencyManager; + } + + /** + * Creates a new compiler instance. + * @param fileManager - The file manager to use + * @param projectPath - The path to the project + * @param opts - Compilation options + */ + public static new(fileManager: FileManager, projectPath: string, opts: NoirWasmCompileOptions) { + if (!isAbsolute(projectPath)) { + throw new Error('projectPath must be an absolute path'); + } + + const noirPackage = NoirPackage.open(projectPath, fileManager); + if (noirPackage.getType() !== 'contract') { + throw new Error('This is not a contract project'); + } + + const dependencyManager = new NoirDependencyManager( + [ + new LocalDependencyResolver(fileManager), + new GithubCodeArchiveDependencyResolver(fileManager), + // TODO support actual Git repositories + ], + noirPackage, + ); + + return new NoirWasmContractCompiler(noirPackage, dependencyManager, fileManager, opts); + } + + /** + * Compiles the project. + */ + public async compile(): Promise { + this.#debugLog(`Compiling contract at ${this.#package.getEntryPointPath()}`); + await this.#dependencyManager.resolveDependencies(); + this.#debugLog(`Dependencies: ${this.#dependencyManager.getPackageNames().join(', ')}`); + + initializeResolver(this.#resolveFile); + + try { + const contract = await compile(this.#package.getEntryPointPath(), true, { + /* eslint-disable camelcase */ + root_dependencies: this.#dependencyManager.getEntrypointDependencies(), + library_dependencies: this.#dependencyManager.getLibraryDependencies(), + /* eslint-enable camelcase */ + }); + + return [{ contract }]; + } catch (err) { + if (err instanceof Error && err.name === 'CompileError') { + this.#processCompileError(err as CompileError); + } else { + this.#log('Error compiling contract ' + err); + } + + throw new Error("Couldn't compile contract"); + } + } + + #resolveFile = (path: string) => { + try { + const libFile = this.#dependencyManager.findFile(path); + return this.#fm.readFileSync(libFile ?? path, 'utf-8'); + } catch (err) { + return ''; + } + }; + + #processCompileError(err: CompileError): void { + this.#log('Error compiling contract'); + for (const diag of err.diagnostics) { + this.#log(` ${diag.message}`); + const contents = this.#resolveFile(diag.file); + const lines = contents.split('\n'); + const lineOffsets = lines.reduce((accum, _, idx) => { + if (idx === 0) { + accum.push(0); + } else { + accum.push(accum[idx - 1] + lines[idx - 1].length + 1); + } + return accum; + }, []); + + for (const secondary of diag.secondaries) { + const errorLine = lineOffsets.findIndex(offset => offset > secondary.start); + this.#log(` ${diag.file}:${errorLine}: ${contents.slice(secondary.start, secondary.end)}`); + } + } + } +} diff --git a/yarn-project/noir-compiler/src/compile/noir/package-config.ts b/yarn-project/noir-compiler/src/compile/noir/package-config.ts new file mode 100644 index 00000000000..fa412b476ce --- /dev/null +++ b/yarn-project/noir-compiler/src/compile/noir/package-config.ts @@ -0,0 +1,47 @@ +import { z } from 'zod'; + +const noirGitDependency = z.object({ + git: z.string(), + tag: z.string().optional(), + directory: z.string().optional(), +}); + +const noirLocalDependency = z.object({ + path: z.string(), +}); + +const noirPackageConfig = z.object({ + package: z.object({ + name: z.string(), + type: z.enum(['lib', 'contract', 'binary']), + }), + dependencies: z.record(z.union([noirGitDependency, noirLocalDependency])), +}); + +/** + * Noir package configuration. + */ +export type NoirPackageConfig = z.infer; + +/** + * A remote package dependency. + */ +export type NoirGitDependencyConfig = z.infer; + +/** + * A local package dependency. + */ +export type NoirLocalDependencyConfig = z.infer; + +/** + * A package dependency. + */ +export type NoirDependencyConfig = NoirGitDependencyConfig | NoirLocalDependencyConfig; + +/** + * Checks that an object is a package configuration. + * @param config - Config to check + */ +export function parsePackageConfig(config: any): NoirPackageConfig { + return noirPackageConfig.parse(config); +} diff --git a/yarn-project/noir-compiler/src/compile/noir/package.ts b/yarn-project/noir-compiler/src/compile/noir/package.ts new file mode 100644 index 00000000000..bdc00b2e34c --- /dev/null +++ b/yarn-project/noir-compiler/src/compile/noir/package.ts @@ -0,0 +1,90 @@ +import { join } from 'node:path'; +import { parse as parseToml } from 'toml'; + +import { FileManager } from './file-manager/file-manager.js'; +import { + NoirGitDependencyConfig, + NoirLocalDependencyConfig, + NoirPackageConfig, + parsePackageConfig, +} from './package-config.js'; + +const CONFIG_FILE_NAME = 'Nargo.toml'; + +/** + * A Noir package. + */ +export class NoirPackage { + #packagePath: string; + #srcPath: string; + #config: NoirPackageConfig; + + public constructor(path: string, srcDir: string, config: NoirPackageConfig) { + this.#packagePath = path; + this.#srcPath = srcDir; + this.#config = config; + } + + /** + * Gets this package's path. + */ + public getPackagePath() { + return this.#packagePath; + } + + /** + * The path to the source directory. + */ + public getSrcPath() { + return this.#srcPath; + } + + /** + * Gets the entrypoint path for this package. + */ + public getEntryPointPath(): string { + let entrypoint: string; + + switch (this.getType()) { + case 'lib': + entrypoint = 'lib.nr'; + break; + case 'contract': + case 'binary': + entrypoint = 'main.nr'; + break; + default: + throw new Error(`Unknown package type: ${this.getType()}`); + } + + // TODO check that `src` exists + return join(this.#srcPath, entrypoint); + } + + /** + * Gets the project type + */ + public getType() { + return this.#config.package.type; + } + + /** + * Gets this package's dependencies. + */ + public getDependencies(): Record { + return this.#config.dependencies; + } + + /** + * Opens a path on the filesystem. + * @param path - Path to the package. + * @param fm - A file manager to use. + * @returns The Noir package at the given location + */ + public static open(path: string, fm: FileManager): NoirPackage { + const fileContents = fm.readFileSync(join(path, CONFIG_FILE_NAME), 'utf-8'); + const config = parsePackageConfig(parseToml(fileContents)); + + return new NoirPackage(path, join(path, 'src'), config); + } +} diff --git a/yarn-project/noir-compiler/src/fixtures/test_lib.zip b/yarn-project/noir-compiler/src/fixtures/test_lib.zip new file mode 100644 index 0000000000000000000000000000000000000000..e5004ee2eceff5ec1ed39df5b9e4955e03770b59 GIT binary patch literal 1198 zcmWIWW@Zs#U|`^2uyu0_XPcmG9tz~e1FNVo`d&UP*p#PG|@x z1GCvF`BY05gVfRrZU#n{uu=vFFd=(7nyW!U!0qAp$ksDVi^MK?xg2`4ylu}yu`Rz( zT|fM@WZSzf);zA#3(}7AZmo=*`Q~b0$gArnn-!-!iS=)~qY)bP=YHXg8Mh`|zv=kW z*0V#=%F$Fm0O&$4pnDj=E(9~61TV4+i;I%=;ckn%qmS-3RAX*m;(N>uGzNrGJPI-f z=wZFQBDi5#Jer%Iq5#CDIjPpD=AP`cBT-WjvRhxG8wpEK#2Jj5 zgpdt(1cn7X5I}B4N>Id^4GKmW5Me+Lhnd*T1|}+eCL^K_HAx}6`Z*4hVF?VE`K)Xp Rm$Ct&G!p|uEf6y>008bOBar|A literal 0 HcmV?d00001 diff --git a/yarn-project/noir-compiler/src/index.test.ts b/yarn-project/noir-compiler/src/index.test.ts index 974757ca761..ead724aca47 100644 --- a/yarn-project/noir-compiler/src/index.test.ts +++ b/yarn-project/noir-compiler/src/index.test.ts @@ -1,10 +1,16 @@ import { ContractArtifact } from '@aztec/foundation/abi'; +import { LogFn, createDebugLogger } from '@aztec/foundation/log'; import { fileURLToPath } from '@aztec/foundation/url'; import { execSync } from 'child_process'; import path from 'path'; -import { compileUsingNargo, generateNoirContractInterface, generateTypescriptContractInterface } from './index.js'; +import { + compileUsingNargo, + compileUsingNoirWasm, + generateNoirContractInterface, + generateTypescriptContractInterface, +} from './index.js'; function isNargoAvailable() { try { @@ -15,19 +21,23 @@ function isNargoAvailable() { } } -const describeIf = (cond: () => boolean) => (cond() ? describe : xdescribe); - describe('noir-compiler', () => { let projectPath: string; + let log: LogFn; beforeAll(() => { const currentDirName = path.dirname(fileURLToPath(import.meta.url)); projectPath = path.join(currentDirName, 'fixtures/test_contract'); + log = createDebugLogger('noir-compiler:test'); }); - describeIf(isNargoAvailable)('using nargo binary', () => { + const nargoAvailable = isNargoAvailable(); + const conditionalDescribe = nargoAvailable ? describe : describe.skip; + const conditionalIt = nargoAvailable ? it : it.skip; + + function compilerTest(compileFn: (path: string, opts: { log: LogFn }) => Promise) { let compiled: ContractArtifact[]; beforeAll(async () => { - compiled = await compileUsingNargo(projectPath); + compiled = await compileFn(projectPath, { log }); }); it('compiles the test contract', () => { @@ -43,5 +53,24 @@ describe('noir-compiler', () => { const result = generateNoirContractInterface(compiled[0]); expect(result).toMatchSnapshot(); }); + } + + describe('using wasm binary', () => { + compilerTest(compileUsingNoirWasm); + }); + + conditionalDescribe('using nargo', () => { + compilerTest(compileUsingNargo); + }); + + conditionalIt('both nargo and noir_wasm should compile identically', async () => { + const [noirWasmArtifact, nargoArtifact] = await Promise.all([ + compileUsingNoirWasm(projectPath, { log }), + compileUsingNargo(projectPath, { log }), + ]); + + const withoutDebug = ({ debug: _debug, ...rest }: ContractArtifact): Omit => rest; + + expect(nargoArtifact.map(withoutDebug)).toEqual(noirWasmArtifact.map(withoutDebug)); }); }); diff --git a/yarn-project/noir-compiler/src/index.ts b/yarn-project/noir-compiler/src/index.ts index 1f433b35fe6..b8c48565528 100644 --- a/yarn-project/noir-compiler/src/index.ts +++ b/yarn-project/noir-compiler/src/index.ts @@ -1,11 +1,14 @@ import { ContractArtifact } from '@aztec/foundation/abi'; +import * as fs from 'node:fs'; +import { join, resolve } from 'path'; + import { CompileOpts, NargoContractCompiler } from './compile/nargo.js'; +import { FileManager } from './compile/noir/file-manager/file-manager.js'; +import { NoirWasmCompileOptions, NoirWasmContractCompiler } from './compile/noir/noir-wasm-compiler.js'; import { generateContractArtifact } from './contract-interface-gen/abi.js'; -import NoirVersion from './noir-version.json' assert { type: 'json' }; -const { commit: NoirCommit, tag: NoirTag } = NoirVersion; -export { NoirCommit, NoirTag }; +export * from './noir-version.js'; export { generateNoirContractInterface } from './contract-interface-gen/noir.js'; export { generateTypescriptContractInterface } from './contract-interface-gen/typescript.js'; @@ -22,3 +25,21 @@ export * from './noir_artifact.js'; export async function compileUsingNargo(projectPath: string, opts: CompileOpts = {}): Promise { return (await new NargoContractCompiler(projectPath, opts).compile()).map(generateContractArtifact); } + +/** + * Compile Aztec.nr contracts in project path using built-in noir_wasm. + * @param projectPath - Path to project. + * @param opts - Compiler options. + * @returns Compiled artifacts. + */ +export async function compileUsingNoirWasm( + projectPath: string, + opts: NoirWasmCompileOptions, +): Promise { + const cacheRoot = process.env.XDG_CACHE_HOME ?? join(process.env.HOME ?? '', '.cache'); + const fileManager = new FileManager(fs, join(cacheRoot, 'aztec-noir-compiler')); + + return (await NoirWasmContractCompiler.new(fileManager, resolve(projectPath), opts).compile()).map( + generateContractArtifact, + ); +} diff --git a/yarn-project/noir-compiler/src/noir-version.ts b/yarn-project/noir-compiler/src/noir-version.ts index 4946d70fd3b..43f9383e7d4 100644 --- a/yarn-project/noir-compiler/src/noir-version.ts +++ b/yarn-project/noir-compiler/src/noir-version.ts @@ -1 +1,12 @@ -export { default } from './noir-version.json' assert { type: 'json' }; +import { readFileSync } from 'node:fs'; + +import NoirVersion from './noir-version.json' assert { type: 'json' }; + +// read package.json at runtime instead of compile time so that we keep rootDir as-is in tsconfig +const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8')); + +export const NoirWasmVersion = pkg.dependencies['@noir-lang/noir_wasm']; +export const NoirTag = NoirVersion.tag; +export const NoirCommit = NoirVersion.commit; + +export { NoirVersion }; diff --git a/yarn-project/noir-contracts/package.json b/yarn-project/noir-contracts/package.json index 21f59ba110a..e098ee47798 100644 --- a/yarn-project/noir-contracts/package.json +++ b/yarn-project/noir-contracts/package.json @@ -3,7 +3,6 @@ "version": "0.1.0", "type": "module", "exports": { - ".": "./dest/index.js", "./artifacts": "./dest/artifacts/index.js", "./types": "./dest/types/index.js" }, @@ -25,7 +24,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/noir-contracts/scripts/compile.sh b/yarn-project/noir-contracts/scripts/compile.sh new file mode 100755 index 00000000000..3b1da6139d6 --- /dev/null +++ b/yarn-project/noir-contracts/scripts/compile.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -euo pipefail; + +# Compiles Aztec.nr contracts in parallel, bubbling any compilation errors + +export self_dir=$(dirname "$(realpath $0)") +export COMPILER="$self_dir/../../noir-compiler/dest/cli.js" + +build() { + CONTRACT_NAME=$1 + CONTRACT_FOLDER="$self_dir/../src/contracts/${CONTRACT_NAME}_contract" + echo "Compiling $CONTRACT_NAME..." + rm -rf ${CONTRACT_FOLDER}/target + + node "$COMPILER" contract "$CONTRACT_FOLDER" +} + +export -f build + +# run 4 builds at a time +echo "$@" | xargs -n 1 -P 4 bash -c 'build "$0"' diff --git a/yarn-project/noir-contracts/scripts/compile_all.sh b/yarn-project/noir-contracts/scripts/compile_all.sh index 0eef9863f06..1cd26131e89 100755 --- a/yarn-project/noir-contracts/scripts/compile_all.sh +++ b/yarn-project/noir-contracts/scripts/compile_all.sh @@ -9,4 +9,5 @@ nargo_check # Runs the compile scripts for all contracts. echo "Compiling all contracts" -nargo compile --workspace --no-backend \ No newline at end of file +# ./scripts/compile.sh $(./scripts/get_all_contracts.sh) +nargo compile --workspace --no-backend diff --git a/yarn-project/noir-contracts/scripts/types_all.sh b/yarn-project/noir-contracts/scripts/types_all.sh index e94eb026a13..fe2b1685276 100755 --- a/yarn-project/noir-contracts/scripts/types_all.sh +++ b/yarn-project/noir-contracts/scripts/types_all.sh @@ -3,4 +3,4 @@ ./scripts/types.sh $(./scripts/get_all_contracts.sh) # Remove the debug files as they are no longer needed and can cause prettier and build issues -rm -r ./target/debug* \ No newline at end of file +rm -r ./target/debug* diff --git a/yarn-project/noir-contracts/src/scripts/copy_output.ts b/yarn-project/noir-contracts/src/scripts/copy_output.ts index ab22356465e..5477b601335 100644 --- a/yarn-project/noir-contracts/src/scripts/copy_output.ts +++ b/yarn-project/noir-contracts/src/scripts/copy_output.ts @@ -45,13 +45,12 @@ function writeToProject(artifact: any) { } } -const main = () => { - const name = process.argv[2]; - if (!name) throw new Error(`Missing argument contract name`); - - const projectName = `${snakeCase(name)}_contract`; - - const contractName = upperFirst(camelCase(name)); +/** + * Processes nargo workspace artifacts + * @param projectName - The project name + * @param contractName - The contract name + */ +function processNargoWorkspaceArtifact(projectName: string, contractName: string) { const artifactFile = `${projectName}-${contractName}.json`; const buildJsonFilePath = `./target/${artifactFile}`; @@ -69,9 +68,31 @@ const main = () => { } catch (err) { // Ignore } - // Remove extraneous information from the buildJson (which was output by Nargo) to hone in on the function data we actually care about: const artifactJson: ContractArtifact = generateContractArtifact({ contract: buildJson, debug }); + return artifactJson; +} + +/** + * Processes an artifact generated by noir-compiler. + * Currently unused. This should be used once contracts are compiled with `noir-compiler` instead of Nargo. + * + * @param projectName - The name of the project + * @param contractName - The name of the contract + */ +function _processNoirCompilerArtifact(projectName: string, contractName: string) { + const artifactJsonFilePath = `src/contracts/${projectName}/target/${contractName}.json`; + const artifactJson: ContractArtifact = JSON.parse(readFileSync(artifactJsonFilePath).toString()); + return artifactJson; +} + +const main = () => { + const name = process.argv[2]; + if (!name) throw new Error(`Missing argument contract name`); + + const projectName = `${snakeCase(name)}_contract`; + const contractName = upperFirst(camelCase(name)); + const artifactJson = processNargoWorkspaceArtifact(projectName, contractName); // Write the artifact: const artifactsDir = 'src/artifacts'; diff --git a/yarn-project/noir-private-kernel/package.json b/yarn-project/noir-private-kernel/package.json index 7ffa947a7fb..7950f561383 100644 --- a/yarn-project/noir-private-kernel/package.json +++ b/yarn-project/noir-private-kernel/package.json @@ -23,7 +23,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/p2p-bootstrap/package.json b/yarn-project/p2p-bootstrap/package.json index 646ba2c4abf..e5f3c5b61bf 100644 --- a/yarn-project/p2p-bootstrap/package.json +++ b/yarn-project/p2p-bootstrap/package.json @@ -48,7 +48,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/p2p/package.json b/yarn-project/p2p/package.json index 2b8e317e603..19a4bd74f49 100644 --- a/yarn-project/p2p/package.json +++ b/yarn-project/p2p/package.json @@ -26,7 +26,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/package.common.json b/yarn-project/package.common.json index 6b6f7471258..7e200c5fb86 100644 --- a/yarn-project/package.common.json +++ b/yarn-project/package.common.json @@ -23,7 +23,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/package.json b/yarn-project/package.json index 1fc7450aa56..4a450e19d32 100644 --- a/yarn-project/package.json +++ b/yarn-project/package.json @@ -59,5 +59,9 @@ "prettier": "^2.8.8", "typedoc": "^0.24.8", "typescript": "^5.0.4" + }, + "resolutions": { + "ts-jest@^29.1.0": "patch:ts-jest@npm%3A29.1.1#./.yarn/patches/ts-jest-npm-29.1.1-04e888e48e.patch", + "ts-jest@^29.1.1": "patch:ts-jest@npm%3A29.1.1#./.yarn/patches/ts-jest-npm-29.1.1-04e888e48e.patch" } } diff --git a/yarn-project/prover-client/package.json b/yarn-project/prover-client/package.json index 40741c4cc3a..f52b8a4494f 100644 --- a/yarn-project/prover-client/package.json +++ b/yarn-project/prover-client/package.json @@ -24,7 +24,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/pxe/package.json b/yarn-project/pxe/package.json index 13813f40674..740bf098a5b 100644 --- a/yarn-project/pxe/package.json +++ b/yarn-project/pxe/package.json @@ -26,7 +26,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index ec375cbad3d..16a7349a920 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -22,7 +22,7 @@ import { encodeArguments } from '@aztec/foundation/abi'; import { padArrayEnd } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/fields'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; -import NoirVersion from '@aztec/noir-compiler/noir-version'; +import { NoirVersion } from '@aztec/noir-compiler/noir-version'; import { AuthWitness, AztecNode, diff --git a/yarn-project/scripts/package.json b/yarn-project/scripts/package.json index 7c7fba41894..d228efbe325 100644 --- a/yarn-project/scripts/package.json +++ b/yarn-project/scripts/package.json @@ -49,7 +49,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/sequencer-client/package.json b/yarn-project/sequencer-client/package.json index 28981d4a720..c6a4c6a50bd 100644 --- a/yarn-project/sequencer-client/package.json +++ b/yarn-project/sequencer-client/package.json @@ -26,7 +26,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/types/package.json b/yarn-project/types/package.json index 597a6d02bc1..3bac04d8fd7 100644 --- a/yarn-project/types/package.json +++ b/yarn-project/types/package.json @@ -27,7 +27,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/world-state/package.json b/yarn-project/world-state/package.json index 08a4ef97e05..36c704cd0ae 100644 --- a/yarn-project/world-state/package.json +++ b/yarn-project/world-state/package.json @@ -24,7 +24,7 @@ "jest": { "preset": "ts-jest/presets/default-esm", "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", "rootDir": "./src" diff --git a/yarn-project/yarn-project-base/Dockerfile.dockerignore b/yarn-project/yarn-project-base/Dockerfile.dockerignore index 94371e1cd26..4545ce66a9c 100644 --- a/yarn-project/yarn-project-base/Dockerfile.dockerignore +++ b/yarn-project/yarn-project-base/Dockerfile.dockerignore @@ -26,6 +26,7 @@ boxes/*/* !.yarnrc.yml !.yarn/plugins !.yarn/releases +!.yarn/patches # Unexclude tsconfig files for running project reference checks. !**/tsconfig.json @@ -35,4 +36,4 @@ boxes/*/* !l1-artifacts/scripts # Unexclude prettierignore -!.prettierignore \ No newline at end of file +!.prettierignore diff --git a/yarn-project/yarn-project-base/Dockerfile.dockerignore.v24 b/yarn-project/yarn-project-base/Dockerfile.dockerignore.v24 index 349cce383db..421235079e3 100644 --- a/yarn-project/yarn-project-base/Dockerfile.dockerignore.v24 +++ b/yarn-project/yarn-project-base/Dockerfile.dockerignore.v24 @@ -25,6 +25,7 @@ !.yarnrc.yml !.yarn/plugins !.yarn/releases +!.yarn/patches # Unexclude scripts we use in the Dockerfile. !yarn-project-base/scripts @@ -33,4 +34,4 @@ # 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 \ No newline at end of file +node_modules/*/tsconfig.json diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 0c5499e4683..dc3bfc62406 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -585,6 +585,8 @@ __metadata: dependencies: "@aztec/foundation": "workspace:^" "@jest/globals": ^29.5.0 + "@noir-lang/noir_wasm": 0.18.0-3919619.aztec + "@noir-lang/source-resolver": 0.18.0-3919619.aztec "@rushstack/eslint-patch": ^1.1.4 "@types/fs-extra": ^11.0.1 "@types/jest": ^29.5.0 @@ -604,12 +606,15 @@ __metadata: lodash.compact: ^3.0.1 lodash.times: ^4.3.2 lodash.upperfirst: ^4.3.1 + memfs: ^4.6.0 pako: ^2.1.0 toml: ^3.0.0 ts-jest: ^29.1.0 ts-node: ^10.9.1 tslib: ^2.4.0 typescript: ^5.0.4 + unzipit: ^1.4.3 + zod: ^3.22.4 bin: aztec-compile: dest/cli.js languageName: unknown @@ -3543,6 +3548,15 @@ __metadata: languageName: node linkType: hard +"@noir-lang/noir_wasm@npm:0.18.0-3919619.aztec": + version: 0.18.0-3919619.aztec + resolution: "@noir-lang/noir_wasm@npm:0.18.0-3919619.aztec" + peerDependencies: + "@noir-lang/source-resolver": 0.18.0-3919619.aztec + checksum: 34102eaeaf250cf6c324ba9021ffb88695de2eaf6c648b882ad1bd9f1db3434f3af854a5b13302972a0e9cf34b7921008ca162c4ea68807590e567d343f2cec0 + languageName: node + linkType: hard + "@noir-lang/noirc_abi@npm:0.16.0, @noir-lang/noirc_abi@npm:^0.16.0": version: 0.16.0 resolution: "@noir-lang/noirc_abi@npm:0.16.0" @@ -3550,6 +3564,13 @@ __metadata: languageName: node linkType: hard +"@noir-lang/source-resolver@npm:0.18.0-3919619.aztec": + version: 0.18.0-3919619.aztec + resolution: "@noir-lang/source-resolver@npm:0.18.0-3919619.aztec" + checksum: c7f39beba745fe9ca1b05c1df91bfb76c82704c18390740dbfea480e5e5ed98b7861e62341e2179fa4f5db3e327825336e1f8fe3452e081f4974be438c6b00f5 + languageName: node + linkType: hard + "@noir-lang/types@npm:0.14.1": version: 0.14.1 resolution: "@noir-lang/types@npm:0.14.1" @@ -6640,7 +6661,7 @@ __metadata: languageName: node linkType: hard -"arg@npm:5.0.2": +"arg@npm:5.0.2, arg@npm:^5.0.2": version: 5.0.2 resolution: "arg@npm:5.0.2" checksum: 6c69ada1a9943d332d9e5382393e897c500908d91d5cb735a01120d5f71daf1b339b7b8980cbeaba8fd1afc68e658a739746179e4315a26e8a28951ff9930078 @@ -11120,6 +11141,13 @@ __metadata: languageName: node linkType: hard +"hyperdyperid@npm:^1.2.0": + version: 1.2.0 + resolution: "hyperdyperid@npm:1.2.0" + checksum: 210029d1c86926f09109f6317d143f8b056fc38e8dd11b0c3e3205fc6c6ff8429fb55b4b9c2bce065462719ed9d34366eced387aaa0035d93eb76b306a8547ef + languageName: node + linkType: hard + "iconv-lite@npm:0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" @@ -13101,6 +13129,29 @@ __metadata: languageName: node linkType: hard +"json-joy@npm:^9.2.0": + version: 9.6.0 + resolution: "json-joy@npm:9.6.0" + dependencies: + arg: ^5.0.2 + hyperdyperid: ^1.2.0 + peerDependencies: + quill-delta: ^5 + rxjs: 7 + tslib: 2 + bin: + jj: bin/jj.js + json-pack: bin/json-pack.js + json-pack-test: bin/json-pack-test.js + json-patch: bin/json-patch.js + json-patch-test: bin/json-patch-test.js + json-pointer: bin/json-pointer.js + json-pointer-test: bin/json-pointer-test.js + json-unpack: bin/json-unpack.js + checksum: 517b1b466b1b477bd53ecf23693758e785cebba5bf32dbb04059164b067330246efd35f11df13e8a04fb85ec4c330f806e2752736189a12f1a4ff1a1e423ec27 + languageName: node + linkType: hard + "json-parse-even-better-errors@npm:^2.3.0, json-parse-even-better-errors@npm:^2.3.1": version: 2.3.1 resolution: "json-parse-even-better-errors@npm:2.3.1" @@ -14068,6 +14119,18 @@ __metadata: languageName: node linkType: hard +"memfs@npm:^4.6.0": + version: 4.6.0 + resolution: "memfs@npm:4.6.0" + dependencies: + json-joy: ^9.2.0 + thingies: ^1.11.1 + peerDependencies: + tslib: 2 + checksum: b32a35bee9f96dc011605f3bb39e74e6d2a5de51c952a77bb38a0dfabd3381c40ae382d27f385aa290edee8081597fb1a3b41a07bb3f775fd55312dc30ac1d9d + languageName: node + linkType: hard + "meow@npm:^7.1.1": version: 7.1.1 resolution: "meow@npm:7.1.1" @@ -17891,6 +17954,15 @@ __metadata: languageName: node linkType: hard +"thingies@npm:^1.11.1": + version: 1.12.0 + resolution: "thingies@npm:1.12.0" + peerDependencies: + tslib: ^2 + checksum: 04b75d264e1880676fb9f400b2c0e069abdd00de1fa006d9daee527ad6d899828169fd46bb17e7cabf2802b7dbd64053106e29e7a7aa271659f5b41b3a11657f + languageName: node + linkType: hard + "thread-stream@npm:^0.15.1": version: 0.15.2 resolution: "thread-stream@npm:0.15.2" @@ -18109,7 +18181,7 @@ __metadata: languageName: node linkType: hard -"ts-jest@npm:^29.1.0, ts-jest@npm:^29.1.1": +"ts-jest@npm:29.1.1": version: 29.1.1 resolution: "ts-jest@npm:29.1.1" dependencies: @@ -18142,6 +18214,39 @@ __metadata: languageName: node linkType: hard +"ts-jest@patch:ts-jest@npm%3A29.1.1#./.yarn/patches/ts-jest-npm-29.1.1-04e888e48e.patch::locator=%40aztec%2Faztec3-packages%40workspace%3A.": + version: 29.1.1 + resolution: "ts-jest@patch:ts-jest@npm%3A29.1.1#./.yarn/patches/ts-jest-npm-29.1.1-04e888e48e.patch::version=29.1.1&hash=e3c0a5&locator=%40aztec%2Faztec3-packages%40workspace%3A." + dependencies: + bs-logger: 0.x + fast-json-stable-stringify: 2.x + jest-util: ^29.0.0 + json5: ^2.2.3 + lodash.memoize: 4.x + make-error: 1.x + semver: ^7.5.3 + yargs-parser: ^21.0.1 + peerDependencies: + "@babel/core": ">=7.0.0-beta.0 <8" + "@jest/types": ^29.0.0 + babel-jest: ^29.0.0 + jest: ^29.0.0 + typescript: ">=4.3 <6" + peerDependenciesMeta: + "@babel/core": + optional: true + "@jest/types": + optional: true + babel-jest: + optional: true + esbuild: + optional: true + bin: + ts-jest: cli.js + checksum: 81022fc5cb39e8ab819e37a1cc68cd5e919849baf6573c27ceebcc61a4ba24048bcb7aac13c846396753b4a4a63b20c147cabfdc60727ef9acec835c770d9a8a + languageName: node + linkType: hard + "ts-loader@npm:^9.4.4": version: 9.4.4 resolution: "ts-loader@npm:9.4.4" @@ -18612,6 +18717,15 @@ __metadata: languageName: node linkType: hard +"unzipit@npm:^1.4.3": + version: 1.4.3 + resolution: "unzipit@npm:1.4.3" + dependencies: + uzip-module: ^1.0.2 + checksum: ce29348edab7b5fb5b7b4d43437f48e35812ac8b3cc2d76efd1acfcad6dd1b96b4f96bfd03250a724b87ba99dd531d7727ad24b590acf727dde79f54f5e779ed + languageName: node + linkType: hard + "upath@npm:^2.0.1": version: 2.0.1 resolution: "upath@npm:2.0.1" @@ -18752,6 +18866,13 @@ __metadata: languageName: node linkType: hard +"uzip-module@npm:^1.0.2": + version: 1.0.3 + resolution: "uzip-module@npm:1.0.3" + checksum: fc286c44a04d75055577fae8293d3fee499d1e850f87e88c158b1e3657f4794a3a40ca2d34f73474ff82917176dd5ca9d1c0d1e375a083714e11afabd3afa423 + languageName: node + linkType: hard + "v8-compile-cache-lib@npm:^3.0.1": version: 3.0.1 resolution: "v8-compile-cache-lib@npm:3.0.1" @@ -19707,6 +19828,13 @@ __metadata: languageName: node linkType: hard +"zod@npm:^3.22.4": + version: 3.22.4 + resolution: "zod@npm:3.22.4" + checksum: 80bfd7f8039b24fddeb0718a2ec7c02aa9856e4838d6aa4864335a047b6b37a3273b191ef335bf0b2002e5c514ef261ffcda5a589fb084a48c336ffc4cdbab7f + languageName: node + linkType: hard + "zustand@npm:^4.3.1": version: 4.4.1 resolution: "zustand@npm:4.4.1"