From b60279bd4107cab04d95e0a59caecd4910228eeb Mon Sep 17 00:00:00 2001
From: Tom French <15848336+TomAFrench@users.noreply.github.com>
Date: Thu, 7 Mar 2024 22:10:41 +0000
Subject: [PATCH 01/14] chore: update various dependencies (#4513)
# Description
## Problem\*
Resolves
## Summary\*
## Additional Context
## Documentation\*
Check one:
- [ ] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[Exceptional Case]** Documentation to be submitted in a separate
PR.
# PR Checklist\*
- [ ] I have tested the changes locally.
- [ ] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
---
Cargo.lock | 243 ++++++++++-----------
acvm-repo/bn254_blackbox_solver/Cargo.toml | 4 +-
2 files changed, 116 insertions(+), 131 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 18c1f7ad40e..317418276b1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -391,7 +391,7 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138985dd8aefbefeaa66b01b7f5b2b6b4c333fcef1cc5f32c63a2aabe37d6de3"
dependencies = [
- "futures 0.3.28",
+ "futures 0.3.30",
"lsp-types 0.94.1",
"pin-project-lite",
"rustix",
@@ -521,9 +521,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.3.3"
+version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
+checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "bitmaps"
@@ -947,14 +947,14 @@ checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335"
[[package]]
name = "console"
-version = "0.15.7"
+version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
+checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
dependencies = [
"encode_unicode 0.3.6",
"lazy_static",
"libc",
- "windows-sys 0.45.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -1585,12 +1585,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
-version = "0.3.5"
+version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -1743,9 +1743,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
[[package]]
name = "futures"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
+checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
dependencies = [
"futures-channel",
"futures-core",
@@ -1758,9 +1758,9 @@ dependencies = [
[[package]]
name = "futures-channel"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
dependencies = [
"futures-core",
"futures-sink",
@@ -1768,15 +1768,15 @@ dependencies = [
[[package]]
name = "futures-core"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-executor"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
+checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
dependencies = [
"futures-core",
"futures-task",
@@ -1786,15 +1786,15 @@ dependencies = [
[[package]]
name = "futures-io"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
+checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
[[package]]
name = "futures-macro"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
@@ -1803,21 +1803,21 @@ dependencies = [
[[package]]
name = "futures-sink"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]]
name = "futures-task"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-util"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures 0.1.31",
"futures-channel",
@@ -2076,9 +2076,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "hyper"
-version = "0.14.27"
+version = "0.14.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468"
+checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
dependencies = [
"bytes",
"futures-channel",
@@ -2091,7 +2091,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
- "socket2 0.4.9",
+ "socket2",
"tokio",
"tower-service",
"tracing",
@@ -2100,9 +2100,9 @@ dependencies = [
[[package]]
name = "hyper-rustls"
-version = "0.24.1"
+version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97"
+checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
dependencies = [
"futures-util",
"http",
@@ -2343,7 +2343,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b99d4207e2a04fb4581746903c2bb7eb376f88de9c699d0f3e10feeac0cd3a"
dependencies = [
"derive_more",
- "futures 0.3.28",
+ "futures 0.3.30",
"jsonrpc-core",
"jsonrpc-pubsub",
"log",
@@ -2358,7 +2358,7 @@ version = "18.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb"
dependencies = [
- "futures 0.3.28",
+ "futures 0.3.30",
"futures-executor",
"futures-util",
"log",
@@ -2373,7 +2373,7 @@ version = "18.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b51da17abecbdab3e3d4f26b01c5ec075e88d3abe3ab3b05dc9aa69392764ec0"
dependencies = [
- "futures 0.3.28",
+ "futures 0.3.30",
"jsonrpc-client-transports",
]
@@ -2395,7 +2395,7 @@ version = "18.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff"
dependencies = [
- "futures 0.3.28",
+ "futures 0.3.30",
"hyper",
"jsonrpc-core",
"jsonrpc-server-utils",
@@ -2411,7 +2411,7 @@ version = "18.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240f87695e6c6f62fb37f05c02c04953cf68d6408b8c1c89de85c7a0125b1011"
dependencies = [
- "futures 0.3.28",
+ "futures 0.3.30",
"jsonrpc-core",
"lazy_static",
"log",
@@ -2427,7 +2427,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4"
dependencies = [
"bytes",
- "futures 0.3.28",
+ "futures 0.3.30",
"globset",
"jsonrpc-core",
"lazy_static",
@@ -2485,9 +2485,9 @@ checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
[[package]]
name = "linux-raw-sys"
-version = "0.4.3"
+version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0"
+checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]]
name = "lock_api"
@@ -3451,7 +3451,7 @@ checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e"
dependencies = [
"bit-set",
"bit-vec",
- "bitflags 2.3.3",
+ "bitflags 2.4.2",
"lazy_static",
"num-traits",
"rand 0.8.5",
@@ -3913,15 +3913,15 @@ dependencies = [
[[package]]
name = "rustix"
-version = "0.38.4"
+version = "0.38.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5"
+checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.2",
"errno",
"libc",
"linux-raw-sys",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -4292,9 +4292,9 @@ dependencies = [
[[package]]
name = "shared-buffer"
-version = "0.1.3"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995"
+checksum = "f6c99835bad52957e7aa241d3975ed17c1e5f8c92026377d117a606f36b84b16"
dependencies = [
"bytes",
"memmap2 0.6.2",
@@ -4412,16 +4412,6 @@ dependencies = [
"serde",
]
-[[package]]
-name = "socket2"
-version = "0.4.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
-dependencies = [
- "libc",
- "winapi",
-]
-
[[package]]
name = "socket2"
version = "0.5.5"
@@ -4733,7 +4723,7 @@ dependencies = [
"mio",
"num_cpus",
"pin-project-lite",
- "socket2 0.5.5",
+ "socket2",
"tokio-macros",
"windows-sys 0.48.0",
]
@@ -5249,9 +5239,9 @@ dependencies = [
[[package]]
name = "wasmer"
-version = "4.2.4"
+version = "4.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce45cc009177ca345a6d041f9062305ad467d15e7d41494f5b81ab46d62d7a58"
+checksum = "5c15724dc25d1ee57962334aea8e41ade2675e5ea2ac6b8d42da6051b0face66"
dependencies = [
"bytes",
"cfg-if 1.0.0",
@@ -5265,23 +5255,23 @@ dependencies = [
"shared-buffer",
"target-lexicon",
"thiserror",
+ "tracing",
"wasm-bindgen",
"wasmer-compiler",
"wasmer-compiler-cranelift",
"wasmer-derive",
"wasmer-types",
"wasmer-vm",
- "wasmparser 0.83.0",
- "wasmparser 0.95.0",
+ "wasmparser",
"wat",
"winapi",
]
[[package]]
name = "wasmer-compiler"
-version = "4.2.4"
+version = "4.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e044f6140c844602b920deb4526aea3cc9c0d7cf23f00730bb9b2034669f522a"
+checksum = "55a7f3b3a96f8d844c25e2c032af9572306dd63fa93dc17bcca4c5458ac569bd"
dependencies = [
"backtrace",
"bytes",
@@ -5300,15 +5290,15 @@ dependencies = [
"thiserror",
"wasmer-types",
"wasmer-vm",
- "wasmparser 0.95.0",
+ "wasmparser",
"winapi",
]
[[package]]
name = "wasmer-compiler-cranelift"
-version = "4.2.4"
+version = "4.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32ce02358eb44a149d791c1d6648fb7f8b2f99cd55e3c4eef0474653ec8cc889"
+checksum = "102e2c5bacac69495c4025767e2fa26797ffb27f242dccb7cf57d9cefd944386"
dependencies = [
"cranelift-codegen",
"cranelift-entity",
@@ -5325,9 +5315,9 @@ dependencies = [
[[package]]
name = "wasmer-derive"
-version = "4.2.4"
+version = "4.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c782d80401edb08e1eba206733f7859db6c997fc5a7f5fb44edc3ecd801468f6"
+checksum = "0ea737fa08f95d6abc4459f42a70a9833e8974b814e74971d77ef473814f4d4c"
dependencies = [
"proc-macro-error",
"proc-macro2",
@@ -5337,9 +5327,9 @@ dependencies = [
[[package]]
name = "wasmer-types"
-version = "4.2.4"
+version = "4.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd09e80d4d74bb9fd0ce6c3c106b1ceba1a050f9948db9d9b78ae53c172d6157"
+checksum = "b0689110e291b0f07fc665f2824e5ff81df120848e8a9acfbf1a9bf7990773f9"
dependencies = [
"bytecheck",
"enum-iterator",
@@ -5353,9 +5343,9 @@ dependencies = [
[[package]]
name = "wasmer-vm"
-version = "4.2.4"
+version = "4.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bdcd8a4fd36414a7b6a003dbfbd32393bce3e155d715dd877c05c1b7a41d224d"
+checksum = "4cd41f822a1ac4242d478754e8ceba2806a00ea5072803622e1fe91e8e28b2a1"
dependencies = [
"backtrace",
"cc",
@@ -5381,18 +5371,13 @@ dependencies = [
[[package]]
name = "wasmparser"
-version = "0.83.0"
+version = "0.121.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a"
-
-[[package]]
-name = "wasmparser"
-version = "0.95.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2ea896273ea99b15132414be1da01ab0d8836415083298ecaffbe308eaac87a"
+checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab"
dependencies = [
- "indexmap 1.9.3",
- "url 2.4.0",
+ "bitflags 2.4.2",
+ "indexmap 2.0.0",
+ "semver",
]
[[package]]
@@ -5485,15 +5470,6 @@ dependencies = [
"windows_x86_64_msvc 0.33.0",
]
-[[package]]
-name = "windows-sys"
-version = "0.45.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
-dependencies = [
- "windows-targets 0.42.2",
-]
-
[[package]]
name = "windows-sys"
version = "0.48.0"
@@ -5504,18 +5480,12 @@ dependencies = [
]
[[package]]
-name = "windows-targets"
-version = "0.42.2"
+name = "windows-sys"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
- "windows_aarch64_gnullvm 0.42.2",
- "windows_aarch64_msvc 0.42.2",
- "windows_i686_gnu 0.42.2",
- "windows_i686_msvc 0.42.2",
- "windows_x86_64_gnu 0.42.2",
- "windows_x86_64_gnullvm 0.42.2",
- "windows_x86_64_msvc 0.42.2",
+ "windows-targets 0.52.4",
]
[[package]]
@@ -5534,10 +5504,19 @@ dependencies = [
]
[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.42.2"
+name = "windows-targets"
+version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.4",
+ "windows_aarch64_msvc 0.52.4",
+ "windows_i686_gnu 0.52.4",
+ "windows_i686_msvc 0.52.4",
+ "windows_x86_64_gnu 0.52.4",
+ "windows_x86_64_gnullvm 0.52.4",
+ "windows_x86_64_msvc 0.52.4",
+]
[[package]]
name = "windows_aarch64_gnullvm"
@@ -5545,6 +5524,12 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
+
[[package]]
name = "windows_aarch64_msvc"
version = "0.33.0"
@@ -5553,15 +5538,15 @@ checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.42.2"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.48.0"
+version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
[[package]]
name = "windows_i686_gnu"
@@ -5571,15 +5556,15 @@ checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e"
[[package]]
name = "windows_i686_gnu"
-version = "0.42.2"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_gnu"
-version = "0.48.0"
+version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
[[package]]
name = "windows_i686_msvc"
@@ -5589,15 +5574,15 @@ checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0"
[[package]]
name = "windows_i686_msvc"
-version = "0.42.2"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_i686_msvc"
-version = "0.48.0"
+version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
[[package]]
name = "windows_x86_64_gnu"
@@ -5607,27 +5592,27 @@ checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.42.2"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.48.0"
+version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.42.2"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.48.0"
+version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
[[package]]
name = "windows_x86_64_msvc"
@@ -5637,15 +5622,15 @@ checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.42.2"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.48.0"
+version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
[[package]]
name = "winnow"
diff --git a/acvm-repo/bn254_blackbox_solver/Cargo.toml b/acvm-repo/bn254_blackbox_solver/Cargo.toml
index ea601a6b80f..a0a15409604 100644
--- a/acvm-repo/bn254_blackbox_solver/Cargo.toml
+++ b/acvm-repo/bn254_blackbox_solver/Cargo.toml
@@ -32,7 +32,7 @@ ark-ff = { version = "^0.4.0", default-features = false }
num-bigint.workspace = true
[target.'cfg(target_arch = "wasm32")'.dependencies]
-wasmer = { version = "4.2.3", default-features = false, features = [
+wasmer = { version = "4.2.6", default-features = false, features = [
"js-default",
] }
@@ -42,7 +42,7 @@ js-sys.workspace = true
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
getrandom.workspace = true
-wasmer = "4.2.3"
+wasmer = "4.2.6"
[build-dependencies]
pkg-config = "0.3"
From 9cee413b73298df98a5650bf3d05121213e982e2 Mon Sep 17 00:00:00 2001
From: guipublic <47281315+guipublic@users.noreply.github.com>
Date: Fri, 8 Mar 2024 17:02:25 +0100
Subject: [PATCH 02/14] chore: custom hash for eddsa (#4440)
# Description
## Problem\*
Resolves #3642
## Summary\*
Eddsa verification takes now a hasher so that it can be used with
anything having the Hasher trait.
I added this trait to the stdlib implementations of mimc, poseidon and
poseidon2.
## Additional Context
## Documentation\*
Check one:
- [ ] No documentation needed.
- [X] Documentation included in this PR.
- [ ] **[Exceptional Case]** Documentation to be submitted in a separate
PR.
# PR Checklist\*
- [X] I have tested the changes locally.
- [X] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
---------
Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com>
---
.../cryptographic_primitives/eddsa.mdx | 8 ++
noir_stdlib/src/eddsa.nr | 31 +++++++-
noir_stdlib/src/hash/mimc.nr | 50 ++++++++++--
noir_stdlib/src/hash/poseidon.nr | 76 +++++++++++++++++++
noir_stdlib/src/hash/poseidon2.nr | 39 +++++++++-
.../execution_success/eddsa/src/main.nr | 10 ++-
.../poseidon_bn254_hash/src/main.nr | 2 +-
7 files changed, 202 insertions(+), 14 deletions(-)
diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx
index 99b7f830a20..c2c0624dfad 100644
--- a/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx
+++ b/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx
@@ -15,6 +15,14 @@ Verifier for EdDSA signatures
fn eddsa_poseidon_verify(public_key_x : Field, public_key_y : Field, signature_s: Field, signature_r8_x: Field, signature_r8_y: Field, message: Field) -> bool
```
+It is also possible to specify the hash algorithm used for the signature by using the `eddsa_verify_with_hasher` function with a parameter implementing the Hasher trait. For instance, if you want to use Poseidon2 instead, you can do the following:
+```rust
+use dep::std::hash::poseidon2::Poseidon2Hasher;
+
+let mut hasher = Poseidon2Hasher::default();
+eddsa_verify_with_hasher(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg, &mut hasher);
+```
+
## eddsa::eddsa_to_pub
diff --git a/noir_stdlib/src/eddsa.nr b/noir_stdlib/src/eddsa.nr
index 966bc1da2a1..3aff6043ffd 100644
--- a/noir_stdlib/src/eddsa.nr
+++ b/noir_stdlib/src/eddsa.nr
@@ -1,6 +1,8 @@
use crate::hash::poseidon;
use crate::ec::consts::te::baby_jubjub;
use crate::ec::tecurve::affine::Point as TEPoint;
+use crate::hash::{Hash, Hasher, BuildHasher, BuildHasherDefault};
+use crate::hash::poseidon::PoseidonHasher;
// Returns true if signature is valid
pub fn eddsa_poseidon_verify(
@@ -11,6 +13,28 @@ pub fn eddsa_poseidon_verify(
signature_r8_y: Field,
message: Field
) -> bool {
+ let mut hasher = PoseidonHasher::default();
+ eddsa_verify_with_hasher(
+ pub_key_x,
+ pub_key_y,
+ signature_s,
+ signature_r8_x,
+ signature_r8_y,
+ message,
+ &mut hasher
+ )
+}
+
+pub fn eddsa_verify_with_hasher(
+ pub_key_x: Field,
+ pub_key_y: Field,
+ signature_s: Field,
+ signature_r8_x: Field,
+ signature_r8_y: Field,
+ message: Field,
+ hasher: &mut H
+) -> bool
+where H: Hasher {
// Verifies by testing:
// S * B8 = R8 + H(R8, A, m) * A8
let bjj = baby_jubjub();
@@ -23,7 +47,12 @@ pub fn eddsa_poseidon_verify(
// Ensure S < Subgroup Order
assert(signature_s.lt(bjj.suborder));
// Calculate the h = H(R, A, msg)
- let hash: Field = poseidon::bn254::hash_5([signature_r8_x, signature_r8_y, pub_key_x, pub_key_y, message]);
+ signature_r8_x.hash(hasher);
+ signature_r8_y.hash(hasher);
+ pub_key_x.hash(hasher);
+ pub_key_y.hash(hasher);
+ message.hash(hasher);
+ let hash: Field = (*hasher).finish();
// Calculate second part of the right side: right2 = h*8*A
// Multiply by 8 by doubling 3 times. This also ensures that the result is in the subgroup.
let pub_key_mul_2 = bjj.curve.add(pub_key, pub_key);
diff --git a/noir_stdlib/src/hash/mimc.nr b/noir_stdlib/src/hash/mimc.nr
index 10c0a48917c..db8a32d7909 100644
--- a/noir_stdlib/src/hash/mimc.nr
+++ b/noir_stdlib/src/hash/mimc.nr
@@ -1,3 +1,6 @@
+use crate::hash::Hasher;
+use crate::default::Default;
+
// mimc-p/p implementation
// constants are (publicly generated) random numbers, for instance using keccak as a ROM.
// You must use constants generated for the native field
@@ -16,13 +19,8 @@ fn mimc(x: Field, k: Field, constants: [Field; N], exp: Field) -> Field {
}
global MIMC_BN254_ROUNDS = 91;
-//mimc implementation with hardcoded parameters for BN254 curve.
-#[field(bn254)]
-pub fn mimc_bn254(array: [Field; N]) -> Field {
- //mimc parameters
- let exponent = 7;
- //generated from seed "mimc" using keccak256
- let constants: [Field; MIMC_BN254_ROUNDS] = [
+//generated from seed "mimc" using keccak256
+global MIMC_BN254_CONSTANTS: [Field; MIMC_BN254_ROUNDS] = [
0,
20888961410941983456478427210666206549300505294776164667214940546594746570981,
15265126113435022738560151911929040668591755459209400716467504685752745317193,
@@ -116,10 +114,46 @@ pub fn mimc_bn254(array: [Field; N]) -> Field {
13602139229813231349386885113156901793661719180900395818909719758150455500533
];
+//mimc implementation with hardcoded parameters for BN254 curve.
+#[field(bn254)]
+pub fn mimc_bn254(array: [Field; N]) -> Field {
+ let exponent = 7;
let mut r = 0;
for elem in array {
- let h = mimc(elem, r, constants, exponent);
+ let h = mimc(elem, r, MIMC_BN254_CONSTANTS, exponent);
r = r + elem + h;
}
r
}
+
+struct MimcHasher{
+ _state: [Field],
+ _len: u64,
+}
+
+impl Hasher for MimcHasher {
+ #[field(bn254)]
+ fn finish(self) -> Field {
+ let exponent = 7;
+ let mut r = 0;
+ for i in 0..self._len {
+ let h = mimc(self._state[i], r, MIMC_BN254_CONSTANTS, exponent);
+ r = r + self._state[i] + h;
+ }
+ r
+ }
+
+ fn write(&mut self, input: [Field]){
+ self._state = self._state.append(input);
+ self._len += input.len();
+ }
+}
+
+impl Default for MimcHasher{
+ fn default() -> Self{
+ MimcHasher{
+ _state: [],
+ _len: 0,
+ }
+ }
+}
diff --git a/noir_stdlib/src/hash/poseidon.nr b/noir_stdlib/src/hash/poseidon.nr
index b1a7c4a2367..7f99ad36316 100644
--- a/noir_stdlib/src/hash/poseidon.nr
+++ b/noir_stdlib/src/hash/poseidon.nr
@@ -1,5 +1,7 @@
mod bn254; // Instantiations of Poseidon for prime field of the same order as BN254
use crate::field::modulus_num_bits;
+use crate::hash::Hasher;
+use crate::default::Default;
struct PoseidonConfig {
t: Field, // Width, i.e. state size
@@ -100,3 +102,77 @@ fn apply_matrix(a: [Field; M], x: [Field; N]) -> [Field; N] {
y
}
+
+struct PoseidonHasher{
+ _state: [Field],
+ _len: u64,
+}
+
+impl Hasher for PoseidonHasher {
+ #[field(bn254)]
+ fn finish(self) -> Field {
+ let mut result = 0;
+ assert(self._len < 16);
+ if self._len == 1 {
+ result = bn254::hash_1([self._state[0]]);
+ }
+ if self._len == 2 {
+ result = bn254::hash_2([self._state[0],self._state[1]]);
+ }
+ if self._len == 3 {
+ result = bn254::hash_3([self._state[0],self._state[1],self._state[2]]);
+ }
+ if self._len == 4 {
+ result = bn254::hash_4([self._state[0],self._state[1],self._state[2],self._state[3]]);
+ }
+ if self._len == 5 {
+ result = bn254::hash_5([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4]]);
+ }
+ if self._len == 6 {
+ result = bn254::hash_6([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5]]);
+ }
+ if self._len == 7 {
+ result = bn254::hash_7([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6]]);
+ }
+ if self._len == 8 {
+ result = bn254::hash_8([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7]]);
+ }
+ if self._len == 9 {
+ result = bn254::hash_9([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8]]);
+ }
+ if self._len == 10 {
+ result = bn254::hash_10([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9]]);
+ }
+ if self._len == 11 {
+ result = bn254::hash_11([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10]]);
+ }
+ if self._len == 12 {
+ result = bn254::hash_12([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10], self._state[11]]);
+ }
+ if self._len == 13 {
+ result = bn254::hash_13([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10], self._state[11], self._state[12]]);
+ }
+ if self._len == 14 {
+ result = bn254::hash_14([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10], self._state[11], self._state[12], self._state[13]]);
+ }
+ if self._len == 15 {
+ result = bn254::hash_15([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10], self._state[11], self._state[12], self._state[13], self._state[14]]);
+ }
+
+ result
+ }
+
+ fn write(&mut self, input: [Field]){
+ self._state = self._state.append(input);
+ self._len += input.len();
+ }
+}
+
+impl Default for PoseidonHasher{
+ fn default() -> Self{
+ PoseidonHasher{
+ _state: [],
+ _len: 0,
+ }
+ }
+}
diff --git a/noir_stdlib/src/hash/poseidon2.nr b/noir_stdlib/src/hash/poseidon2.nr
index 40eea029e82..52229f18dbd 100644
--- a/noir_stdlib/src/hash/poseidon2.nr
+++ b/noir_stdlib/src/hash/poseidon2.nr
@@ -1,3 +1,6 @@
+use crate::hash::Hasher;
+use crate::default::Default;
+
global RATE = 3;
struct Poseidon2 {
@@ -9,7 +12,7 @@ struct Poseidon2 {
impl Poseidon2 {
- pub fn hash(input: [Field; N], message_size: u32) -> Field {
+ pub fn hash(input: [Field; N], message_size: u64) -> Field {
if message_size == N {
Poseidon2::hash_internal(input, N, false)
} else {
@@ -92,12 +95,12 @@ impl Poseidon2 {
result
}
- fn hash_internal(input: [Field; N], in_len: u32, is_variable_length: bool) -> Field {
+ fn hash_internal(input: [Field; N], in_len: u64, is_variable_length: bool) -> Field {
let two_pow_64 = 18446744073709551616;
let iv : Field = (in_len as Field) * two_pow_64;
let mut sponge = Poseidon2::new(iv);
for i in 0..input.len() {
- if i as u32 < in_len {
+ if i < in_len {
sponge.absorb(input[i]);
}
}
@@ -111,3 +114,33 @@ impl Poseidon2 {
sponge.squeeze()
}
}
+
+struct Poseidon2Hasher{
+ _state: [Field],
+ _len: u64,
+}
+
+impl Hasher for Poseidon2Hasher {
+ fn finish(self) -> Field {
+ let iv : Field = (self._state.len() as Field)*18446744073709551616; // iv = (self._state.len() << 64)
+ let mut sponge = Poseidon2::new(iv);
+ for i in 0..self._len {
+ sponge.absorb(self._state[i]);
+ }
+ sponge.squeeze()
+ }
+
+ fn write(&mut self, input: [Field]){
+ self._state = self._state.append(input);
+ self._len += input.len();
+ }
+}
+
+impl Default for Poseidon2Hasher{
+ fn default() -> Self{
+ Poseidon2Hasher{
+ _state: [],
+ _len: 0,
+ }
+ }
+}
diff --git a/test_programs/execution_success/eddsa/src/main.nr b/test_programs/execution_success/eddsa/src/main.nr
index 4404ffe75f7..fd1a95ee5fb 100644
--- a/test_programs/execution_success/eddsa/src/main.nr
+++ b/test_programs/execution_success/eddsa/src/main.nr
@@ -2,7 +2,9 @@ use dep::std::compat;
use dep::std::ec::consts::te::baby_jubjub;
use dep::std::ec::tecurve::affine::Point as TEPoint;
use dep::std::hash;
-use dep::std::eddsa::{eddsa_to_pub, eddsa_poseidon_verify};
+use dep::std::eddsa::{eddsa_to_pub, eddsa_poseidon_verify, eddsa_verify_with_hasher};
+use dep::std::hash::poseidon2::Poseidon2Hasher;
+use dep::std::hash::pedersen::PedersenHasher;
fn main(msg: pub Field, _priv_key_a: Field, _priv_key_b: Field) {
// Skip this test for non-bn254 backends
@@ -48,5 +50,11 @@ fn main(msg: pub Field, _priv_key_a: Field, _priv_key_b: Field) {
assert(!eddsa_poseidon_verify(pub_key_a.x, pub_key_a.y, s_b, r8_b.x, r8_b.y, msg));
// User A's signature over the message can't be used with another message
assert(!eddsa_poseidon_verify(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg + 1));
+ // Using a different hash should fail
+ let mut hasher = Poseidon2Hasher::default();
+ assert(!eddsa_verify_with_hasher(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg, &mut hasher));
+ // Using a different hash should fail
+ let mut hasher = PedersenHasher::default();
+ assert(!eddsa_verify_with_hasher(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg, &mut hasher));
}
}
diff --git a/test_programs/execution_success/poseidon_bn254_hash/src/main.nr b/test_programs/execution_success/poseidon_bn254_hash/src/main.nr
index 939b99595c7..a1607956190 100644
--- a/test_programs/execution_success/poseidon_bn254_hash/src/main.nr
+++ b/test_programs/execution_success/poseidon_bn254_hash/src/main.nr
@@ -9,7 +9,7 @@ fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field, x3: [Field
let hash2 = poseidon::bn254::hash_4(x2);
assert(hash2 == y2);
- let hash3 = poseidon2::Poseidon2::hash(x3, x3.len() as u32);
+ let hash3 = poseidon2::Poseidon2::hash(x3, x3.len());
assert(hash3 == y3);
}
// docs:end:poseidon
From 66f22aab1f0a0e9dc970e32e54c28f12fb39ea8f Mon Sep 17 00:00:00 2001
From: jfecher
Date: Fri, 8 Mar 2024 14:03:13 -0600
Subject: [PATCH 03/14] chore: Add HashMap docs (#4457)
# Description
## Problem\*
## Summary\*
Adds docs for https://github.com/noir-lang/noir/pull/4242
I've also edited the interface for `entries`, `keys`, and `values` to
return `BoundedVec`s instead of arrays with optional elements. This is
much more natural I think. I originally intended this for a separate PR,
but I didn't want to write documentation for the old version of these
functions only to immediately have to edit them afterward.
## Additional Context
## Documentation\*
Check one:
- [ ] No documentation needed.
- [x] Documentation included in this PR.
- [ ] **[Exceptional Case]** Documentation to be submitted in a separate
PR.
# PR Checklist\*
- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
---------
Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com>
---
.../standard_library/containers/hashmap.md | 270 ++++++++++++++++++
noir_stdlib/src/collections/map.nr | 112 +++++---
.../execution_success/hashmap/src/main.nr | 182 +++++++++++-
.../execution_success/hashmap/src/utils.nr | 12 +-
4 files changed, 517 insertions(+), 59 deletions(-)
create mode 100644 docs/docs/noir/standard_library/containers/hashmap.md
diff --git a/docs/docs/noir/standard_library/containers/hashmap.md b/docs/docs/noir/standard_library/containers/hashmap.md
new file mode 100644
index 00000000000..093b6d38d11
--- /dev/null
+++ b/docs/docs/noir/standard_library/containers/hashmap.md
@@ -0,0 +1,270 @@
+---
+title: HashMap
+keywords: [noir, map, hash, hashmap]
+sidebar_position: 1
+---
+
+`HashMap` is used to efficiently store and look up key-value pairs.
+
+`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements.
+Note that due to hash collisions, the actual maximum number of elements stored by any particular
+hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since
+every hash value will be performed modulo `MaxLen`.
+
+When creating `HashMap`s, the `MaxLen` generic should always be specified if it is not already
+known. Otherwise, the compiler may infer a different value for `MaxLen` (such as zero), which
+will likely change the result of the program. This behavior is set to become an error in future
+versions instead.
+
+Example:
+
+```rust
+// Create a mapping from Fields to u32s with a maximum length of 12
+// using a pedersen hash
+let mut map: HashMap> = HashMap::default();
+
+map.insert(1, 2);
+map.insert(3, 4);
+
+let two = map.get(1).unwrap();
+```
+
+## Methods
+
+### default
+
+#include_code default noir_stdlib/src/collections/map.nr rust
+
+Creates a fresh, empty HashMap.
+
+When using this function, always make sure to specify the maximum size of the hash map.
+
+This is the same `default` from the `Default` implementation given further below. It is
+repeated here for convenience since it is the recommended way to create a hashmap.
+
+Example:
+
+#include_code default_example test_programs/execution_success/hashmap/src/main.nr rust
+
+Because `HashMap` has so many generic arguments that are likely to be the same throughout
+your program, it may be helpful to create a type alias:
+
+#include_code type_alias test_programs/execution_success/hashmap/src/main.nr rust
+
+### with_hasher
+
+#include_code with_hasher noir_stdlib/src/collections/map.nr rust
+
+Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple
+hashmaps are created with the same hasher instance.
+
+Example:
+
+#include_code with_hasher_example test_programs/execution_success/hashmap/src/main.nr rust
+
+### get
+
+#include_code get noir_stdlib/src/collections/map.nr rust
+
+Retrieves a value from the hashmap, returning `Option::none()` if it was not found.
+
+Example:
+
+#include_code get_example test_programs/execution_success/hashmap/src/main.nr rust
+
+### insert
+
+#include_code insert noir_stdlib/src/collections/map.nr rust
+
+Inserts a new key-value pair into the map. If the key was already in the map, its
+previous value will be overridden with the newly provided one.
+
+Example:
+
+#include_code insert_example test_programs/execution_success/hashmap/src/main.nr rust
+
+### remove
+
+#include_code remove noir_stdlib/src/collections/map.nr rust
+
+Removes the given key-value pair from the map. If the key was not already present
+in the map, this does nothing.
+
+Example:
+
+#include_code remove_example test_programs/execution_success/hashmap/src/main.nr rust
+
+### is_empty
+
+#include_code is_empty noir_stdlib/src/collections/map.nr rust
+
+True if the length of the hash map is empty.
+
+Example:
+
+#include_code is_empty_example test_programs/execution_success/hashmap/src/main.nr rust
+
+### len
+
+#include_code len noir_stdlib/src/collections/map.nr rust
+
+Returns the current length of this hash map.
+
+Example:
+
+#include_code len_example test_programs/execution_success/hashmap/src/main.nr rust
+
+### capacity
+
+#include_code capacity noir_stdlib/src/collections/map.nr rust
+
+Returns the maximum capacity of this hashmap. This is always equal to the capacity
+specified in the hashmap's type.
+
+Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a
+static capacity that does not increase as the map grows larger. Thus, this capacity
+is also the maximum possible element count that can be inserted into the hashmap.
+Due to hash collisions (modulo the hashmap length), it is likely the actual maximum
+element count will be lower than the full capacity.
+
+Example:
+
+#include_code capacity_example test_programs/execution_success/hashmap/src/main.nr rust
+
+### clear
+
+#include_code clear noir_stdlib/src/collections/map.nr rust
+
+Clears the hashmap, removing all key-value pairs from it.
+
+Example:
+
+#include_code clear_example test_programs/execution_success/hashmap/src/main.nr rust
+
+### contains_key
+
+#include_code contains_key noir_stdlib/src/collections/map.nr rust
+
+True if the hashmap contains the given key. Unlike `get`, this will not also return
+the value associated with the key.
+
+Example:
+
+#include_code contains_key_example test_programs/execution_success/hashmap/src/main.nr rust
+
+### entries
+
+#include_code entries noir_stdlib/src/collections/map.nr rust
+
+Returns a vector of each key-value pair present in the hashmap.
+
+The length of the returned vector is always equal to the length of the hashmap.
+
+Example:
+
+#include_code entries_example test_programs/execution_success/hashmap/src/main.nr rust
+
+### keys
+
+#include_code keys noir_stdlib/src/collections/map.nr rust
+
+Returns a vector of each key present in the hashmap.
+
+The length of the returned vector is always equal to the length of the hashmap.
+
+Example:
+
+#include_code keys_example test_programs/execution_success/hashmap/src/main.nr rust
+
+### values
+
+#include_code values noir_stdlib/src/collections/map.nr rust
+
+Returns a vector of each value present in the hashmap.
+
+The length of the returned vector is always equal to the length of the hashmap.
+
+Example:
+
+#include_code values_example test_programs/execution_success/hashmap/src/main.nr rust
+
+### iter_mut
+
+#include_code iter_mut noir_stdlib/src/collections/map.nr rust
+
+Iterates through each key-value pair of the HashMap, setting each key-value pair to the
+result returned from the given function.
+
+Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated
+through. If this is not desired, use `iter_values_mut` if only values need to be mutated,
+or `entries` if neither keys nor values need to be mutated.
+
+The iteration order is left unspecified. As a result, if two keys are mutated to become
+equal, which of the two values that will be present for the key in the resulting map is also unspecified.
+
+Example:
+
+#include_code iter_mut_example test_programs/execution_success/hashmap/src/main.nr rust
+
+### iter_keys_mut
+
+#include_code iter_keys_mut noir_stdlib/src/collections/map.nr rust
+
+Iterates through the HashMap, mutating each key to the result returned from
+the given function.
+
+Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated
+through. If only iteration is desired and the keys are not intended to be mutated,
+prefer using `entries` instead.
+
+The iteration order is left unspecified. As a result, if two keys are mutated to become
+equal, which of the two values that will be present for the key in the resulting map is also unspecified.
+
+Example:
+
+#include_code iter_keys_mut_example test_programs/execution_success/hashmap/src/main.nr rust
+
+### iter_values_mut
+
+#include_code iter_values_mut noir_stdlib/src/collections/map.nr rust
+
+Iterates through the HashMap, applying the given function to each value and mutating the
+value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut`
+because the keys are untouched and the underlying hashmap thus does not need to be reordered.
+
+Example:
+
+#include_code iter_values_mut_example test_programs/execution_success/hashmap/src/main.nr rust
+
+### retain
+
+#include_code retain noir_stdlib/src/collections/map.nr rust
+
+Retains only the key-value pairs for which the given function returns true.
+Any key-value pairs for which the function returns false will be removed from the map.
+
+Example:
+
+#include_code retain_example test_programs/execution_success/hashmap/src/main.nr rust
+
+## Trait Implementations
+
+### default
+
+#include_code default noir_stdlib/src/collections/map.nr rust
+
+Constructs an empty HashMap.
+
+Example:
+
+#include_code default_example test_programs/execution_success/hashmap/src/main.nr rust
+
+### eq
+
+#include_code eq noir_stdlib/src/collections/map.nr rust
+
+Checks if two HashMaps are equal.
+
+Example:
+
+#include_code eq_example test_programs/execution_success/hashmap/src/main.nr rust
diff --git a/noir_stdlib/src/collections/map.nr b/noir_stdlib/src/collections/map.nr
index 056299b4238..2d76acf1f3a 100644
--- a/noir_stdlib/src/collections/map.nr
+++ b/noir_stdlib/src/collections/map.nr
@@ -3,6 +3,7 @@ use crate::collections::vec::Vec;
use crate::option::Option;
use crate::default::Default;
use crate::hash::{Hash, Hasher, BuildHasher};
+use crate::collections::bounded_vec::BoundedVec;
// We use load factor α_max = 0.75.
// Upon exceeding it, assert will fail in order to inform the user
@@ -78,21 +79,26 @@ impl Slot {
// it is very unlikely to be after - performance will be heavily degraded.
impl HashMap {
// Creates a new instance of HashMap with specified BuildHasher.
+ // docs:start:with_hasher
pub fn with_hasher(_build_hasher: B) -> Self
where
B: BuildHasher {
+ // docs:end:with_hasher
let _table = [Slot::default(); N];
let _len = 0;
Self { _table, _len, _build_hasher }
}
// Clears the map, removing all key-value entries.
+ // docs:start:clear
pub fn clear(&mut self) {
+ // docs:end:clear
self._table = [Slot::default(); N];
self._len = 0;
}
// Returns true if the map contains a value for the specified key.
+ // docs:start:contains_key
pub fn contains_key(
self,
key: K
@@ -101,89 +107,80 @@ impl HashMap {
K: Hash + Eq,
B: BuildHasher,
H: Hasher {
+ // docs:end:contains_key
self.get(key).is_some()
}
// Returns true if the map contains no elements.
+ // docs:start:is_empty
pub fn is_empty(self) -> bool {
+ // docs:end:is_empty
self._len == 0
}
- // Get the Option<(K, V) array of valid entries
- // with a length of map capacity. First len() elements
- // are safe to unwrap_unchecked(), whilst remaining
- // are guaranteed to be Option::none().
- //
- // This design is reasoned by compile-time limitations and
- // temporary nested slices ban.
- pub fn entries(self) -> [Option<(K, V)>; N] {
- let mut entries = [Option::none(); N];
- let mut valid_amount = 0;
+ // Returns a BoundedVec of all valid entries in this HashMap.
+ // The length of the returned vector will always match the length of this HashMap.
+ // docs:start:entries
+ pub fn entries(self) -> BoundedVec<(K, V), N> {
+ // docs:end:entries
+ let mut entries = BoundedVec::new();
for slot in self._table {
if slot.is_valid() {
- entries[valid_amount] = slot.key_value();
- valid_amount += 1;
+ // SAFETY: slot.is_valid() should ensure there is a valid key-value pairing here
+ let key_value = slot.key_value().unwrap_unchecked();
+ entries.push(key_value);
}
}
- let msg = f"Amount of valid elements should have been {self._len} times, but got {valid_amount}.";
- assert(valid_amount == self._len, msg);
+ let msg = f"Amount of valid elements should have been {self._len} times, but got {entries.len()}.";
+ assert(entries.len() == self._len, msg);
entries
}
- // Get the Option array of valid keys
- // with a length of map capacity. First len() elements
- // are safe to unwrap_unchecked(), whilst remaining
- // are guaranteed to be Option::none().
- //
- // This design is reasoned by compile-time limitations and
- // temporary nested slices ban.
- pub fn keys(self) -> [Option; N] {
- let mut keys = [Option::none(); N];
- let mut valid_amount = 0;
+ // Returns a BoundedVec containing all the keys within this HashMap.
+ // The length of the returned vector will always match the length of this HashMap.
+ // docs:start:keys
+ pub fn keys(self) -> BoundedVec {
+ // docs:end:keys
+ let mut keys = BoundedVec::new();
for slot in self._table {
if slot.is_valid() {
let (key, _) = slot.key_value_unchecked();
- keys[valid_amount] = Option::some(key);
- valid_amount += 1;
+ keys.push(key);
}
}
- let msg = f"Amount of valid elements should have been {self._len} times, but got {valid_amount}.";
- assert(valid_amount == self._len, msg);
+ let msg = f"Amount of valid elements should have been {self._len} times, but got {keys.len()}.";
+ assert(keys.len() == self._len, msg);
keys
}
- // Get the Option array of valid values
- // with a length of map capacity. First len() elements
- // are safe to unwrap_unchecked(), whilst remaining
- // are guaranteed to be Option::none().
- //
- // This design is reasoned by compile-time limitations and
- // temporary nested slices ban.
- pub fn values(self) -> [Option; N] {
- let mut values = [Option::none(); N];
- let mut valid_amount = 0;
+ // Returns a BoundedVec containing all the values within this HashMap.
+ // The length of the returned vector will always match the length of this HashMap.
+ // docs:start:values
+ pub fn values(self) -> BoundedVec {
+ // docs:end:values
+ let mut values = BoundedVec::new();
for slot in self._table {
if slot.is_valid() {
let (_, value) = slot.key_value_unchecked();
- values[valid_amount] = Option::some(value);
- valid_amount += 1;
+ values.push(value);
}
}
- let msg = f"Amount of valid elements should have been {self._len} times, but got {valid_amount}.";
- assert(valid_amount == self._len, msg);
+ let msg = f"Amount of valid elements should have been {self._len} times, but got {values.len()}.";
+ assert(values.len() == self._len, msg);
values
}
// For each key-value entry applies mutator function.
+ // docs:start:iter_mut
pub fn iter_mut(
&mut self,
f: fn(K, V) -> (K, V)
@@ -192,12 +189,13 @@ impl HashMap {
K: Eq + Hash,
B: BuildHasher,
H: Hasher {
+ // docs:end:iter_mut
let mut entries = self.entries();
let mut new_map = HashMap::with_hasher(self._build_hasher);
for i in 0..N {
if i < self._len {
- let entry = entries[i].unwrap_unchecked();
+ let entry = entries.get_unchecked(i);
let (key, value) = f(entry.0, entry.1);
new_map.insert(key, value);
}
@@ -207,6 +205,7 @@ impl HashMap {
}
// For each key applies mutator function.
+ // docs:start:iter_keys_mut
pub fn iter_keys_mut(
&mut self,
f: fn(K) -> K
@@ -215,12 +214,13 @@ impl HashMap {
K: Eq + Hash,
B: BuildHasher,
H: Hasher {
+ // docs:end:iter_keys_mut
let mut entries = self.entries();
let mut new_map = HashMap::with_hasher(self._build_hasher);
for i in 0..N {
if i < self._len {
- let entry = entries[i].unwrap_unchecked();
+ let entry = entries.get_unchecked(i);
let (key, value) = (f(entry.0), entry.1);
new_map.insert(key, value);
}
@@ -230,7 +230,9 @@ impl HashMap {
}
// For each value applies mutator function.
+ // docs:start:iter_values_mut
pub fn iter_values_mut(&mut self, f: fn(V) -> V) {
+ // docs:end:iter_values_mut
for i in 0..N {
let mut slot = self._table[i];
if slot.is_valid() {
@@ -242,7 +244,9 @@ impl HashMap {
}
// Retains only the elements specified by the predicate.
+ // docs:start:retain
pub fn retain(&mut self, f: fn(K, V) -> bool) {
+ // docs:end:retain
for index in 0..N {
let mut slot = self._table[index];
if slot.is_valid() {
@@ -257,16 +261,21 @@ impl HashMap {
}
// Amount of active key-value entries.
+ // docs:start:len
pub fn len(self) -> u64 {
+ // docs:end:len
self._len
}
// Get the compile-time map capacity.
+ // docs:start:capacity
pub fn capacity(_self: Self) -> u64 {
+ // docs:end:capacity
N
}
// Get the value by key. If it does not exist, returns none().
+ // docs:start:get
pub fn get(
self,
key: K
@@ -275,6 +284,7 @@ impl HashMap {
K: Eq + Hash,
B: BuildHasher,
H: Hasher {
+ // docs:end:get
let mut result = Option::none();
let hash = self.hash(key);
@@ -300,6 +310,7 @@ impl HashMap {
}
// Insert key-value entry. In case key was already present, value is overridden.
+ // docs:start:insert
pub fn insert(
&mut self,
key: K,
@@ -309,6 +320,7 @@ impl HashMap {
K: Eq + Hash,
B: BuildHasher,
H: Hasher {
+ // docs:end:insert
self.assert_load_factor();
let hash = self.hash(key);
@@ -340,7 +352,8 @@ impl HashMap {
}
}
- // Remove key-value entry. If key is not present, HashMap remains unchanged.
+ // Removes a key-value entry. If key is not present, HashMap remains unchanged.
+ // docs:start:remove
pub fn remove(
&mut self,
key: K
@@ -349,6 +362,7 @@ impl HashMap {
K: Eq + Hash,
B: BuildHasher,
H: Hasher {
+ // docs:end:remove
let hash = self.hash(key);
let mut break = false;
@@ -409,6 +423,7 @@ impl HashMap {
// Equality class on HashMap has to test that they have
// equal sets of key-value entries,
// thus one is a subset of the other and vice versa.
+// docs:start:eq
impl Eq for HashMap
where
K: Eq + Hash,
@@ -416,7 +431,8 @@ where
B: BuildHasher,
H: Hasher
{
- fn eq(self, other: HashMap) -> bool{
+ fn eq(self, other: HashMap) -> bool {
+// docs:end:eq
let mut equal = false;
if self.len() == other.len(){
@@ -443,12 +459,14 @@ where
}
}
+// docs:start:default
impl Default for HashMap
where
B: BuildHasher + Default,
H: Hasher + Default
{
- fn default() -> Self{
+ fn default() -> Self {
+// docs:end:default
let _build_hasher = B::default();
let map: HashMap = HashMap::with_hasher(_build_hasher);
map
diff --git a/test_programs/execution_success/hashmap/src/main.nr b/test_programs/execution_success/hashmap/src/main.nr
index 597a5c0b7de..4d2cbd45993 100644
--- a/test_programs/execution_success/hashmap/src/main.nr
+++ b/test_programs/execution_success/hashmap/src/main.nr
@@ -37,6 +37,8 @@ fn main(input: [Entry; HASHMAP_LEN]) {
test_retain();
test_iterators();
test_mut_iterators();
+
+ doc_tests();
}
// Insert, get, remove.
@@ -154,9 +156,9 @@ fn test_iterators() {
hashmap.insert(5, 7);
hashmap.insert(11, 13);
- let keys: [K; 3] = cut(hashmap.keys()).map(|k: Option| k.unwrap_unchecked()).sort_via(K_CMP);
- let values: [V; 3] = cut(hashmap.values()).map(|v: Option| v.unwrap_unchecked()).sort_via(V_CMP);
- let entries: [(K, V); 3] = cut(hashmap.entries()).map(|e: Option<(K, V)>| e.unwrap_unchecked()).sort_via(KV_CMP);
+ let keys: [K; 3] = cut(hashmap.keys()).sort_via(K_CMP);
+ let values: [V; 3] = cut(hashmap.values()).sort_via(V_CMP);
+ let entries: [(K, V); 3] = cut(hashmap.entries()).sort_via(KV_CMP);
assert(keys == [2, 5, 11], "Got incorrect iteration of keys.");
assert(values == [3, 7, 13], "Got incorrect iteration of values.");
@@ -177,8 +179,8 @@ fn test_mut_iterators() {
let f = |v: V| -> V{ v * 5};
hashmap.iter_values_mut(f);
- let keys: [K; 3] = cut(hashmap.keys()).map(|k: Option| k.unwrap_unchecked()).sort_via(K_CMP);
- let values: [V; 3] = cut(hashmap.values()).map(|v: Option| v.unwrap_unchecked()).sort_via(V_CMP);
+ let keys: [K; 3] = cut(hashmap.keys()).sort_via(K_CMP);
+ let values: [V; 3] = cut(hashmap.values()).sort_via(V_CMP);
assert(keys == [6, 15, 33], f"Got incorrect iteration of keys: {keys}");
assert(values == [15, 35, 65], "Got incorrect iteration of values.");
@@ -186,7 +188,175 @@ fn test_mut_iterators() {
let f = |k: K, v: V| -> (K, V){(k * 2, v * 2)};
hashmap.iter_mut(f);
- let entries: [(K, V); 3] = cut(hashmap.entries()).map(|e: Option<(K, V)>| e.unwrap_unchecked()).sort_via(KV_CMP);
+ let entries: [(K, V); 3] = cut(hashmap.entries()).sort_via(KV_CMP);
assert(entries == [(12, 30), (30, 70), (66, 130)], "Got incorrect iteration of entries.");
}
+
+// docs:start:type_alias
+type MyMap = HashMap>;
+// docs:end:type_alias
+
+/// Tests examples from the stdlib hashmap documentation
+fn doc_tests() {
+ // docs:start:default_example
+ let hashmap: HashMap> = HashMap::default();
+ assert(hashmap.is_empty());
+ // docs:end:default_example
+
+ // docs:start:with_hasher_example
+ let my_hasher: BuildHasherDefault = Default::default();
+ let hashmap: HashMap> = HashMap::with_hasher(my_hasher);
+ assert(hashmap.is_empty());
+ // docs:end:with_hasher_example
+
+ // docs:start:insert_example
+ let mut map: HashMap> = HashMap::default();
+ map.insert(12, 42);
+ assert(map.len() == 1);
+ // docs:end:insert_example
+
+ get_example(map);
+
+ // docs:start:remove_example
+ map.remove(12);
+ assert(map.is_empty());
+
+ // If a key was not present in the map, remove does nothing
+ map.remove(12);
+ assert(map.is_empty());
+ // docs:end:remove_example
+
+ // docs:start:is_empty_example
+ assert(map.is_empty());
+
+ map.insert(1, 2);
+ assert(!map.is_empty());
+
+ map.remove(1);
+ assert(map.is_empty());
+ // docs:end:is_empty_example
+
+ // docs:start:len_example
+ // This is equivalent to checking map.is_empty()
+ assert(map.len() == 0);
+
+ map.insert(1, 2);
+ map.insert(3, 4);
+ map.insert(5, 6);
+ assert(map.len() == 3);
+
+ // 3 was already present as a key in the hash map, so the length is unchanged
+ map.insert(3, 7);
+ assert(map.len() == 3);
+
+ map.remove(1);
+ assert(map.len() == 2);
+ // docs:end:len_example
+
+ // docs:start:capacity_example
+ let empty_map: HashMap> = HashMap::default();
+ assert(empty_map.len() == 0);
+ assert(empty_map.capacity() == 42);
+ // docs:end:capacity_example
+
+ // docs:start:clear_example
+ assert(!map.is_empty());
+ map.clear();
+ assert(map.is_empty());
+ // docs:end:clear_example
+
+ // docs:start:contains_key_example
+ if map.contains_key(7) {
+ let value = map.get(7);
+ assert(value.is_some());
+ } else {
+ println("No value for key 7!");
+ }
+ // docs:end:contains_key_example
+
+ entries_examples(map);
+ iter_examples(map);
+
+ // docs:start:retain_example
+ map.retain(|k, v| (k != 0) & (v != 0));
+ // docs:end:retain_example
+
+ // docs:start:eq_example
+ let mut map1: HashMap> = HashMap::default();
+ let mut map2: HashMap> = HashMap::default();
+
+ map1.insert(1, 2);
+ map1.insert(3, 4);
+
+ map2.insert(3, 4);
+ map2.insert(1, 2);
+
+ assert(map1 == map2);
+ // docs:end:eq_example
+}
+
+// docs:start:get_example
+fn get_example(map: HashMap>) {
+ let x = map.get(12);
+
+ if x.is_some() {
+ assert(x.unwrap() == 42);
+ }
+}
+// docs:end:get_example
+
+fn entries_examples(map: HashMap>) {
+ // docs:start:entries_example
+ let entries = map.entries();
+
+ // The length of a hashmap may not be compile-time known, so we
+ // need to loop over its capacity instead
+ for i in 0..map.capacity() {
+ if i < entries.len() {
+ let (key, value) = entries.get(i);
+ println(f"{key} -> {value}");
+ }
+ }
+ // docs:end:entries_example
+
+ // docs:start:keys_example
+ let keys = map.keys();
+
+ for i in 0..keys.max_len() {
+ if i < keys.len() {
+ let key = keys.get_unchecked(i);
+ let value = map.get(key).unwrap_unchecked();
+ println(f"{key} -> {value}");
+ }
+ }
+ // docs:end:keys_example
+
+ // docs:start:values_example
+ let values = map.values();
+
+ for i in 0..values.max_len() {
+ if i < values.len() {
+ let value = values.get_unchecked(i);
+ println(f"Found value {value}");
+ }
+ }
+ // docs:end:values_example
+}
+
+fn iter_examples(mut map: HashMap>) {
+ // docs:start:iter_mut_example
+ // Add 1 to each key in the map, and double the value associated with that key.
+ map.iter_mut(|k, v| (k + 1, v * 2));
+ // docs:end:iter_mut_example
+
+ // docs:start:iter_keys_mut_example
+ // Double each key, leaving the value associated with that key untouched
+ map.iter_keys_mut(|k| k * 2);
+ // docs:end:iter_keys_mut_example
+
+ // docs:start:iter_values_mut_example
+ // Halve each value
+ map.iter_values_mut(|v| v / 2);
+ // docs:end:iter_values_mut_example
+}
diff --git a/test_programs/execution_success/hashmap/src/utils.nr b/test_programs/execution_success/hashmap/src/utils.nr
index 45c9ca9bbf7..ee73245a902 100644
--- a/test_programs/execution_success/hashmap/src/utils.nr
+++ b/test_programs/execution_success/hashmap/src/utils.nr
@@ -1,10 +1,10 @@
-// Compile-time: cuts the M first elements from the [T; N] array.
-pub(crate) fn cut(input: [T; N]) -> [T; M] {
- assert(M as u64 < N as u64, "M should be less than N.");
+// Compile-time: cuts the M first elements from the BoundedVec.
+pub(crate) fn cut(input: BoundedVec) -> [T; M] {
+ assert(M < N, "M should be less than N.");
- let mut new = [dep::std::unsafe::zeroed(); M];
+ let mut new = BoundedVec::new();
for i in 0..M {
- new[i] = input[i];
+ new.push(input.get(i));
}
- new
+ new.storage()
}
From f988d020e43cdf36a38613f2052d4518de39193a Mon Sep 17 00:00:00 2001
From: Maxim Vezenov
Date: Fri, 8 Mar 2024 20:03:50 +0000
Subject: [PATCH 04/14] fix(ssa): Handle mergers of slices returned from calls
(#4496)
# Description
## Problem\*
Resolves #4418
## Summary\*
We need to add handling for slices returned from `ToRadix` and `ToBits`.
~~This PR does two fixes.~~
EDIT: After moving to the iterative flattening appraoch w are now
accurately tracking slice capacities when they are the parameters of
blocks.
## Additional Context
## Documentation\*
Check one:
- [ ] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[Exceptional Case]** Documentation to be submitted in a separate
PR.
# PR Checklist\*
- [X] I have tested the changes locally.
- [X] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
---
.../ssa/opt/flatten_cfg/capacity_tracker.rs | 32 ++++++++++++++++---
.../execution_success/slices/src/main.nr | 26 +++++++++++++++
2 files changed, 53 insertions(+), 5 deletions(-)
diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/capacity_tracker.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/capacity_tracker.rs
index 7cd0fe3084e..bdfc04f0bbe 100644
--- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/capacity_tracker.rs
+++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/capacity_tracker.rs
@@ -5,6 +5,7 @@ use crate::ssa::ir::{
value::{Value, ValueId},
};
+use acvm::FieldElement;
use fxhash::FxHashMap as HashMap;
pub(crate) struct SliceCapacityTracker<'a> {
@@ -62,21 +63,27 @@ impl<'a> SliceCapacityTracker<'a> {
| Intrinsic::SlicePushFront
| Intrinsic::SlicePopBack
| Intrinsic::SliceInsert
- | Intrinsic::SliceRemove => (1, 1),
+ | Intrinsic::SliceRemove => (Some(1), 1),
// `pop_front` returns the popped element, and then the respective slice.
// This means in the case of a slice with structs, the result index of the popped slice
// will change depending on the number of elements in the struct.
// For example, a slice with four elements will look as such in SSA:
// v3, v4, v5, v6, v7, v8 = call slice_pop_front(v1, v2)
// where v7 is the slice length and v8 is the popped slice itself.
- Intrinsic::SlicePopFront => (1, results.len() - 1),
+ Intrinsic::SlicePopFront => (Some(1), results.len() - 1),
+ // The slice capacity of these intrinsics is not determined by the arguments of the function.
+ Intrinsic::ToBits(_) | Intrinsic::ToRadix(_) => (None, 1),
_ => return,
};
- let slice_contents = arguments[argument_index];
+ let result_slice = results[result_index];
match intrinsic {
Intrinsic::SlicePushBack
| Intrinsic::SlicePushFront
| Intrinsic::SliceInsert => {
+ let argument_index = argument_index
+ .expect("ICE: Should have an argument index for slice intrinsics");
+ let slice_contents = arguments[argument_index];
+
for arg in &arguments[(argument_index + 1)..] {
let element_typ = self.dfg.type_of_value(*arg);
if element_typ.contains_slice_element() {
@@ -85,20 +92,35 @@ impl<'a> SliceCapacityTracker<'a> {
}
if let Some(contents_capacity) = slice_sizes.get(&slice_contents) {
let new_capacity = *contents_capacity + 1;
- slice_sizes.insert(results[result_index], new_capacity);
+ slice_sizes.insert(result_slice, new_capacity);
}
}
Intrinsic::SlicePopBack
| Intrinsic::SliceRemove
| Intrinsic::SlicePopFront => {
+ let argument_index = argument_index
+ .expect("ICE: Should have an argument index for slice intrinsics");
+ let slice_contents = arguments[argument_index];
+
// We do not decrement the size on intrinsics that could remove values from a slice.
// This is because we could potentially go back to the smaller slice and not fill in dummies.
// This pass should be tracking the potential max that a slice ***could be***
if let Some(contents_capacity) = slice_sizes.get(&slice_contents) {
let new_capacity = *contents_capacity - 1;
- slice_sizes.insert(results[result_index], new_capacity);
+ slice_sizes.insert(result_slice, new_capacity);
}
}
+ Intrinsic::ToBits(_) => {
+ // Compiler sanity check
+ assert!(matches!(self.dfg.type_of_value(result_slice), Type::Slice(_)));
+ slice_sizes.insert(result_slice, FieldElement::max_num_bits() as usize);
+ }
+ Intrinsic::ToRadix(_) => {
+ // Compiler sanity check
+ assert!(matches!(self.dfg.type_of_value(result_slice), Type::Slice(_)));
+ slice_sizes
+ .insert(result_slice, FieldElement::max_num_bytes() as usize);
+ }
_ => {}
}
}
diff --git a/test_programs/execution_success/slices/src/main.nr b/test_programs/execution_success/slices/src/main.nr
index 6823bf05d96..e44edf872a4 100644
--- a/test_programs/execution_success/slices/src/main.nr
+++ b/test_programs/execution_success/slices/src/main.nr
@@ -51,6 +51,8 @@ fn main(x: Field, y: pub Field) {
regression_merge_slices(x, y);
regression_2370();
+ regression_4418(x);
+ regression_slice_call_result(x, y);
regression_4506();
}
// Ensure that slices of struct/tuple values work.
@@ -300,6 +302,30 @@ fn regression_2370() {
slice = [1, 2, 3];
}
+fn regression_4418(x: Field) {
+ let mut crash = x.to_be_bytes(32);
+
+ if x != 0 {
+ crash[0] = 10;
+ }
+}
+
+fn regression_slice_call_result(x: Field, y: Field) {
+ let mut slice = merge_slices_return(x, y);
+ if x != 0 {
+ slice = slice.push_back(5);
+ slice = slice.push_back(10);
+ } else {
+ slice = slice.push_back(5);
+ }
+ assert(slice.len() == 5);
+ assert(slice[0] == 0);
+ assert(slice[1] == 0);
+ assert(slice[2] == 10);
+ assert(slice[3] == 5);
+ assert(slice[4] == 10);
+}
+
fn regression_4506() {
let slice: [Field] = [1, 2, 3];
assert(slice == slice);
From 810d1958dcff55deac1867d3484bbc8bbe43d0fe Mon Sep 17 00:00:00 2001
From: Tom French <15848336+TomAFrench@users.noreply.github.com>
Date: Mon, 11 Mar 2024 09:45:58 +0000
Subject: [PATCH 05/14] chore(ci): fix JS publishing workflow checking out
inconsistent commits (#4493)
---
.github/JS_PUBLISH_FAILED.md | 2 +-
.github/workflows/publish-es-packages.yml | 8 ++++++--
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/.github/JS_PUBLISH_FAILED.md b/.github/JS_PUBLISH_FAILED.md
index 5b9f79aac1f..9adba2776c8 100644
--- a/.github/JS_PUBLISH_FAILED.md
+++ b/.github/JS_PUBLISH_FAILED.md
@@ -1,6 +1,6 @@
---
title: "JS packages failed to publish"
-assignees: TomAFrench kevaundray savio-sou
+assignees: TomAFrench kevaundray Savio-Sou
labels: js
---
diff --git a/.github/workflows/publish-es-packages.yml b/.github/workflows/publish-es-packages.yml
index f72a97b2684..470db3b78f7 100644
--- a/.github/workflows/publish-es-packages.yml
+++ b/.github/workflows/publish-es-packages.yml
@@ -20,7 +20,9 @@ jobs:
steps:
- name: Checkout Noir repo
uses: actions/checkout@v4
-
+ with:
+ ref: ${{ inputs.noir-ref }}
+
- name: Setup toolchain
uses: dtolnay/rust-toolchain@1.73.0
@@ -87,6 +89,8 @@ jobs:
steps:
- name: Checkout sources
uses: actions/checkout@v4
+ with:
+ ref: ${{ inputs.noir-ref }}
- name: Setup toolchain
uses: dtolnay/rust-toolchain@1.73.0
@@ -164,4 +168,4 @@ jobs:
WORKFLOW_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
with:
update_existing: true
- filename: .github/JS_PUBLISH_FAILED.md
\ No newline at end of file
+ filename: .github/JS_PUBLISH_FAILED.md
From 8d6667773db13392a97a610cb5b996bffdf3e405 Mon Sep 17 00:00:00 2001
From: jfecher
Date: Mon, 11 Mar 2024 05:30:26 -0500
Subject: [PATCH 06/14] chore: Move `check_method_signatures` to type checking
phase (#4516)
# Description
## Problem\*
Resolves https://github.com/noir-lang/noir/issues/3583
## Summary\*
Resolves an old TODO by moving this function to the type checking phase.
I've rewritten the function since it was binding certain type variables
then unbound them afterward. I've changed that to substitute new
variables and only use `try_unify` to avoid accidentally binding
anything.
## Additional Context
## Documentation\*
Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[Exceptional Case]** Documentation to be submitted in a separate
PR.
# PR Checklist\*
- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
---
.../src/hir/def_collector/dc_crate.rs | 176 +++---------------
.../src/hir/def_collector/errors.rs | 20 --
.../src/hir/resolution/resolver.rs | 10 +-
.../src/hir/resolution/traits.rs | 17 +-
.../src/hir/type_check/errors.rs | 32 +++-
.../noirc_frontend/src/hir/type_check/expr.rs | 4 +-
.../noirc_frontend/src/hir/type_check/mod.rs | 159 +++++++++++++++-
.../noirc_frontend/src/hir_def/function.rs | 10 +-
compiler/noirc_frontend/src/tests.rs | 16 +-
9 files changed, 239 insertions(+), 205 deletions(-)
diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs
index 27b1d376f11..62febc89899 100644
--- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs
+++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs
@@ -5,13 +5,14 @@ use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId};
use crate::hir::resolution::errors::ResolverError;
use crate::hir::resolution::import::{resolve_import, ImportDirective};
-use crate::hir::resolution::resolver::Resolver;
use crate::hir::resolution::{
collect_impls, collect_trait_impls, path_resolver, resolve_free_functions, resolve_globals,
resolve_impls, resolve_structs, resolve_trait_by_path, resolve_trait_impls, resolve_traits,
resolve_type_aliases,
};
-use crate::hir::type_check::{type_check_func, TypeCheckError, TypeChecker};
+use crate::hir::type_check::{
+ check_trait_impl_method_matches_declaration, type_check_func, TypeCheckError, TypeChecker,
+};
use crate::hir::Context;
use crate::macros_api::{MacroError, MacroProcessor};
@@ -20,8 +21,7 @@ use crate::node_interner::{FuncId, GlobalId, NodeInterner, StructId, TraitId, Ty
use crate::parser::{ParserError, SortedModule};
use crate::{
ExpressionKind, Ident, LetStatement, Literal, NoirFunction, NoirStruct, NoirTrait,
- NoirTypeAlias, Path, PathKind, Type, TypeBindings, UnresolvedGenerics,
- UnresolvedTraitConstraint, UnresolvedType,
+ NoirTypeAlias, Path, PathKind, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType,
};
use fm::FileId;
use iter_extended::vecmap;
@@ -368,12 +368,12 @@ impl DefCollector {
&mut errors,
));
- functions.extend(resolve_trait_impls(
+ let impl_functions = resolve_trait_impls(
context,
def_collector.collected_traits_impls,
crate_id,
&mut errors,
- ));
+ );
for macro_processor in macro_processors {
macro_processor.process_typed_ast(&crate_id, context).unwrap_or_else(
@@ -387,6 +387,8 @@ impl DefCollector {
errors.extend(type_check_globals(&mut context.def_interner, resolved_globals.globals));
errors.extend(type_check_functions(&mut context.def_interner, functions));
+ errors.extend(type_check_trait_impl_signatures(&mut context.def_interner, &impl_functions));
+ errors.extend(type_check_functions(&mut context.def_interner, impl_functions));
errors
}
}
@@ -468,158 +470,24 @@ fn type_check_functions(
.into_iter()
.flat_map(|(file, func)| {
type_check_func(interner, func)
- .iter()
- .cloned()
+ .into_iter()
.map(|e| (e.into(), file))
.collect::>()
})
.collect()
}
-// TODO(vitkov): Move this out of here and into type_check
-#[allow(clippy::too_many_arguments)]
-pub(crate) fn check_methods_signatures(
- resolver: &mut Resolver,
- impl_methods: &[(FileId, FuncId)],
- trait_id: TraitId,
- trait_name_span: Span,
- // These are the generics on the trait itself from the impl.
- // E.g. in `impl Foo for Bar`, this is `vec![A, B]`.
- trait_generics: Vec,
- trait_impl_generic_count: usize,
- file_id: FileId,
- errors: &mut Vec<(CompilationError, FileId)>,
-) {
- let self_type = resolver.get_self_type().expect("trait impl must have a Self type").clone();
- let trait_generics = vecmap(trait_generics, |typ| resolver.resolve_type(typ));
-
- // Temporarily bind the trait's Self type to self_type so we can type check
- let the_trait = resolver.interner.get_trait_mut(trait_id);
- the_trait.self_type_typevar.bind(self_type);
-
- if trait_generics.len() != the_trait.generics.len() {
- let error = DefCollectorErrorKind::MismatchGenericCount {
- actual_generic_count: trait_generics.len(),
- expected_generic_count: the_trait.generics.len(),
- // Preferring to use 'here' over a more precise term like 'this reference'
- // to try to make the error easier to understand for newer users.
- location: "here it",
- origin: the_trait.name.to_string(),
- span: trait_name_span,
- };
- errors.push((error.into(), file_id));
- }
-
- // We also need to bind the traits generics to the trait's generics on the impl
- for (generic, binding) in the_trait.generics.iter().zip(trait_generics) {
- generic.bind(binding);
- }
-
- // Temporarily take the trait's methods so we can use both them and a mutable reference
- // to the interner within the loop.
- let trait_methods = std::mem::take(&mut the_trait.methods);
-
- for (file_id, func_id) in impl_methods {
- let func_name = resolver.interner.function_name(func_id).to_owned();
-
- // This is None in the case where the impl block has a method that's not part of the trait.
- // If that's the case, a `MethodNotInTrait` error has already been thrown, and we can ignore
- // the impl method, since there's nothing in the trait to match its signature against.
- if let Some(trait_method) =
- trait_methods.iter().find(|method| method.name.0.contents == func_name)
- {
- let impl_method = resolver.interner.function_meta(func_id);
-
- let impl_method_generic_count =
- impl_method.typ.generic_count() - trait_impl_generic_count;
-
- // We subtract 1 here to account for the implicit generic `Self` type that is on all
- // traits (and thus trait methods) but is not required (or allowed) for users to specify.
- let the_trait = resolver.interner.get_trait(trait_id);
- let trait_method_generic_count =
- trait_method.generics().len() - 1 - the_trait.generics.len();
-
- if impl_method_generic_count != trait_method_generic_count {
- let trait_name = resolver.interner.get_trait(trait_id).name.clone();
-
- let error = DefCollectorErrorKind::MismatchGenericCount {
- actual_generic_count: impl_method_generic_count,
- expected_generic_count: trait_method_generic_count,
- origin: format!("{}::{}", trait_name, func_name),
- location: "this method",
- span: impl_method.location.span,
- };
- errors.push((error.into(), *file_id));
- }
-
- // This instantiation is technically not needed. We could bind each generic in the
- // trait function to the impl's corresponding generic but to do so we'd have to rely
- // on the trait function's generics being first in the generic list, since the same
- // list also contains the generic `Self` variable, and any generics on the trait itself.
- //
- // Instantiating the impl method's generics here instead is a bit less precise but
- // doesn't rely on any orderings that may be changed.
- let impl_function_type = impl_method.typ.instantiate(resolver.interner).0;
-
- let mut bindings = TypeBindings::new();
- let mut typecheck_errors = Vec::new();
-
- if let Type::Function(impl_params, impl_return, _) = impl_function_type.as_monotype() {
- if trait_method.arguments().len() != impl_params.len() {
- let error = DefCollectorErrorKind::MismatchTraitImplementationNumParameters {
- actual_num_parameters: impl_method.parameters.0.len(),
- expected_num_parameters: trait_method.arguments().len(),
- trait_name: resolver.interner.get_trait(trait_id).name.to_string(),
- method_name: func_name.to_string(),
- span: impl_method.location.span,
- };
- errors.push((error.into(), *file_id));
- }
-
- // Check the parameters of the impl method against the parameters of the trait method
- let args = trait_method.arguments().iter();
- let args_and_params = args.zip(impl_params).zip(&impl_method.parameters.0);
-
- for (parameter_index, ((expected, actual), (hir_pattern, _, _))) in
- args_and_params.enumerate()
- {
- if expected.try_unify(actual, &mut bindings).is_err() {
- typecheck_errors.push(TypeCheckError::TraitMethodParameterTypeMismatch {
- method_name: func_name.to_string(),
- expected_typ: expected.to_string(),
- actual_typ: actual.to_string(),
- parameter_span: hir_pattern.span(),
- parameter_index: parameter_index + 1,
- });
- }
- }
-
- if trait_method.return_type().try_unify(impl_return, &mut bindings).is_err() {
- let impl_method = resolver.interner.function_meta(func_id);
- let ret_type_span = impl_method.return_type.get_type().span;
- let expr_span = ret_type_span.expect("return type must always have a span");
-
- let expected_typ = trait_method.return_type().to_string();
- let expr_typ = impl_method.return_type().to_string();
- let error = TypeCheckError::TypeMismatch { expr_typ, expected_typ, expr_span };
- typecheck_errors.push(error);
- }
- } else {
- unreachable!(
- "impl_function_type is not a function type, it is: {impl_function_type}"
- );
- }
-
- errors.extend(typecheck_errors.iter().cloned().map(|e| (e.into(), *file_id)));
- }
- }
-
- // Now unbind `Self` and the trait's generics
- let the_trait = resolver.interner.get_trait_mut(trait_id);
- the_trait.set_methods(trait_methods);
- the_trait.self_type_typevar.unbind(the_trait.self_type_typevar_id);
-
- for generic in &the_trait.generics {
- generic.unbind(generic.id());
- }
+fn type_check_trait_impl_signatures(
+ interner: &mut NodeInterner,
+ file_func_ids: &[(FileId, FuncId)],
+) -> Vec<(CompilationError, fm::FileId)> {
+ file_func_ids
+ .iter()
+ .flat_map(|(file, func)| {
+ check_trait_impl_method_matches_declaration(interner, *func)
+ .into_iter()
+ .map(|e| (e.into(), *file))
+ .collect::>()
+ })
+ .collect()
}
diff --git a/compiler/noirc_frontend/src/hir/def_collector/errors.rs b/compiler/noirc_frontend/src/hir/def_collector/errors.rs
index de45be48c4e..29daf5d6369 100644
--- a/compiler/noirc_frontend/src/hir/def_collector/errors.rs
+++ b/compiler/noirc_frontend/src/hir/def_collector/errors.rs
@@ -41,14 +41,6 @@ pub enum DefCollectorErrorKind {
OverlappingImplNote { span: Span },
#[error("Cannot `impl` a type defined outside the current crate")]
ForeignImpl { span: Span, type_name: String },
- #[error("Mismatched number of parameters in trait implementation")]
- MismatchTraitImplementationNumParameters {
- actual_num_parameters: usize,
- expected_num_parameters: usize,
- trait_name: String,
- method_name: String,
- span: Span,
- },
#[error("Mismatched number of generics in {location}")]
MismatchGenericCount {
actual_generic_count: usize,
@@ -176,18 +168,6 @@ impl From for Diagnostic {
"".to_string(),
trait_path.span(),
),
- DefCollectorErrorKind::MismatchTraitImplementationNumParameters {
- expected_num_parameters,
- actual_num_parameters,
- trait_name,
- method_name,
- span,
- } => {
- let plural = if expected_num_parameters == 1 { "" } else { "s" };
- let primary_message = format!(
- "`{trait_name}::{method_name}` expects {expected_num_parameters} parameter{plural}, but this method has {actual_num_parameters}");
- Diagnostic::simple_error(primary_message, "".to_string(), span)
- }
DefCollectorErrorKind::MismatchGenericCount {
actual_generic_count,
expected_generic_count,
diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs
index 322891f0ae9..875d0ceb85e 100644
--- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs
+++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs
@@ -217,6 +217,7 @@ impl<'a> Resolver<'a> {
pub fn resolve_trait_function(
&mut self,
name: &Ident,
+ generics: &UnresolvedGenerics,
parameters: &[(Ident, UnresolvedType)],
return_type: &FunctionReturnType,
where_clause: &[UnresolvedTraitConstraint],
@@ -237,7 +238,7 @@ impl<'a> Resolver<'a> {
is_internal: false,
is_unconstrained: false,
visibility: FunctionVisibility::Public, // Trait functions are always public
- generics: Vec::new(), // self.generics should already be set
+ generics: generics.clone(),
parameters: vecmap(parameters, |(name, typ)| Param {
visibility: Visibility::Private,
pattern: Pattern::Identifier(name.clone()),
@@ -975,11 +976,18 @@ impl<'a> Resolver<'a> {
self.handle_function_type(&func_id);
self.handle_is_function_internal(&func_id);
+ let direct_generics = func.def.generics.iter();
+ let direct_generics = direct_generics
+ .filter_map(|generic| self.find_generic(&generic.0.contents))
+ .map(|(name, typevar, _span)| (name.clone(), typevar.clone()))
+ .collect();
+
FuncMeta {
name: name_ident,
kind: func.kind,
location,
typ,
+ direct_generics,
trait_impl: self.current_trait_impl,
parameters: parameters.into(),
return_type: func.def.return_type.clone(),
diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs
index 8f966be312b..5d546954f0d 100644
--- a/compiler/noirc_frontend/src/hir/resolution/traits.rs
+++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs
@@ -8,9 +8,7 @@ use crate::{
graph::CrateId,
hir::{
def_collector::{
- dc_crate::{
- check_methods_signatures, CompilationError, UnresolvedTrait, UnresolvedTraitImpl,
- },
+ dc_crate::{CompilationError, UnresolvedTrait, UnresolvedTraitImpl},
errors::{DefCollectorErrorKind, DuplicateType},
},
def_map::{CrateDefMap, ModuleDefId, ModuleId},
@@ -131,6 +129,7 @@ fn resolve_trait_methods(
let func_id = unresolved_trait.method_ids[&name.0.contents];
let (_, func_meta) = resolver.resolve_trait_function(
name,
+ generics,
parameters,
return_type,
where_clause,
@@ -365,6 +364,7 @@ pub(crate) fn resolve_trait_by_path(
Err(_) => Err(DefCollectorErrorKind::TraitNotFound { trait_path: path }),
}
}
+
pub(crate) fn resolve_trait_impls(
context: &mut Context,
traits: Vec,
@@ -424,17 +424,6 @@ pub(crate) fn resolve_trait_impls(
new_resolver.set_self_type(Some(self_type.clone()));
if let Some(trait_id) = maybe_trait_id {
- check_methods_signatures(
- &mut new_resolver,
- &impl_methods,
- trait_id,
- trait_impl.trait_path.span(),
- trait_impl.trait_generics,
- trait_impl.generics.len(),
- trait_impl.file_id,
- errors,
- );
-
let where_clause = trait_impl
.where_clause
.into_iter()
diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs
index cba2400441f..7eacc8eb2d1 100644
--- a/compiler/noirc_frontend/src/hir/type_check/errors.rs
+++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs
@@ -44,7 +44,7 @@ pub enum TypeCheckError {
#[error("Expected type {expected} is not the same as {actual}")]
TypeMismatchWithSource { expected: Type, actual: Type, span: Span, source: Source },
#[error("Expected {expected:?} found {found:?}")]
- ArityMisMatch { expected: u16, found: u16, span: Span },
+ ArityMisMatch { expected: usize, found: usize, span: Span },
#[error("Return type in a function cannot be public")]
PublicReturnType { typ: Type, span: Span },
#[error("Cannot cast type {from}, 'as' is only for primitive field or integer types")]
@@ -53,8 +53,10 @@ pub enum TypeCheckError {
ExpectedFunction { found: Type, span: Span },
#[error("Type {lhs_type} has no member named {field_name}")]
AccessUnknownMember { lhs_type: Type, field_name: String, span: Span },
- #[error("Function expects {expected} parameters but {found} given")]
+ #[error("Function expects {expected} parameters but {found} were given")]
ParameterCountMismatch { expected: usize, found: usize, span: Span },
+ #[error("{item} expects {expected} generics but {found} were given")]
+ GenericCountMismatch { item: String, expected: usize, found: usize, span: Span },
#[error("Only integer and Field types may be casted to")]
UnsupportedCast { span: Span },
#[error("Index {index} is out of bounds for this tuple {lhs_type} of length {length}")]
@@ -124,6 +126,14 @@ pub enum TypeCheckError {
UnconstrainedSliceReturnToConstrained { span: Span },
#[error("Only sized types may be used in the entry point to a program")]
InvalidTypeForEntryPoint { span: Span },
+ #[error("Mismatched number of parameters in trait implementation")]
+ MismatchTraitImplNumParameters {
+ actual_num_parameters: usize,
+ expected_num_parameters: usize,
+ trait_name: String,
+ method_name: String,
+ span: Span,
+ },
}
impl TypeCheckError {
@@ -193,6 +203,12 @@ impl From for Diagnostic {
let msg = format!("Function expects {expected} parameter{empty_or_s} but {found} {was_or_were} given");
Diagnostic::simple_error(msg, String::new(), span)
}
+ TypeCheckError::GenericCountMismatch { item, expected, found, span } => {
+ let empty_or_s = if expected == 1 { "" } else { "s" };
+ let was_or_were = if found == 1 { "was" } else { "were" };
+ let msg = format!("{item} expects {expected} generic{empty_or_s} but {found} {was_or_were} given");
+ Diagnostic::simple_error(msg, String::new(), span)
+ }
TypeCheckError::InvalidCast { span, .. }
| TypeCheckError::ExpectedFunction { span, .. }
| TypeCheckError::AccessUnknownMember { span, .. }
@@ -289,6 +305,18 @@ impl From for Diagnostic {
TypeCheckError::InvalidTypeForEntryPoint { span } => Diagnostic::simple_error(
"Only sized types may be used in the entry point to a program".to_string(),
"Slices, references, or any type containing them may not be used in main or a contract function".to_string(), span),
+ TypeCheckError::MismatchTraitImplNumParameters {
+ expected_num_parameters,
+ actual_num_parameters,
+ trait_name,
+ method_name,
+ span,
+ } => {
+ let plural = if expected_num_parameters == 1 { "" } else { "s" };
+ let primary_message = format!(
+ "`{trait_name}::{method_name}` expects {expected_num_parameters} parameter{plural}, but this method has {actual_num_parameters}");
+ Diagnostic::simple_error(primary_message, "".to_string(), span)
+ }
}
}
}
diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs
index c5287d35caf..7219f4d09c6 100644
--- a/compiler/noirc_frontend/src/hir/type_check/expr.rs
+++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs
@@ -1040,9 +1040,9 @@ impl<'interner> TypeChecker<'interner> {
}
ret
}
+ // ignoring env for subtype on purpose
Type::Function(parameters, ret, _env) => {
- // ignoring env for subtype on purpose
- self.bind_function_type_impl(parameters.as_ref(), ret.as_ref(), args.as_ref(), span)
+ self.bind_function_type_impl(¶meters, &ret, &args, span)
}
Type::Error => Type::Error,
found => {
diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs
index aab793ec867..ab759f454e5 100644
--- a/compiler/noirc_frontend/src/hir/type_check/mod.rs
+++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs
@@ -12,11 +12,17 @@ mod expr;
mod stmt;
pub use errors::TypeCheckError;
+use noirc_errors::Span;
use crate::{
- hir_def::{expr::HirExpression, function::Param, stmt::HirStatement, traits::TraitConstraint},
+ hir_def::{
+ expr::HirExpression,
+ function::{Param, Parameters},
+ stmt::HirStatement,
+ traits::TraitConstraint,
+ },
node_interner::{ExprId, FuncId, GlobalId, NodeInterner},
- Type,
+ Type, TypeBindings,
};
use self::errors::Source;
@@ -179,6 +185,154 @@ fn function_info(interner: &NodeInterner, function_body_id: &ExprId) -> (noirc_e
(expr_span, empty_function)
}
+/// Checks that the type of a function in a trait impl matches the type
+/// of the corresponding function declaration in the trait itself.
+///
+/// To do this, given a trait such as:
+/// `trait Foo { fn foo(...); }`
+///
+/// And an impl such as:
+/// `impl Foo for Bar { fn foo(...); } `
+///
+/// We have to substitute:
+/// - Self for Bar
+/// - A for D
+/// - B for F
+///
+/// Before we can type check. Finally, we must also check that the unification
+/// result does not introduce any new bindings. This can happen if the impl
+/// function's type is more general than that of the trait function. E.g.
+/// `fn baz(a: A, b: B)` when the impl required `fn baz(a: A, b: A)`.
+///
+/// This does not type check the body of the impl function.
+pub(crate) fn check_trait_impl_method_matches_declaration(
+ interner: &mut NodeInterner,
+ function: FuncId,
+) -> Vec {
+ let meta = interner.function_meta(&function);
+ let method_name = interner.function_name(&function);
+ let mut errors = Vec::new();
+
+ let definition_type = meta.typ.as_monotype();
+
+ let impl_ =
+ meta.trait_impl.expect("Trait impl function should have a corresponding trait impl");
+ let impl_ = interner.get_trait_implementation(impl_);
+ let impl_ = impl_.borrow();
+ let trait_info = interner.get_trait(impl_.trait_id);
+
+ let mut bindings = TypeBindings::new();
+ bindings.insert(
+ trait_info.self_type_typevar_id,
+ (trait_info.self_type_typevar.clone(), impl_.typ.clone()),
+ );
+
+ if trait_info.generics.len() != impl_.trait_generics.len() {
+ let expected = trait_info.generics.len();
+ let found = impl_.trait_generics.len();
+ let span = impl_.ident.span();
+ let item = trait_info.name.to_string();
+ errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, span });
+ }
+
+ // Substitute each generic on the trait with the corresponding generic on the impl
+ for (generic, arg) in trait_info.generics.iter().zip(&impl_.trait_generics) {
+ bindings.insert(generic.id(), (generic.clone(), arg.clone()));
+ }
+
+ // If this is None, the trait does not have the corresponding function.
+ // This error should have been caught in name resolution already so we don't
+ // issue an error for it here.
+ if let Some(trait_fn_id) = trait_info.method_ids.get(method_name) {
+ let trait_fn_meta = interner.function_meta(trait_fn_id);
+
+ if trait_fn_meta.direct_generics.len() != meta.direct_generics.len() {
+ let expected = trait_fn_meta.direct_generics.len();
+ let found = meta.direct_generics.len();
+ let span = meta.name.location.span;
+ let item = method_name.to_string();
+ errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, span });
+ }
+
+ // Substitute each generic on the trait function with the corresponding generic on the impl function
+ for ((_, trait_fn_generic), (name, impl_fn_generic)) in
+ trait_fn_meta.direct_generics.iter().zip(&meta.direct_generics)
+ {
+ let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone());
+ bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), arg));
+ }
+
+ let (declaration_type, _) = trait_fn_meta.typ.instantiate_with_bindings(bindings, interner);
+
+ check_function_type_matches_expected_type(
+ &declaration_type,
+ definition_type,
+ method_name,
+ &meta.parameters,
+ meta.name.location.span,
+ &trait_info.name.0.contents,
+ &mut errors,
+ );
+ }
+
+ errors
+}
+
+fn check_function_type_matches_expected_type(
+ expected: &Type,
+ actual: &Type,
+ method_name: &str,
+ actual_parameters: &Parameters,
+ span: Span,
+ trait_name: &str,
+ errors: &mut Vec,
+) {
+ let mut bindings = TypeBindings::new();
+ // Shouldn't need to unify envs, they should always be equal since they're both free functions
+ if let (Type::Function(params_a, ret_a, _env_a), Type::Function(params_b, ret_b, _env_b)) =
+ (expected, actual)
+ {
+ if params_a.len() == params_b.len() {
+ for (i, (a, b)) in params_a.iter().zip(params_b.iter()).enumerate() {
+ if a.try_unify(b, &mut bindings).is_err() {
+ errors.push(TypeCheckError::TraitMethodParameterTypeMismatch {
+ method_name: method_name.to_string(),
+ expected_typ: a.to_string(),
+ actual_typ: b.to_string(),
+ parameter_span: actual_parameters.0[i].0.span(),
+ parameter_index: i + 1,
+ });
+ }
+ }
+
+ if ret_b.try_unify(ret_a, &mut bindings).is_err() {
+ errors.push(TypeCheckError::TypeMismatch {
+ expected_typ: ret_a.to_string(),
+ expr_typ: ret_b.to_string(),
+ expr_span: span,
+ });
+ }
+ } else {
+ errors.push(TypeCheckError::MismatchTraitImplNumParameters {
+ actual_num_parameters: params_b.len(),
+ expected_num_parameters: params_a.len(),
+ trait_name: trait_name.to_string(),
+ method_name: method_name.to_string(),
+ span,
+ });
+ }
+ }
+
+ // If result bindings is not empty, a type variable was bound which means the two
+ // signatures were not a perfect match. Note that this relies on us already binding
+ // all the expected generics to each other prior to this check.
+ if !bindings.is_empty() {
+ let expected_typ = expected.to_string();
+ let expr_typ = actual.to_string();
+ errors.push(TypeCheckError::TypeMismatch { expected_typ, expr_typ, expr_span: span });
+ }
+}
+
impl<'interner> TypeChecker<'interner> {
fn new(interner: &'interner mut NodeInterner) -> Self {
Self { interner, errors: Vec::new(), trait_constraints: Vec::new(), current_function: None }
@@ -346,6 +500,7 @@ mod test {
trait_impl: None,
return_type: FunctionReturnType::Default(Span::default()),
trait_constraints: Vec::new(),
+ direct_generics: Vec::new(),
is_entry_point: true,
};
interner.push_fn_meta(func_meta, func_id);
diff --git a/compiler/noirc_frontend/src/hir_def/function.rs b/compiler/noirc_frontend/src/hir_def/function.rs
index 82bbe1aa5b6..56543e8185c 100644
--- a/compiler/noirc_frontend/src/hir_def/function.rs
+++ b/compiler/noirc_frontend/src/hir_def/function.rs
@@ -1,12 +1,14 @@
use iter_extended::vecmap;
use noirc_errors::{Location, Span};
+use std::rc::Rc;
+
use super::expr::{HirBlockExpression, HirExpression, HirIdent};
use super::stmt::HirPattern;
use super::traits::TraitConstraint;
use crate::node_interner::{ExprId, NodeInterner, TraitImplId};
use crate::FunctionKind;
-use crate::{Distinctness, FunctionReturnType, Type, Visibility};
+use crate::{Distinctness, FunctionReturnType, Type, TypeVariable, Visibility};
/// A Hir function is a block expression
/// with a list of statements
@@ -103,6 +105,12 @@ pub struct FuncMeta {
/// or a Type::Forall for generic functions.
pub typ: Type,
+ /// The set of generics that are declared directly on this function in the source code.
+ /// This does not include generics from an outer scope, like those introduced by
+ /// an `impl` block. This also does not include implicit generics added by the compiler
+ /// such as a trait's `Self` type variable.
+ pub direct_generics: Vec<(Rc, TypeVariable)>,
+
pub location: Location,
// This flag is needed for the attribute check pass
diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs
index 9be6252b10a..3f78bd43ba9 100644
--- a/compiler/noirc_frontend/src/tests.rs
+++ b/compiler/noirc_frontend/src/tests.rs
@@ -535,15 +535,13 @@ mod test {
assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors);
for (err, _file_id) in errors {
match &err {
- CompilationError::DefinitionError(
- DefCollectorErrorKind::MismatchTraitImplementationNumParameters {
- actual_num_parameters,
- expected_num_parameters,
- trait_name,
- method_name,
- ..
- },
- ) => {
+ CompilationError::TypeError(TypeCheckError::MismatchTraitImplNumParameters {
+ actual_num_parameters,
+ expected_num_parameters,
+ trait_name,
+ method_name,
+ ..
+ }) => {
assert_eq!(actual_num_parameters, &1_usize);
assert_eq!(expected_num_parameters, &2_usize);
assert_eq!(method_name, "default");
From 2a53545f4238c9b8535e6bc5b0720fa15f44f946 Mon Sep 17 00:00:00 2001
From: jfecher
Date: Mon, 11 Mar 2024 09:11:09 -0500
Subject: [PATCH 07/14] fix: Fix brillig slowdown when assigning arrays in
loops (#4472)
# Description
## Problem\*
Resolves https://github.com/noir-lang/noir/issues/3795
## Summary\*
This is an older version of https://github.com/noir-lang/noir/pull/4210
which undoes the change in
https://github.com/noir-lang/noir/pull/4210/commits/d331ee27e63df73e7e294fba7031a4c0ab073294
due to a regression https://github.com/noir-lang/noir/issues/4332.
This PR is not yet confirmed to work since I do not have a test case for
it! @sirasistant, do you mind seeing if this fixes the regression issue?
## Additional Context
## Documentation\*
Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[Exceptional Case]** Documentation to be submitted in a separate
PR.
# PR Checklist\*
- [ ] I have tested the changes locally.
- [ ] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
---
.../src/ssa/function_builder/mod.rs | 19 +++++++++++++--
.../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 5 ++++
.../brillig_cow_assign/Nargo.toml | 7 ++++++
.../brillig_cow_assign/Prover.toml | 2 ++
.../brillig_cow_assign/src/main.nr | 23 +++++++++++++++++++
5 files changed, 54 insertions(+), 2 deletions(-)
create mode 100644 test_programs/execution_success/brillig_cow_assign/Nargo.toml
create mode 100644 test_programs/execution_success/brillig_cow_assign/Prover.toml
create mode 100644 test_programs/execution_success/brillig_cow_assign/src/main.nr
diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs
index 9d27554dcaa..bf34a47485b 100644
--- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs
+++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs
@@ -393,10 +393,25 @@ impl FunctionBuilder {
self.increment_array_reference_count(value);
}
}
- Type::Array(..) | Type::Slice(..) => {
- self.insert_instruction(Instruction::IncrementRc { value }, None);
+ typ @ Type::Array(..) | typ @ Type::Slice(..) => {
// If there are nested arrays or slices, we wait until ArrayGet
// is issued to increment the count of that array.
+ self.insert_instruction(Instruction::IncrementRc { value }, None);
+
+ // This is a bit odd, but in brillig the inc_rc instruction operates on
+ // a copy of the array's metadata, so we need to re-store a loaded array
+ // even if there have been no other changes to it.
+ if let Value::Instruction { instruction, .. } = &self.current_function.dfg[value] {
+ let instruction = &self.current_function.dfg[*instruction];
+ if let Instruction::Load { address } = instruction {
+ // We can't re-use `value` in case the original address was stored
+ // to again in the meantime. So introduce another load.
+ let address = *address;
+ let value = self.insert_load(address, typ);
+ self.insert_instruction(Instruction::IncrementRc { value }, None);
+ self.insert_store(address, value);
+ }
+ }
}
}
}
diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs
index d95295ae3c9..f3fa5d1d2f8 100644
--- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs
+++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs
@@ -722,6 +722,11 @@ impl<'a> FunctionContext<'a> {
let lhs = self.extract_current_value(&assign.lvalue)?;
let rhs = self.codegen_expression(&assign.expression)?;
+ rhs.clone().for_each(|value| {
+ let value = value.eval(self);
+ self.builder.increment_array_reference_count(value);
+ });
+
self.assign_new_value(lhs, rhs);
Ok(Self::unit_value())
}
diff --git a/test_programs/execution_success/brillig_cow_assign/Nargo.toml b/test_programs/execution_success/brillig_cow_assign/Nargo.toml
new file mode 100644
index 00000000000..a878566a372
--- /dev/null
+++ b/test_programs/execution_success/brillig_cow_assign/Nargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "brillig_cow_assign"
+type = "bin"
+authors = [""]
+compiler_version = ">=0.23.0"
+
+[dependencies]
diff --git a/test_programs/execution_success/brillig_cow_assign/Prover.toml b/test_programs/execution_success/brillig_cow_assign/Prover.toml
new file mode 100644
index 00000000000..882c73b83f8
--- /dev/null
+++ b/test_programs/execution_success/brillig_cow_assign/Prover.toml
@@ -0,0 +1,2 @@
+items_to_update = 10
+index = 6
diff --git a/test_programs/execution_success/brillig_cow_assign/src/main.nr b/test_programs/execution_success/brillig_cow_assign/src/main.nr
new file mode 100644
index 00000000000..e5c3e2bd2f5
--- /dev/null
+++ b/test_programs/execution_success/brillig_cow_assign/src/main.nr
@@ -0,0 +1,23 @@
+global N = 10;
+
+unconstrained fn main() {
+ let mut arr = [0; N];
+ let mut mid_change = arr;
+
+ for i in 0..N {
+ if i == N / 2 {
+ mid_change = arr;
+ }
+ arr[i] = 27;
+ }
+
+ // Expect:
+ // arr = [27, 27, 27, 27, 27, 27, 27, 27, 27, 27]
+ // mid_change = [27, 27, 27, 27, 27, 0, 0, 0, 0, 0]
+
+ let modified_i = N / 2 + 1;
+ assert_eq(arr[modified_i], 27);
+
+ // Fail here!
+ assert(mid_change[modified_i] != 27);
+}
From b94adb92657e2b4a51dc7216a88e080aed1cf8b0 Mon Sep 17 00:00:00 2001
From: jfecher
Date: Mon, 11 Mar 2024 11:55:32 -0500
Subject: [PATCH 08/14] fix: Add `follow_bindings` to follow `Type::Alias`
links (#4521)
# Description
## Problem\*
Resolves https://github.com/noir-lang/noir/issues/4518
## Summary\*
## Additional Context
A match case for `Type::Alias` was forgotten when that was initially
added.
## Documentation\*
Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[Exceptional Case]** Documentation to be submitted in a separate
PR.
# PR Checklist\*
- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
---
.../noirc_frontend/src/monomorphization/mod.rs | 14 +++-----------
1 file changed, 3 insertions(+), 11 deletions(-)
diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs
index cfd9a61d13f..9d11ecd54bc 100644
--- a/compiler/noirc_frontend/src/monomorphization/mod.rs
+++ b/compiler/noirc_frontend/src/monomorphization/mod.rs
@@ -1691,23 +1691,15 @@ impl<'interner> Monomorphizer<'interner> {
}
fn unwrap_tuple_type(typ: &HirType) -> Vec {
- match typ {
+ match typ.follow_bindings() {
HirType::Tuple(fields) => fields.clone(),
- HirType::TypeVariable(binding, TypeVariableKind::Normal) => match &*binding.borrow() {
- TypeBinding::Bound(binding) => unwrap_tuple_type(binding),
- TypeBinding::Unbound(_) => unreachable!(),
- },
other => unreachable!("unwrap_tuple_type: expected tuple, found {:?}", other),
}
}
fn unwrap_struct_type(typ: &HirType) -> Vec<(String, HirType)> {
- match typ {
- HirType::Struct(def, args) => def.borrow().get_fields(args),
- HirType::TypeVariable(binding, TypeVariableKind::Normal) => match &*binding.borrow() {
- TypeBinding::Bound(binding) => unwrap_struct_type(binding),
- TypeBinding::Unbound(_) => unreachable!(),
- },
+ match typ.follow_bindings() {
+ HirType::Struct(def, args) => def.borrow().get_fields(&args),
other => unreachable!("unwrap_struct_type: expected struct, found {:?}", other),
}
}
From c6e6efd5fa314c61055e6cb15565ccbec4c78bbf Mon Sep 17 00:00:00 2001
From: guipublic <47281315+guipublic@users.noreply.github.com>
Date: Mon, 11 Mar 2024 21:02:54 +0100
Subject: [PATCH 09/14] chore: document big integers (#4487)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
# Description
## Problem\*
Resolves #4205
## Summary\*
Add a section on standard library documentation
## Additional Context
## Documentation\*
Check one:
- [ ] No documentation needed.
- [X] Documentation included in this PR.
- [ ] **[Exceptional Case]** Documentation to be submitted in a separate
PR.
# PR Checklist\*
- [X] I have tested the changes locally.
- [X] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
---------
Co-authored-by: José Pedro Sousa
Co-authored-by: Savio <72797635+Savio-Sou@users.noreply.github.com>
---
cspell.json | 2 +
docs/.markdownlint.json | 3 +
docs/docs/noir/standard_library/bigint.md | 102 ++++++++++++++++++
noir_stdlib/src/bigint.nr | 2 +
.../execution_success/bigint/src/main.nr | 12 +++
5 files changed, 121 insertions(+)
create mode 100644 docs/.markdownlint.json
create mode 100644 docs/docs/noir/standard_library/bigint.md
diff --git a/cspell.json b/cspell.json
index a96e3de901a..d961b600f40 100644
--- a/cspell.json
+++ b/cspell.json
@@ -154,6 +154,8 @@
"sdiv",
"secp256k1",
"secp256r1",
+ "Secpk",
+ "Secpr",
"signedness",
"signorecello",
"smol",
diff --git a/docs/.markdownlint.json b/docs/.markdownlint.json
new file mode 100644
index 00000000000..40896b4542f
--- /dev/null
+++ b/docs/.markdownlint.json
@@ -0,0 +1,3 @@
+{
+ "no-missing-space-atx": false
+}
diff --git a/docs/docs/noir/standard_library/bigint.md b/docs/docs/noir/standard_library/bigint.md
new file mode 100644
index 00000000000..7d7931840cf
--- /dev/null
+++ b/docs/docs/noir/standard_library/bigint.md
@@ -0,0 +1,102 @@
+---
+title: Big Integers
+description: How to use big integers from Noir standard library
+keywords:
+ [
+ Big Integer,
+ Noir programming language,
+ Noir libraries,
+ ]
+---
+
+The BigInt module in the standard library exposes some class of integers which do not fit (well) into a Noir native field. It implements modulo arithmetic, modulo a 'big' prime number.
+
+:::note
+
+The module can currently be considered as `Field`s with fixed modulo sizes used by a set of elliptic curves, in addition to just the native curve. [More work](https://github.com/noir-lang/noir/issues/510) is needed to achieve arbitrarily sized big integers.
+
+:::
+
+Currently 6 classes of integers (i.e 'big' prime numbers) are available in the module, namely:
+
+- BN254 Fq: Bn254Fq
+- BN254 Fr: Bn254Fr
+- Secp256k1 Fq: Secpk1Fq
+- Secp256k1 Fr: Secpk1Fr
+- Secp256r1 Fr: Secpr1Fr
+- Secp256r1 Fq: Secpr1Fq
+
+Where XXX Fq and XXX Fr denote respectively the order of the base and scalar field of the (usual) elliptic curve XXX.
+For instance the big integer 'Secpk1Fq' in the standard library refers to integers modulo $2^{256}-2^{32}-977$.
+
+Feel free to explore the source code for the other primes:
+
+#include_code curve_order_base noir_stdlib/src/bigint.nr rust
+
+## Example usage
+
+A common use-case is when constructing a big integer from its bytes representation, and performing arithmetic operations on it:
+
+#include_code big_int_example test_programs/execution_success/bigint/src/main.nr rust
+
+## Methods
+
+The available operations for each big integer are:
+
+### from_le_bytes
+
+Construct a big integer from its little-endian bytes representation. Example:
+
+```rust
+ let a = Secpk1Fq::from_le_bytes([x, y, 0, 45, 2]);
+ ```
+
+Sure, here's the formatted version of the remaining methods:
+
+### to_le_bytes
+
+Return the little-endian bytes representation of a big integer. Example:
+
+```rust
+let bytes = a.to_le_bytes();
+```
+
+### add
+
+Add two big integers. Example:
+
+```rust
+let sum = a + b;
+```
+
+### sub
+
+Subtract two big integers. Example:
+
+```rust
+let difference = a - b;
+```
+
+### mul
+
+Multiply two big integers. Example:
+
+```rust
+let product = a * b;
+```
+
+### div
+
+Divide two big integers. Note that division is field division and not euclidean division. Example:
+
+```rust
+let quotient = a / b;
+```
+
+### eq
+
+Compare two big integers. Example:
+
+```rust
+let are_equal = a == b;
+```
diff --git a/noir_stdlib/src/bigint.nr b/noir_stdlib/src/bigint.nr
index 98237a54779..e7fc28ba39b 100644
--- a/noir_stdlib/src/bigint.nr
+++ b/noir_stdlib/src/bigint.nr
@@ -1,6 +1,7 @@
use crate::ops::{Add, Sub, Mul, Div};
use crate::cmp::Eq;
+// docs:start:curve_order_base
global bn254_fq = [0x47, 0xFD, 0x7C, 0xD8, 0x16, 0x8C, 0x20, 0x3C, 0x8d, 0xca, 0x71, 0x68, 0x91, 0x6a, 0x81, 0x97,
0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30];
global bn254_fr = [0x01, 0x00, 0x00, 0x00, 0x3F, 0x59, 0x1F, 0x43, 0x09, 0x97, 0xB9, 0x79, 0x48, 0xE8, 0x33, 0x28,
@@ -13,6 +14,7 @@ global secpr1_fq = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF];
global secpr1_fr = [0x51, 0x25, 0x63, 0xFC, 0xC2, 0xCA, 0xB9, 0xF3, 0x84, 0x9E, 0x17, 0xA7, 0xAD, 0xFA, 0xE6, 0xBC,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,0xFF, 0xFF, 0xFF, 0xFF];
+// docs:end:curve_order_base
struct BigInt {
pointer: u32,
diff --git a/test_programs/execution_success/bigint/src/main.nr b/test_programs/execution_success/bigint/src/main.nr
index b93fec370e5..b6c23276c15 100644
--- a/test_programs/execution_success/bigint/src/main.nr
+++ b/test_programs/execution_success/bigint/src/main.nr
@@ -1,4 +1,5 @@
use dep::std::bigint;
+use dep::std::{bigint::Secpk1Fq, println};
fn main(mut x: [u8; 5], y: [u8; 5]) {
let a = bigint::Secpk1Fq::from_le_bytes([x[0], x[1], x[2], x[3], x[4]]);
@@ -13,4 +14,15 @@ fn main(mut x: [u8; 5], y: [u8; 5]) {
let d = a * b - b;
let d1 = bigint::Secpk1Fq::from_le_bytes(597243850900842442924.to_le_bytes(10));
assert(d1 == d);
+ // big_int_example(x[0], x[1]);
}
+
+// docs:start:big_int_example
+fn big_int_example(x: u8, y: u8) {
+ let a = Secpk1Fq::from_le_bytes([x, y, 0, 45, 2]);
+ let b = Secpk1Fq::from_le_bytes([y, x, 9]);
+ let c = (a + b) * b / a;
+ let d = c.to_le_bytes();
+ println(d[0]);
+}
+// docs:end:big_int_example
From 3f676051a6073d6eabdc7fee68e4b522334344f6 Mon Sep 17 00:00:00 2001
From: kevaundray
Date: Mon, 11 Mar 2024 20:27:40 +0000
Subject: [PATCH 10/14] chore: Release Noir(0.25.0) (#4352)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
:robot: I have created a release *beep* *boop*
---
0.25.0
## [0.25.0](https://github.com/noir-lang/noir/compare/v0.24.0...v0.25.0)
(2024-03-11)
### ⚠ BREAKING CHANGES
* Internal as a macro
(https://github.com/AztecProtocol/aztec-packages/pull/4898)
* reserve `unchecked` keyword
([#4432](https://github.com/noir-lang/noir/issues/4432))
* Remove empty value from bounded vec
([#4431](https://github.com/noir-lang/noir/issues/4431))
* Ban Fields in for loop indices and bitwise ops
([#4376](https://github.com/noir-lang/noir/issues/4376))
* bump msrv to 1.73.0
([#4406](https://github.com/noir-lang/noir/issues/4406))
* **ci:** Bump MSRV to 1.72.1 and enforce that ACVM can be published
using updated lockfile
([#4385](https://github.com/noir-lang/noir/issues/4385))
* Restrict bit sizes
([#4235](https://github.com/noir-lang/noir/issues/4235))
* move noir out of yarn-project
(https://github.com/AztecProtocol/aztec-packages/pull/4479)
* note type ids
(https://github.com/AztecProtocol/aztec-packages/pull/4500)
### Features
* Add eddsa_poseidon_to_pub function to stdlib with test + docs
([#4473](https://github.com/noir-lang/noir/issues/4473))
([00d2c32](https://github.com/noir-lang/noir/commit/00d2c32e58176cc5de3574c8435a54d415c4a5fa))
* Add HashMap to the stdlib
([#4242](https://github.com/noir-lang/noir/issues/4242))
([650ffc5](https://github.com/noir-lang/noir/commit/650ffc5053cdca4b6ad2e027fa1f4fd90ef64871))
* Add option to set max memory for bb.js
([#4227](https://github.com/noir-lang/noir/issues/4227))
([8a6b131](https://github.com/noir-lang/noir/commit/8a6b131402892a570bc2de6f5869de73b0bd979e))
* Add overflow and underflow checks for unsigned integers in brillig
([#4445](https://github.com/noir-lang/noir/issues/4445))
([21fc4b8](https://github.com/noir-lang/noir/commit/21fc4b85763dccae6dce0a46a318718c3c913471))
* Add poseidon2 opcode implementation for acvm/brillig, and Noir
([#4398](https://github.com/noir-lang/noir/issues/4398))
([10e8292](https://github.com/noir-lang/noir/commit/10e82920798380f50046e52db4a20ca205191ab7))
* Added cast opcode and cast calldata
(https://github.com/AztecProtocol/aztec-packages/pull/4423)
([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a))
* Allow type aliases to reference other aliases
([#4353](https://github.com/noir-lang/noir/issues/4353))
([c44ef14](https://github.com/noir-lang/noir/commit/c44ef14847a436733206b6dd9590a7ab214ecd97))
* Backpropagate constants in ACIR during optimization
([#3926](https://github.com/noir-lang/noir/issues/3926))
([aad0da0](https://github.com/noir-lang/noir/commit/aad0da024c69663f42e6913e674682d5864b26ae))
* **ci:** Use wasm-opt when compiling wasm packages
([#4334](https://github.com/noir-lang/noir/issues/4334))
([e382921](https://github.com/noir-lang/noir/commit/e3829213d8411f84e117a14b43816967925095e0))
* DAP Preflight and debugger compilation options
([#4185](https://github.com/noir-lang/noir/issues/4185))
([e0ad0b2](https://github.com/noir-lang/noir/commit/e0ad0b2b31f6d46be75d23aec6a82850a9c4bd75))
* Expose separate functions to compile programs vs contracts in
`noir_wasm` ([#4413](https://github.com/noir-lang/noir/issues/4413))
([7cd5fdb](https://github.com/noir-lang/noir/commit/7cd5fdb3d2a53475b7c8681231d517cab30f9f9b))
* Internal as a macro
(https://github.com/AztecProtocol/aztec-packages/pull/4898)
([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e))
* Note type ids
(https://github.com/AztecProtocol/aztec-packages/pull/4500)
([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a))
* Restrict bit sizes
([#4235](https://github.com/noir-lang/noir/issues/4235))
([1048f81](https://github.com/noir-lang/noir/commit/1048f815abb1f27e9c84ab5b9568a3673c12a50a))
* Run tests in parallel in `nargo test`
([#4484](https://github.com/noir-lang/noir/issues/4484))
([761734e](https://github.com/noir-lang/noir/commit/761734e6cb3ff5911aa85d0cee96ad26092b4905))
* Skip redundant range checks in brillig
([#4460](https://github.com/noir-lang/noir/issues/4460))
([cb4c1c5](https://github.com/noir-lang/noir/commit/cb4c1c5264b95d01f69d99f916ced71ad9cdc9d1))
* Sync from aztec-packages
([#4483](https://github.com/noir-lang/noir/issues/4483))
([fe8f277](https://github.com/noir-lang/noir/commit/fe8f2776ccfde29209a2c3fc162311c99e4f59be))
* Track stack frames and their variables in the debugger
([#4188](https://github.com/noir-lang/noir/issues/4188))
([ae1a9d9](https://github.com/noir-lang/noir/commit/ae1a9d923998177516919bbba6ff4b0584fa1e9f))
* TypeVariableKind for just Integers
([#4118](https://github.com/noir-lang/noir/issues/4118))
([c956be8](https://github.com/noir-lang/noir/commit/c956be870fb47403a6da6585fce6bea2d40ee268))
* Update error message when trying to load workspace as dependency
([#4393](https://github.com/noir-lang/noir/issues/4393))
([d2585e7](https://github.com/noir-lang/noir/commit/d2585e738a63208fca3c9e26242e896d7f1df1e4))
### Bug Fixes
* **acir:** Array dynamic flatten
([#4351](https://github.com/noir-lang/noir/issues/4351))
([b2aaeab](https://github.com/noir-lang/noir/commit/b2aaeab319a0c66c431a7db6852f743eccde8e98))
* **acir:** Use types on dynamic arrays
([#4364](https://github.com/noir-lang/noir/issues/4364))
([ba2c541](https://github.com/noir-lang/noir/commit/ba2c541ec45de92bba98de34771b73cbb7865c93))
* Add `follow_bindings` to follow `Type::Alias` links
([#4521](https://github.com/noir-lang/noir/issues/4521))
([b94adb9](https://github.com/noir-lang/noir/commit/b94adb92657e2b4a51dc7216a88e080aed1cf8b0))
* Add handling to `noir_wasm` for projects without dependencies
([#4344](https://github.com/noir-lang/noir/issues/4344))
([4982251](https://github.com/noir-lang/noir/commit/49822511710a7f1c42b8ed343e80456f8e6db2d9))
* Allow type aliases in main
([#4505](https://github.com/noir-lang/noir/issues/4505))
([8a5359c](https://github.com/noir-lang/noir/commit/8a5359c012579e54c2766de1074482a36ecada32))
* Ban Fields in for loop indices and bitwise ops
([#4376](https://github.com/noir-lang/noir/issues/4376))
([601fd9a](https://github.com/noir-lang/noir/commit/601fd9afc502236af1db0c4492698ba2298c7501))
* Brillig range check with consistent bit size
([#4357](https://github.com/noir-lang/noir/issues/4357))
([ea47d4a](https://github.com/noir-lang/noir/commit/ea47d4a67c6a18e4a7d3a49079d9eb24a1026a25))
* Build noir_codegen when publishing
([#4448](https://github.com/noir-lang/noir/issues/4448))
([cb1ceee](https://github.com/noir-lang/noir/commit/cb1ceee58b11b0ce6f8845361af3418d13c506bd))
* Consistent bit size for truncate
([#4370](https://github.com/noir-lang/noir/issues/4370))
([dcd7a1e](https://github.com/noir-lang/noir/commit/dcd7a1e561a68504b9038ffbb3c80f5c981f9f0c))
* Correct formatting for databus visibility types
([#4423](https://github.com/noir-lang/noir/issues/4423))
([cd796de](https://github.com/noir-lang/noir/commit/cd796dea4937dd1a261f154e5f2e599bbc649165))
* Correct invalid brillig codegen for `EmbeddedCurvePoint.add`
([#4382](https://github.com/noir-lang/noir/issues/4382))
([5051ec4](https://github.com/noir-lang/noir/commit/5051ec4d434a9e5cf405c68357faaf213e68de9e))
* **docs:** Update install versions
([#4396](https://github.com/noir-lang/noir/issues/4396))
([b283637](https://github.com/noir-lang/noir/commit/b283637e092038eb296c468168aec2d41e1c2734))
* **docs:** Update noirjs_app for 0.23
([#4378](https://github.com/noir-lang/noir/issues/4378))
([f77f702](https://github.com/noir-lang/noir/commit/f77f702e0cfb81dcce4dd97e274b831e887ba5d2))
* Enforce matching types of binary ops in SSA
([#4391](https://github.com/noir-lang/noir/issues/4391))
([70866ae](https://github.com/noir-lang/noir/commit/70866aea976d59dbcbd4af34067fdd8f46555673))
* Fix brillig slowdown when assigning arrays in loops
([#4472](https://github.com/noir-lang/noir/issues/4472))
([2a53545](https://github.com/noir-lang/noir/commit/2a53545f4238c9b8535e6bc5b0720fa15f44f946))
* **flake:** Stop flake.nix removing ignored-tests.txt
([#4455](https://github.com/noir-lang/noir/issues/4455))
([ebaf05a](https://github.com/noir-lang/noir/commit/ebaf05ab10834dd10e04c7ea5130f96c6cdf98ed))
* Force src impl for == on slices
([#4507](https://github.com/noir-lang/noir/issues/4507))
([1691274](https://github.com/noir-lang/noir/commit/169127444e8b16a8aad4acfe29ba812894fd897c))
* Handling of gh deps in noir_wasm
([#4499](https://github.com/noir-lang/noir/issues/4499))
([1d65370](https://github.com/noir-lang/noir/commit/1d653704715bf9999eb6a40ed7500e752e2c73b7))
* Iterative flattening pass
([#4492](https://github.com/noir-lang/noir/issues/4492))
([33c1ef7](https://github.com/noir-lang/noir/commit/33c1ef70e7859fdee7babfb5d38191f53e73a0df))
* Noir test incorrect reporting
(https://github.com/AztecProtocol/aztec-packages/pull/4925)
([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e))
* Only add `.nr` files to file manager
([#4380](https://github.com/noir-lang/noir/issues/4380))
([8536c7c](https://github.com/noir-lang/noir/commit/8536c7c8ea8fc6b740b2ae6d1aef3bc7e1907b8c))
* Remove panic when generic array length is not resolvable
([#4408](https://github.com/noir-lang/noir/issues/4408))
([00ab3db](https://github.com/noir-lang/noir/commit/00ab3db86b06111d144516e862902b8604284611))
* Remove print from monomorphization pass
([#4417](https://github.com/noir-lang/noir/issues/4417))
([27c66b3](https://github.com/noir-lang/noir/commit/27c66b3d0741e68ed591ae8a16b47b30bc87175f))
* **ssa:** Handle mergers of slices returned from calls
([#4496](https://github.com/noir-lang/noir/issues/4496))
([f988d02](https://github.com/noir-lang/noir/commit/f988d020e43cdf36a38613f2052d4518de39193a))
* Use correct type for numeric generics
([#4386](https://github.com/noir-lang/noir/issues/4386))
([0a1d109](https://github.com/noir-lang/noir/commit/0a1d109f478c997da5c43876fd12464af638bb15))
* Variables from trait constraints being permanently bound over when
used within a trait impl
([#4450](https://github.com/noir-lang/noir/issues/4450))
([ac60ef5](https://github.com/noir-lang/noir/commit/ac60ef5e12fcfb907fbdcff709d7cbad05f2b939))
### Miscellaneous Chores
* Bump msrv to 1.73.0
([#4406](https://github.com/noir-lang/noir/issues/4406))
([b5e5c30](https://github.com/noir-lang/noir/commit/b5e5c30f4db52c79ef556e80660f39db369b1911))
* **ci:** Bump MSRV to 1.72.1 and enforce that ACVM can be published
using updated lockfile
([#4385](https://github.com/noir-lang/noir/issues/4385))
([2fc95d2](https://github.com/noir-lang/noir/commit/2fc95d2d82b3220267ce7d5815e7073e00ef1360))
* Move noir out of yarn-project
(https://github.com/AztecProtocol/aztec-packages/pull/4479)
([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a))
* Remove empty value from bounded vec
([#4431](https://github.com/noir-lang/noir/issues/4431))
([b9384fb](https://github.com/noir-lang/noir/commit/b9384fb23abf4ab15e880fb7e03c21509a9fa8a6))
* Reserve `unchecked` keyword
([#4432](https://github.com/noir-lang/noir/issues/4432))
([9544813](https://github.com/noir-lang/noir/commit/9544813fabbd18a87dd88456e6a5b781bd0cf008))
0.41.0
## [0.41.0](https://github.com/noir-lang/noir/compare/v0.40.0...v0.41.0)
(2024-03-11)
### ⚠ BREAKING CHANGES
* Internal as a macro
(https://github.com/AztecProtocol/aztec-packages/pull/4898)
* move noir out of yarn-project
(https://github.com/AztecProtocol/aztec-packages/pull/4479)
* note type ids
(https://github.com/AztecProtocol/aztec-packages/pull/4500)
* rename bigint_neg into bigint_sub
(https://github.com/AztecProtocol/aztec-packages/pull/4420)
* Add expression width into acir
(https://github.com/AztecProtocol/aztec-packages/pull/4014)
* init storage macro
(https://github.com/AztecProtocol/aztec-packages/pull/4200)
* **acir:** Move `is_recursive` flag to be part of the circuit
definition (https://github.com/AztecProtocol/aztec-packages/pull/4221)
* Sync commits from `aztec-packages`
([#4144](https://github.com/noir-lang/noir/issues/4144))
* Breaking changes from aztec-packages
([#3955](https://github.com/noir-lang/noir/issues/3955))
* Rename Arithmetic opcode to AssertZero
([#3840](https://github.com/noir-lang/noir/issues/3840))
* Remove unused methods on ACIR opcodes
([#3841](https://github.com/noir-lang/noir/issues/3841))
* Remove partial backend feature
([#3805](https://github.com/noir-lang/noir/issues/3805))
### Features
* Add bit size to const opcode
(https://github.com/AztecProtocol/aztec-packages/pull/4385)
([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
* Add expression width into acir
(https://github.com/AztecProtocol/aztec-packages/pull/4014)
([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
* Add instrumentation for tracking variables in debugging
([#4122](https://github.com/noir-lang/noir/issues/4122))
([c58d691](https://github.com/noir-lang/noir/commit/c58d69141b54a918cd1675400c00bfd48720f896))
* Add poseidon2 opcode implementation for acvm/brillig, and Noir
([#4398](https://github.com/noir-lang/noir/issues/4398))
([10e8292](https://github.com/noir-lang/noir/commit/10e82920798380f50046e52db4a20ca205191ab7))
* Add support for overriding expression width
([#4117](https://github.com/noir-lang/noir/issues/4117))
([c8026d5](https://github.com/noir-lang/noir/commit/c8026d557d535b10fe455165d6445076df7a03de))
* Added cast opcode and cast calldata
(https://github.com/AztecProtocol/aztec-packages/pull/4423)
([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a))
* Allow brillig to read arrays directly from memory
(https://github.com/AztecProtocol/aztec-packages/pull/4460)
([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
* Allow nested arrays and vectors in Brillig foreign calls
(https://github.com/AztecProtocol/aztec-packages/pull/4478)
([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
* Allow variables and stack trace inspection in the debugger
([#4184](https://github.com/noir-lang/noir/issues/4184))
([bf263fc](https://github.com/noir-lang/noir/commit/bf263fc8d843940f328a90f6366edd2671fb2682))
* **avm:** Back in avm context with macro - refactor context
(https://github.com/AztecProtocol/aztec-packages/pull/4438)
([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
* **aztec-nr:** Initial work for aztec public vm macro
(https://github.com/AztecProtocol/aztec-packages/pull/4400)
([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
* Aztec-packages
([#3754](https://github.com/noir-lang/noir/issues/3754))
([c043265](https://github.com/noir-lang/noir/commit/c043265e550b59bd4296504826fe15d3ce3e9ad2))
* Backpropagate constants in ACIR during optimization
([#3926](https://github.com/noir-lang/noir/issues/3926))
([aad0da0](https://github.com/noir-lang/noir/commit/aad0da024c69663f42e6913e674682d5864b26ae))
* Breaking changes from aztec-packages
([#3955](https://github.com/noir-lang/noir/issues/3955))
([5be049e](https://github.com/noir-lang/noir/commit/5be049eee6c342649462282ee04f6411e6ea392c))
* Evaluation of dynamic assert messages
([#4101](https://github.com/noir-lang/noir/issues/4101))
([c284e01](https://github.com/noir-lang/noir/commit/c284e01bfe20ceae4414dc123624b5cbb8b66d09))
* Init storage macro
(https://github.com/AztecProtocol/aztec-packages/pull/4200)
([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
* Internal as a macro
(https://github.com/AztecProtocol/aztec-packages/pull/4898)
([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e))
* Note type ids
(https://github.com/AztecProtocol/aztec-packages/pull/4500)
([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a))
* Remove range constraints from witnesses which are constrained to be
constants ([#3928](https://github.com/noir-lang/noir/issues/3928))
([afe9c7a](https://github.com/noir-lang/noir/commit/afe9c7a38bb9d4245205d3aa46d4ce23d70a5671))
* Remove replacement of boolean range opcodes with `AssertZero` opcodes
([#4107](https://github.com/noir-lang/noir/issues/4107))
([dac0e87](https://github.com/noir-lang/noir/commit/dac0e87ee3be3446b92bbb12ef4832fd493fcee3))
* Speed up transformation of debug messages
([#3815](https://github.com/noir-lang/noir/issues/3815))
([2a8af1e](https://github.com/noir-lang/noir/commit/2a8af1e4141ffff61547ee1c2837a6392bd5db48))
* Sync `aztec-packages`
([#4011](https://github.com/noir-lang/noir/issues/4011))
([fee2452](https://github.com/noir-lang/noir/commit/fee24523c427c27f0bdaf98ea09a852a2da3e94c))
* Sync commits from `aztec-packages`
([#4068](https://github.com/noir-lang/noir/issues/4068))
([7a8f3a3](https://github.com/noir-lang/noir/commit/7a8f3a33b57875e681e3d81e667e3570a1cdbdcc))
* Sync commits from `aztec-packages`
([#4144](https://github.com/noir-lang/noir/issues/4144))
([0205d3b](https://github.com/noir-lang/noir/commit/0205d3b4ad0cf5ffd775a43eb5af273a772cf138))
* Sync from aztec-packages
([#4483](https://github.com/noir-lang/noir/issues/4483))
([fe8f277](https://github.com/noir-lang/noir/commit/fe8f2776ccfde29209a2c3fc162311c99e4f59be))
### Bug Fixes
* Deserialize odd length hex literals
([#3747](https://github.com/noir-lang/noir/issues/3747))
([4000fb2](https://github.com/noir-lang/noir/commit/4000fb279221eb07187d657bfaa7f1c7b311abf2))
* Noir test incorrect reporting
(https://github.com/AztecProtocol/aztec-packages/pull/4925)
([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e))
* Remove panic from `init_log_level` in `acvm_js`
([#4195](https://github.com/noir-lang/noir/issues/4195))
([2e26530](https://github.com/noir-lang/noir/commit/2e26530bf53006c1ed4fee310bcaa905c95dd95b))
* Return error rather instead of panicking on invalid circuit
([#3976](https://github.com/noir-lang/noir/issues/3976))
([67201bf](https://github.com/noir-lang/noir/commit/67201bfc21a9c8858aa86be9cd47d463fb78d925))
### Miscellaneous Chores
* **acir:** Move `is_recursive` flag to be part of the circuit
definition (https://github.com/AztecProtocol/aztec-packages/pull/4221)
([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
* Move noir out of yarn-project
(https://github.com/AztecProtocol/aztec-packages/pull/4479)
([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a))
* Remove partial backend feature
([#3805](https://github.com/noir-lang/noir/issues/3805))
([0383100](https://github.com/noir-lang/noir/commit/0383100853a80a5b28b797cdfeae0d271f1b7805))
* Remove unused methods on ACIR opcodes
([#3841](https://github.com/noir-lang/noir/issues/3841))
([9e5d0e8](https://github.com/noir-lang/noir/commit/9e5d0e813d61a0bfb5ee68174ed287c5a20f1579))
* Rename Arithmetic opcode to AssertZero
([#3840](https://github.com/noir-lang/noir/issues/3840))
([836f171](https://github.com/noir-lang/noir/commit/836f17145c2901060706294461c2d282dd121b3e))
* Rename bigint_neg into bigint_sub
(https://github.com/AztecProtocol/aztec-packages/pull/4420)
([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
---------
Co-authored-by: Savio <72797635+Savio-Sou@users.noreply.github.com>
---
.release-please-manifest.json | 4 +-
CHANGELOG.md | 77 +++
Cargo.lock | 50 +-
Cargo.toml | 14 +-
acvm-repo/CHANGELOG.md | 64 ++
acvm-repo/acir/Cargo.toml | 2 +-
acvm-repo/acir_field/Cargo.toml | 2 +-
acvm-repo/acvm/Cargo.toml | 2 +-
acvm-repo/acvm_js/Cargo.toml | 2 +-
acvm-repo/acvm_js/package.json | 2 +-
acvm-repo/blackbox_solver/Cargo.toml | 2 +-
acvm-repo/brillig/Cargo.toml | 2 +-
acvm-repo/brillig_vm/Cargo.toml | 2 +-
compiler/wasm/package.json | 2 +-
.../explainers/explainer-oracle.md | 57 ++
.../explainers/explainer-recursion.md | 176 ++++++
.../getting_started/_category_.json | 5 +
.../hello_noir/_category_.json | 5 +
.../getting_started/hello_noir/index.md | 142 +++++
.../hello_noir/project_breakdown.md | 199 ++++++
.../installation/_category_.json | 6 +
.../getting_started/installation/index.md | 48 ++
.../installation/other_install_methods.md | 254 ++++++++
.../getting_started/tooling/_category_.json | 6 +
.../getting_started/tooling/index.mdx | 38 ++
.../tooling/language_server.md | 43 ++
.../getting_started/tooling/testing.md | 62 ++
.../version-v0.25.0/how_to/_category_.json | 5 +
.../version-v0.25.0/how_to/how-to-oracles.md | 280 +++++++++
.../how_to/how-to-recursion.md | 179 ++++++
.../how_to/how-to-solidity-verifier.md | 231 +++++++
.../version-v0.25.0/how_to/merkle-proof.mdx | 48 ++
.../how_to/using-devcontainers.mdx | 110 ++++
docs/versioned_docs/version-v0.25.0/index.mdx | 67 +++
.../version-v0.25.0/migration_notes.md | 105 ++++
.../noir/concepts/_category_.json | 6 +
.../version-v0.25.0/noir/concepts/assert.md | 45 ++
.../version-v0.25.0/noir/concepts/comments.md | 33 +
.../noir/concepts/control_flow.md | 45 ++
.../version-v0.25.0/noir/concepts/data_bus.md | 21 +
.../noir/concepts/data_types/_category_.json | 5 +
.../noir/concepts/data_types/arrays.md | 251 ++++++++
.../noir/concepts/data_types/booleans.md | 31 +
.../noir/concepts/data_types/fields.md | 192 ++++++
.../concepts/data_types/function_types.md | 26 +
.../noir/concepts/data_types/index.md | 110 ++++
.../noir/concepts/data_types/integers.md | 155 +++++
.../noir/concepts/data_types/references.md | 23 +
.../noir/concepts/data_types/slices.mdx | 147 +++++
.../noir/concepts/data_types/strings.md | 80 +++
.../noir/concepts/data_types/structs.md | 70 +++
.../noir/concepts/data_types/tuples.md | 48 ++
.../version-v0.25.0/noir/concepts/distinct.md | 64 ++
.../noir/concepts/functions.md | 226 +++++++
.../version-v0.25.0/noir/concepts/generics.md | 106 ++++
.../version-v0.25.0/noir/concepts/globals.md | 72 +++
.../version-v0.25.0/noir/concepts/lambdas.md | 81 +++
.../noir/concepts/mutability.md | 121 ++++
.../version-v0.25.0/noir/concepts/ops.md | 98 +++
.../version-v0.25.0/noir/concepts/oracles.md | 23 +
.../noir/concepts/shadowing.md | 44 ++
.../version-v0.25.0/noir/concepts/traits.md | 389 ++++++++++++
.../noir/concepts/unconstrained.md | 95 +++
.../modules_packages_crates/_category_.json | 6 +
.../crates_and_packages.md | 43 ++
.../modules_packages_crates/dependencies.md | 124 ++++
.../noir/modules_packages_crates/modules.md | 105 ++++
.../modules_packages_crates/workspaces.md | 40 ++
.../noir/standard_library/_category_.json | 6 +
.../noir/standard_library/black_box_fns.md | 31 +
.../noir/standard_library/bn254.md | 46 ++
.../standard_library/containers/boundedvec.md | 326 ++++++++++
.../standard_library/containers/hashmap.md | 569 ++++++++++++++++++
.../noir/standard_library/containers/index.md | 5 +
.../noir/standard_library/containers/vec.mdx | 151 +++++
.../cryptographic_primitives/_category_.json | 5 +
.../cryptographic_primitives/ec_primitives.md | 102 ++++
.../ecdsa_sig_verification.mdx | 60 ++
.../cryptographic_primitives/eddsa.mdx | 37 ++
.../cryptographic_primitives/hashes.mdx | 251 ++++++++
.../cryptographic_primitives/index.md | 14 +
.../cryptographic_primitives/scalar.mdx | 33 +
.../cryptographic_primitives/schnorr.mdx | 45 ++
.../noir/standard_library/logging.md | 78 +++
.../noir/standard_library/merkle_trees.md | 58 ++
.../noir/standard_library/options.md | 101 ++++
.../noir/standard_library/recursion.md | 88 +++
.../noir/standard_library/traits.md | 399 ++++++++++++
.../noir/standard_library/zeroed.md | 25 +
.../version-v0.25.0/reference/_category_.json | 5 +
.../reference/nargo_commands.md | 380 ++++++++++++
.../version-v0.25.0/tutorials/noirjs_app.md | 279 +++++++++
.../version-v0.25.0-sidebars.json | 83 +++
flake.nix | 2 +-
tooling/noir_codegen/package.json | 2 +-
tooling/noir_js/package.json | 2 +-
.../noir_js_backend_barretenberg/package.json | 2 +-
tooling/noir_js_types/package.json | 2 +-
tooling/noirc_abi_wasm/package.json | 2 +-
99 files changed, 8358 insertions(+), 49 deletions(-)
create mode 100644 docs/versioned_docs/version-v0.25.0/explainers/explainer-oracle.md
create mode 100644 docs/versioned_docs/version-v0.25.0/explainers/explainer-recursion.md
create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/_category_.json
create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/_category_.json
create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/index.md
create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/project_breakdown.md
create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/installation/_category_.json
create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/installation/index.md
create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/installation/other_install_methods.md
create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/tooling/_category_.json
create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/tooling/index.mdx
create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/tooling/language_server.md
create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/tooling/testing.md
create mode 100644 docs/versioned_docs/version-v0.25.0/how_to/_category_.json
create mode 100644 docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md
create mode 100644 docs/versioned_docs/version-v0.25.0/how_to/how-to-recursion.md
create mode 100644 docs/versioned_docs/version-v0.25.0/how_to/how-to-solidity-verifier.md
create mode 100644 docs/versioned_docs/version-v0.25.0/how_to/merkle-proof.mdx
create mode 100644 docs/versioned_docs/version-v0.25.0/how_to/using-devcontainers.mdx
create mode 100644 docs/versioned_docs/version-v0.25.0/index.mdx
create mode 100644 docs/versioned_docs/version-v0.25.0/migration_notes.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/_category_.json
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/assert.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/comments.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/control_flow.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_bus.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/_category_.json
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/arrays.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/booleans.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/fields.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/function_types.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/index.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/integers.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/references.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/slices.mdx
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/strings.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/structs.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/tuples.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/distinct.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/functions.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/generics.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/globals.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/lambdas.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/mutability.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/ops.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/oracles.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/shadowing.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/traits.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/unconstrained.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/_category_.json
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/crates_and_packages.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/dependencies.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/modules.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/workspaces.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/_category_.json
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/black_box_fns.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/bn254.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/boundedvec.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/hashmap.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/index.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/vec.mdx
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/_category_.json
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/ec_primitives.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/eddsa.mdx
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/hashes.mdx
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/index.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/scalar.mdx
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/schnorr.mdx
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/logging.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/merkle_trees.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/options.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/recursion.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/traits.md
create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/zeroed.md
create mode 100644 docs/versioned_docs/version-v0.25.0/reference/_category_.json
create mode 100644 docs/versioned_docs/version-v0.25.0/reference/nargo_commands.md
create mode 100644 docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md
create mode 100644 docs/versioned_sidebars/version-v0.25.0-sidebars.json
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 8916585d7f1..b38234ca0b9 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,4 +1,4 @@
{
- ".": "0.24.0",
- "acvm-repo": "0.40.0"
+ ".": "0.25.0",
+ "acvm-repo": "0.41.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e9b2dfb48a5..cdbccf768ca 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,82 @@
# Changelog
+## [0.25.0](https://github.com/noir-lang/noir/compare/v0.24.0...v0.25.0) (2024-03-11)
+
+
+### ⚠ BREAKING CHANGES
+
+* Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898)
+* reserve `unchecked` keyword ([#4432](https://github.com/noir-lang/noir/issues/4432))
+* Remove empty value from bounded vec ([#4431](https://github.com/noir-lang/noir/issues/4431))
+* Ban Fields in for loop indices and bitwise ops ([#4376](https://github.com/noir-lang/noir/issues/4376))
+* bump msrv to 1.73.0 ([#4406](https://github.com/noir-lang/noir/issues/4406))
+* **ci:** Bump MSRV to 1.72.1 and enforce that ACVM can be published using updated lockfile ([#4385](https://github.com/noir-lang/noir/issues/4385))
+* Restrict bit sizes ([#4235](https://github.com/noir-lang/noir/issues/4235))
+* move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479)
+* note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500)
+
+### Features
+
+* Add eddsa_poseidon_to_pub function to stdlib with test + docs ([#4473](https://github.com/noir-lang/noir/issues/4473)) ([00d2c32](https://github.com/noir-lang/noir/commit/00d2c32e58176cc5de3574c8435a54d415c4a5fa))
+* Add HashMap to the stdlib ([#4242](https://github.com/noir-lang/noir/issues/4242)) ([650ffc5](https://github.com/noir-lang/noir/commit/650ffc5053cdca4b6ad2e027fa1f4fd90ef64871))
+* Add option to set max memory for bb.js ([#4227](https://github.com/noir-lang/noir/issues/4227)) ([8a6b131](https://github.com/noir-lang/noir/commit/8a6b131402892a570bc2de6f5869de73b0bd979e))
+* Add overflow and underflow checks for unsigned integers in brillig ([#4445](https://github.com/noir-lang/noir/issues/4445)) ([21fc4b8](https://github.com/noir-lang/noir/commit/21fc4b85763dccae6dce0a46a318718c3c913471))
+* Add poseidon2 opcode implementation for acvm/brillig, and Noir ([#4398](https://github.com/noir-lang/noir/issues/4398)) ([10e8292](https://github.com/noir-lang/noir/commit/10e82920798380f50046e52db4a20ca205191ab7))
+* Added cast opcode and cast calldata (https://github.com/AztecProtocol/aztec-packages/pull/4423) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a))
+* Allow type aliases to reference other aliases ([#4353](https://github.com/noir-lang/noir/issues/4353)) ([c44ef14](https://github.com/noir-lang/noir/commit/c44ef14847a436733206b6dd9590a7ab214ecd97))
+* Backpropagate constants in ACIR during optimization ([#3926](https://github.com/noir-lang/noir/issues/3926)) ([aad0da0](https://github.com/noir-lang/noir/commit/aad0da024c69663f42e6913e674682d5864b26ae))
+* **ci:** Use wasm-opt when compiling wasm packages ([#4334](https://github.com/noir-lang/noir/issues/4334)) ([e382921](https://github.com/noir-lang/noir/commit/e3829213d8411f84e117a14b43816967925095e0))
+* DAP Preflight and debugger compilation options ([#4185](https://github.com/noir-lang/noir/issues/4185)) ([e0ad0b2](https://github.com/noir-lang/noir/commit/e0ad0b2b31f6d46be75d23aec6a82850a9c4bd75))
+* Expose separate functions to compile programs vs contracts in `noir_wasm` ([#4413](https://github.com/noir-lang/noir/issues/4413)) ([7cd5fdb](https://github.com/noir-lang/noir/commit/7cd5fdb3d2a53475b7c8681231d517cab30f9f9b))
+* Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e))
+* Note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a))
+* Restrict bit sizes ([#4235](https://github.com/noir-lang/noir/issues/4235)) ([1048f81](https://github.com/noir-lang/noir/commit/1048f815abb1f27e9c84ab5b9568a3673c12a50a))
+* Run tests in parallel in `nargo test` ([#4484](https://github.com/noir-lang/noir/issues/4484)) ([761734e](https://github.com/noir-lang/noir/commit/761734e6cb3ff5911aa85d0cee96ad26092b4905))
+* Skip redundant range checks in brillig ([#4460](https://github.com/noir-lang/noir/issues/4460)) ([cb4c1c5](https://github.com/noir-lang/noir/commit/cb4c1c5264b95d01f69d99f916ced71ad9cdc9d1))
+* Sync from aztec-packages ([#4483](https://github.com/noir-lang/noir/issues/4483)) ([fe8f277](https://github.com/noir-lang/noir/commit/fe8f2776ccfde29209a2c3fc162311c99e4f59be))
+* Track stack frames and their variables in the debugger ([#4188](https://github.com/noir-lang/noir/issues/4188)) ([ae1a9d9](https://github.com/noir-lang/noir/commit/ae1a9d923998177516919bbba6ff4b0584fa1e9f))
+* TypeVariableKind for just Integers ([#4118](https://github.com/noir-lang/noir/issues/4118)) ([c956be8](https://github.com/noir-lang/noir/commit/c956be870fb47403a6da6585fce6bea2d40ee268))
+* Update error message when trying to load workspace as dependency ([#4393](https://github.com/noir-lang/noir/issues/4393)) ([d2585e7](https://github.com/noir-lang/noir/commit/d2585e738a63208fca3c9e26242e896d7f1df1e4))
+
+
+### Bug Fixes
+
+* **acir:** Array dynamic flatten ([#4351](https://github.com/noir-lang/noir/issues/4351)) ([b2aaeab](https://github.com/noir-lang/noir/commit/b2aaeab319a0c66c431a7db6852f743eccde8e98))
+* **acir:** Use types on dynamic arrays ([#4364](https://github.com/noir-lang/noir/issues/4364)) ([ba2c541](https://github.com/noir-lang/noir/commit/ba2c541ec45de92bba98de34771b73cbb7865c93))
+* Add `follow_bindings` to follow `Type::Alias` links ([#4521](https://github.com/noir-lang/noir/issues/4521)) ([b94adb9](https://github.com/noir-lang/noir/commit/b94adb92657e2b4a51dc7216a88e080aed1cf8b0))
+* Add handling to `noir_wasm` for projects without dependencies ([#4344](https://github.com/noir-lang/noir/issues/4344)) ([4982251](https://github.com/noir-lang/noir/commit/49822511710a7f1c42b8ed343e80456f8e6db2d9))
+* Allow type aliases in main ([#4505](https://github.com/noir-lang/noir/issues/4505)) ([8a5359c](https://github.com/noir-lang/noir/commit/8a5359c012579e54c2766de1074482a36ecada32))
+* Ban Fields in for loop indices and bitwise ops ([#4376](https://github.com/noir-lang/noir/issues/4376)) ([601fd9a](https://github.com/noir-lang/noir/commit/601fd9afc502236af1db0c4492698ba2298c7501))
+* Brillig range check with consistent bit size ([#4357](https://github.com/noir-lang/noir/issues/4357)) ([ea47d4a](https://github.com/noir-lang/noir/commit/ea47d4a67c6a18e4a7d3a49079d9eb24a1026a25))
+* Build noir_codegen when publishing ([#4448](https://github.com/noir-lang/noir/issues/4448)) ([cb1ceee](https://github.com/noir-lang/noir/commit/cb1ceee58b11b0ce6f8845361af3418d13c506bd))
+* Consistent bit size for truncate ([#4370](https://github.com/noir-lang/noir/issues/4370)) ([dcd7a1e](https://github.com/noir-lang/noir/commit/dcd7a1e561a68504b9038ffbb3c80f5c981f9f0c))
+* Correct formatting for databus visibility types ([#4423](https://github.com/noir-lang/noir/issues/4423)) ([cd796de](https://github.com/noir-lang/noir/commit/cd796dea4937dd1a261f154e5f2e599bbc649165))
+* Correct invalid brillig codegen for `EmbeddedCurvePoint.add` ([#4382](https://github.com/noir-lang/noir/issues/4382)) ([5051ec4](https://github.com/noir-lang/noir/commit/5051ec4d434a9e5cf405c68357faaf213e68de9e))
+* **docs:** Update install versions ([#4396](https://github.com/noir-lang/noir/issues/4396)) ([b283637](https://github.com/noir-lang/noir/commit/b283637e092038eb296c468168aec2d41e1c2734))
+* **docs:** Update noirjs_app for 0.23 ([#4378](https://github.com/noir-lang/noir/issues/4378)) ([f77f702](https://github.com/noir-lang/noir/commit/f77f702e0cfb81dcce4dd97e274b831e887ba5d2))
+* Enforce matching types of binary ops in SSA ([#4391](https://github.com/noir-lang/noir/issues/4391)) ([70866ae](https://github.com/noir-lang/noir/commit/70866aea976d59dbcbd4af34067fdd8f46555673))
+* Fix brillig slowdown when assigning arrays in loops ([#4472](https://github.com/noir-lang/noir/issues/4472)) ([2a53545](https://github.com/noir-lang/noir/commit/2a53545f4238c9b8535e6bc5b0720fa15f44f946))
+* **flake:** Stop flake.nix removing ignored-tests.txt ([#4455](https://github.com/noir-lang/noir/issues/4455)) ([ebaf05a](https://github.com/noir-lang/noir/commit/ebaf05ab10834dd10e04c7ea5130f96c6cdf98ed))
+* Force src impl for == on slices ([#4507](https://github.com/noir-lang/noir/issues/4507)) ([1691274](https://github.com/noir-lang/noir/commit/169127444e8b16a8aad4acfe29ba812894fd897c))
+* Handling of gh deps in noir_wasm ([#4499](https://github.com/noir-lang/noir/issues/4499)) ([1d65370](https://github.com/noir-lang/noir/commit/1d653704715bf9999eb6a40ed7500e752e2c73b7))
+* Iterative flattening pass ([#4492](https://github.com/noir-lang/noir/issues/4492)) ([33c1ef7](https://github.com/noir-lang/noir/commit/33c1ef70e7859fdee7babfb5d38191f53e73a0df))
+* Noir test incorrect reporting (https://github.com/AztecProtocol/aztec-packages/pull/4925) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e))
+* Only add `.nr` files to file manager ([#4380](https://github.com/noir-lang/noir/issues/4380)) ([8536c7c](https://github.com/noir-lang/noir/commit/8536c7c8ea8fc6b740b2ae6d1aef3bc7e1907b8c))
+* Remove panic when generic array length is not resolvable ([#4408](https://github.com/noir-lang/noir/issues/4408)) ([00ab3db](https://github.com/noir-lang/noir/commit/00ab3db86b06111d144516e862902b8604284611))
+* Remove print from monomorphization pass ([#4417](https://github.com/noir-lang/noir/issues/4417)) ([27c66b3](https://github.com/noir-lang/noir/commit/27c66b3d0741e68ed591ae8a16b47b30bc87175f))
+* **ssa:** Handle mergers of slices returned from calls ([#4496](https://github.com/noir-lang/noir/issues/4496)) ([f988d02](https://github.com/noir-lang/noir/commit/f988d020e43cdf36a38613f2052d4518de39193a))
+* Use correct type for numeric generics ([#4386](https://github.com/noir-lang/noir/issues/4386)) ([0a1d109](https://github.com/noir-lang/noir/commit/0a1d109f478c997da5c43876fd12464af638bb15))
+* Variables from trait constraints being permanently bound over when used within a trait impl ([#4450](https://github.com/noir-lang/noir/issues/4450)) ([ac60ef5](https://github.com/noir-lang/noir/commit/ac60ef5e12fcfb907fbdcff709d7cbad05f2b939))
+
+
+### Miscellaneous Chores
+
+* Bump msrv to 1.73.0 ([#4406](https://github.com/noir-lang/noir/issues/4406)) ([b5e5c30](https://github.com/noir-lang/noir/commit/b5e5c30f4db52c79ef556e80660f39db369b1911))
+* **ci:** Bump MSRV to 1.72.1 and enforce that ACVM can be published using updated lockfile ([#4385](https://github.com/noir-lang/noir/issues/4385)) ([2fc95d2](https://github.com/noir-lang/noir/commit/2fc95d2d82b3220267ce7d5815e7073e00ef1360))
+* Move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a))
+* Remove empty value from bounded vec ([#4431](https://github.com/noir-lang/noir/issues/4431)) ([b9384fb](https://github.com/noir-lang/noir/commit/b9384fb23abf4ab15e880fb7e03c21509a9fa8a6))
+* Reserve `unchecked` keyword ([#4432](https://github.com/noir-lang/noir/issues/4432)) ([9544813](https://github.com/noir-lang/noir/commit/9544813fabbd18a87dd88456e6a5b781bd0cf008))
+
## [0.24.0](https://github.com/noir-lang/noir/compare/v0.23.0...v0.24.0) (2024-02-12)
diff --git a/Cargo.lock b/Cargo.lock
index 317418276b1..ad3dce229fc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,7 +4,7 @@ version = 3
[[package]]
name = "acir"
-version = "0.40.0"
+version = "0.41.0"
dependencies = [
"acir_field",
"base64 0.21.2",
@@ -23,7 +23,7 @@ dependencies = [
[[package]]
name = "acir_field"
-version = "0.40.0"
+version = "0.41.0"
dependencies = [
"ark-bls12-381",
"ark-bn254",
@@ -37,7 +37,7 @@ dependencies = [
[[package]]
name = "acvm"
-version = "0.40.0"
+version = "0.41.0"
dependencies = [
"acir",
"acvm_blackbox_solver",
@@ -53,7 +53,7 @@ dependencies = [
[[package]]
name = "acvm_blackbox_solver"
-version = "0.40.0"
+version = "0.41.0"
dependencies = [
"acir",
"blake2",
@@ -68,7 +68,7 @@ dependencies = [
[[package]]
name = "acvm_js"
-version = "0.40.0"
+version = "0.41.0"
dependencies = [
"acvm",
"bn254_blackbox_solver",
@@ -212,7 +212,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "arena"
-version = "0.24.0"
+version = "0.25.0"
[[package]]
name = "ark-bls12-381"
@@ -413,7 +413,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "aztec_macros"
-version = "0.24.0"
+version = "0.25.0"
dependencies = [
"convert_case 0.6.0",
"iter-extended",
@@ -602,7 +602,7 @@ dependencies = [
[[package]]
name = "brillig"
-version = "0.40.0"
+version = "0.41.0"
dependencies = [
"acir_field",
"serde",
@@ -610,7 +610,7 @@ dependencies = [
[[package]]
name = "brillig_vm"
-version = "0.40.0"
+version = "0.41.0"
dependencies = [
"acir",
"acvm_blackbox_solver",
@@ -1706,7 +1706,7 @@ dependencies = [
[[package]]
name = "fm"
-version = "0.24.0"
+version = "0.25.0"
dependencies = [
"codespan-reporting",
"iter-extended",
@@ -2298,7 +2298,7 @@ dependencies = [
[[package]]
name = "iter-extended"
-version = "0.24.0"
+version = "0.25.0"
[[package]]
name = "itertools"
@@ -2651,7 +2651,7 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389"
[[package]]
name = "nargo"
-version = "0.24.0"
+version = "0.25.0"
dependencies = [
"acvm",
"codespan-reporting",
@@ -2678,7 +2678,7 @@ dependencies = [
[[package]]
name = "nargo_cli"
-version = "0.24.0"
+version = "0.25.0"
dependencies = [
"acvm",
"assert_cmd",
@@ -2730,7 +2730,7 @@ dependencies = [
[[package]]
name = "nargo_fmt"
-version = "0.24.0"
+version = "0.25.0"
dependencies = [
"bytecount",
"noirc_frontend",
@@ -2742,7 +2742,7 @@ dependencies = [
[[package]]
name = "nargo_toml"
-version = "0.24.0"
+version = "0.25.0"
dependencies = [
"dirs",
"fm",
@@ -2815,7 +2815,7 @@ dependencies = [
[[package]]
name = "noir_debugger"
-version = "0.24.0"
+version = "0.25.0"
dependencies = [
"acvm",
"assert_cmd",
@@ -2850,7 +2850,7 @@ dependencies = [
[[package]]
name = "noir_lsp"
-version = "0.24.0"
+version = "0.25.0"
dependencies = [
"acvm",
"async-lsp",
@@ -2876,7 +2876,7 @@ dependencies = [
[[package]]
name = "noir_wasm"
-version = "0.24.0"
+version = "0.25.0"
dependencies = [
"acvm",
"build-data",
@@ -2899,7 +2899,7 @@ dependencies = [
[[package]]
name = "noirc_abi"
-version = "0.24.0"
+version = "0.25.0"
dependencies = [
"acvm",
"iter-extended",
@@ -2916,7 +2916,7 @@ dependencies = [
[[package]]
name = "noirc_abi_wasm"
-version = "0.24.0"
+version = "0.25.0"
dependencies = [
"acvm",
"build-data",
@@ -2933,7 +2933,7 @@ dependencies = [
[[package]]
name = "noirc_driver"
-version = "0.24.0"
+version = "0.25.0"
dependencies = [
"acvm",
"aztec_macros",
@@ -2954,7 +2954,7 @@ dependencies = [
[[package]]
name = "noirc_errors"
-version = "0.24.0"
+version = "0.25.0"
dependencies = [
"acvm",
"base64 0.21.2",
@@ -2972,7 +2972,7 @@ dependencies = [
[[package]]
name = "noirc_evaluator"
-version = "0.24.0"
+version = "0.25.0"
dependencies = [
"acvm",
"fxhash",
@@ -2988,7 +2988,7 @@ dependencies = [
[[package]]
name = "noirc_frontend"
-version = "0.24.0"
+version = "0.25.0"
dependencies = [
"acvm",
"arena",
@@ -3013,7 +3013,7 @@ dependencies = [
[[package]]
name = "noirc_printable_type"
-version = "0.24.0"
+version = "0.25.0"
dependencies = [
"acvm",
"iter-extended",
diff --git a/Cargo.toml b/Cargo.toml
index 38f39137360..2ddb9c9e28f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -40,7 +40,7 @@ resolver = "2"
[workspace.package]
# x-release-please-start-version
-version = "0.24.0"
+version = "0.25.0"
# x-release-please-end
authors = ["The Noir Team "]
edition = "2021"
@@ -51,12 +51,12 @@ repository = "https://github.com/noir-lang/noir/"
[workspace.dependencies]
# ACVM workspace dependencies
-acir_field = { version = "0.40.0", path = "acvm-repo/acir_field", default-features = false }
-acir = { version = "0.40.0", path = "acvm-repo/acir", default-features = false }
-acvm = { version = "0.40.0", path = "acvm-repo/acvm" }
-brillig = { version = "0.40.0", path = "acvm-repo/brillig", default-features = false }
-brillig_vm = { version = "0.40.0", path = "acvm-repo/brillig_vm", default-features = false }
-acvm_blackbox_solver = { version = "0.40.0", path = "acvm-repo/blackbox_solver", default-features = false }
+acir_field = { version = "0.41.0", path = "acvm-repo/acir_field", default-features = false }
+acir = { version = "0.41.0", path = "acvm-repo/acir", default-features = false }
+acvm = { version = "0.41.0", path = "acvm-repo/acvm" }
+brillig = { version = "0.41.0", path = "acvm-repo/brillig", default-features = false }
+brillig_vm = { version = "0.41.0", path = "acvm-repo/brillig_vm", default-features = false }
+acvm_blackbox_solver = { version = "0.41.0", path = "acvm-repo/blackbox_solver", default-features = false }
bn254_blackbox_solver = { version = "0.39.0", path = "acvm-repo/bn254_blackbox_solver", default-features = false }
# Noir compiler workspace dependencies
diff --git a/acvm-repo/CHANGELOG.md b/acvm-repo/CHANGELOG.md
index acb465e5cc9..4f220d6eeba 100644
--- a/acvm-repo/CHANGELOG.md
+++ b/acvm-repo/CHANGELOG.md
@@ -5,6 +5,70 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.41.0](https://github.com/noir-lang/noir/compare/v0.40.0...v0.41.0) (2024-03-11)
+
+
+### ⚠ BREAKING CHANGES
+
+* Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898)
+* move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479)
+* note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500)
+* rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420)
+* Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014)
+* init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200)
+* **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221)
+* Sync commits from `aztec-packages` ([#4144](https://github.com/noir-lang/noir/issues/4144))
+* Breaking changes from aztec-packages ([#3955](https://github.com/noir-lang/noir/issues/3955))
+* Rename Arithmetic opcode to AssertZero ([#3840](https://github.com/noir-lang/noir/issues/3840))
+* Remove unused methods on ACIR opcodes ([#3841](https://github.com/noir-lang/noir/issues/3841))
+* Remove partial backend feature ([#3805](https://github.com/noir-lang/noir/issues/3805))
+
+### Features
+
+* Add bit size to const opcode (https://github.com/AztecProtocol/aztec-packages/pull/4385) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
+* Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
+* Add instrumentation for tracking variables in debugging ([#4122](https://github.com/noir-lang/noir/issues/4122)) ([c58d691](https://github.com/noir-lang/noir/commit/c58d69141b54a918cd1675400c00bfd48720f896))
+* Add poseidon2 opcode implementation for acvm/brillig, and Noir ([#4398](https://github.com/noir-lang/noir/issues/4398)) ([10e8292](https://github.com/noir-lang/noir/commit/10e82920798380f50046e52db4a20ca205191ab7))
+* Add support for overriding expression width ([#4117](https://github.com/noir-lang/noir/issues/4117)) ([c8026d5](https://github.com/noir-lang/noir/commit/c8026d557d535b10fe455165d6445076df7a03de))
+* Added cast opcode and cast calldata (https://github.com/AztecProtocol/aztec-packages/pull/4423) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a))
+* Allow brillig to read arrays directly from memory (https://github.com/AztecProtocol/aztec-packages/pull/4460) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
+* Allow nested arrays and vectors in Brillig foreign calls (https://github.com/AztecProtocol/aztec-packages/pull/4478) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
+* Allow variables and stack trace inspection in the debugger ([#4184](https://github.com/noir-lang/noir/issues/4184)) ([bf263fc](https://github.com/noir-lang/noir/commit/bf263fc8d843940f328a90f6366edd2671fb2682))
+* **avm:** Back in avm context with macro - refactor context (https://github.com/AztecProtocol/aztec-packages/pull/4438) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
+* **aztec-nr:** Initial work for aztec public vm macro (https://github.com/AztecProtocol/aztec-packages/pull/4400) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
+* Aztec-packages ([#3754](https://github.com/noir-lang/noir/issues/3754)) ([c043265](https://github.com/noir-lang/noir/commit/c043265e550b59bd4296504826fe15d3ce3e9ad2))
+* Backpropagate constants in ACIR during optimization ([#3926](https://github.com/noir-lang/noir/issues/3926)) ([aad0da0](https://github.com/noir-lang/noir/commit/aad0da024c69663f42e6913e674682d5864b26ae))
+* Breaking changes from aztec-packages ([#3955](https://github.com/noir-lang/noir/issues/3955)) ([5be049e](https://github.com/noir-lang/noir/commit/5be049eee6c342649462282ee04f6411e6ea392c))
+* Evaluation of dynamic assert messages ([#4101](https://github.com/noir-lang/noir/issues/4101)) ([c284e01](https://github.com/noir-lang/noir/commit/c284e01bfe20ceae4414dc123624b5cbb8b66d09))
+* Init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
+* Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e))
+* Note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a))
+* Remove range constraints from witnesses which are constrained to be constants ([#3928](https://github.com/noir-lang/noir/issues/3928)) ([afe9c7a](https://github.com/noir-lang/noir/commit/afe9c7a38bb9d4245205d3aa46d4ce23d70a5671))
+* Remove replacement of boolean range opcodes with `AssertZero` opcodes ([#4107](https://github.com/noir-lang/noir/issues/4107)) ([dac0e87](https://github.com/noir-lang/noir/commit/dac0e87ee3be3446b92bbb12ef4832fd493fcee3))
+* Speed up transformation of debug messages ([#3815](https://github.com/noir-lang/noir/issues/3815)) ([2a8af1e](https://github.com/noir-lang/noir/commit/2a8af1e4141ffff61547ee1c2837a6392bd5db48))
+* Sync `aztec-packages` ([#4011](https://github.com/noir-lang/noir/issues/4011)) ([fee2452](https://github.com/noir-lang/noir/commit/fee24523c427c27f0bdaf98ea09a852a2da3e94c))
+* Sync commits from `aztec-packages` ([#4068](https://github.com/noir-lang/noir/issues/4068)) ([7a8f3a3](https://github.com/noir-lang/noir/commit/7a8f3a33b57875e681e3d81e667e3570a1cdbdcc))
+* Sync commits from `aztec-packages` ([#4144](https://github.com/noir-lang/noir/issues/4144)) ([0205d3b](https://github.com/noir-lang/noir/commit/0205d3b4ad0cf5ffd775a43eb5af273a772cf138))
+* Sync from aztec-packages ([#4483](https://github.com/noir-lang/noir/issues/4483)) ([fe8f277](https://github.com/noir-lang/noir/commit/fe8f2776ccfde29209a2c3fc162311c99e4f59be))
+
+
+### Bug Fixes
+
+* Deserialize odd length hex literals ([#3747](https://github.com/noir-lang/noir/issues/3747)) ([4000fb2](https://github.com/noir-lang/noir/commit/4000fb279221eb07187d657bfaa7f1c7b311abf2))
+* Noir test incorrect reporting (https://github.com/AztecProtocol/aztec-packages/pull/4925) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e))
+* Remove panic from `init_log_level` in `acvm_js` ([#4195](https://github.com/noir-lang/noir/issues/4195)) ([2e26530](https://github.com/noir-lang/noir/commit/2e26530bf53006c1ed4fee310bcaa905c95dd95b))
+* Return error rather instead of panicking on invalid circuit ([#3976](https://github.com/noir-lang/noir/issues/3976)) ([67201bf](https://github.com/noir-lang/noir/commit/67201bfc21a9c8858aa86be9cd47d463fb78d925))
+
+
+### Miscellaneous Chores
+
+* **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
+* Move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a))
+* Remove partial backend feature ([#3805](https://github.com/noir-lang/noir/issues/3805)) ([0383100](https://github.com/noir-lang/noir/commit/0383100853a80a5b28b797cdfeae0d271f1b7805))
+* Remove unused methods on ACIR opcodes ([#3841](https://github.com/noir-lang/noir/issues/3841)) ([9e5d0e8](https://github.com/noir-lang/noir/commit/9e5d0e813d61a0bfb5ee68174ed287c5a20f1579))
+* Rename Arithmetic opcode to AssertZero ([#3840](https://github.com/noir-lang/noir/issues/3840)) ([836f171](https://github.com/noir-lang/noir/commit/836f17145c2901060706294461c2d282dd121b3e))
+* Rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
+
## [0.40.0](https://github.com/noir-lang/noir/compare/v0.39.0...v0.40.0) (2024-02-12)
diff --git a/acvm-repo/acir/Cargo.toml b/acvm-repo/acir/Cargo.toml
index 7021333486f..be859d7d054 100644
--- a/acvm-repo/acir/Cargo.toml
+++ b/acvm-repo/acir/Cargo.toml
@@ -2,7 +2,7 @@
name = "acir"
description = "ACIR is the IR that the VM processes, it is analogous to LLVM IR"
# x-release-please-start-version
-version = "0.40.0"
+version = "0.41.0"
# x-release-please-end
authors.workspace = true
edition.workspace = true
diff --git a/acvm-repo/acir_field/Cargo.toml b/acvm-repo/acir_field/Cargo.toml
index 6f4971770bd..c2056b73277 100644
--- a/acvm-repo/acir_field/Cargo.toml
+++ b/acvm-repo/acir_field/Cargo.toml
@@ -2,7 +2,7 @@
name = "acir_field"
description = "The field implementation being used by ACIR."
# x-release-please-start-version
-version = "0.40.0"
+version = "0.41.0"
# x-release-please-end
authors.workspace = true
edition.workspace = true
diff --git a/acvm-repo/acvm/Cargo.toml b/acvm-repo/acvm/Cargo.toml
index fce9a8e8e8b..d585850170a 100644
--- a/acvm-repo/acvm/Cargo.toml
+++ b/acvm-repo/acvm/Cargo.toml
@@ -2,7 +2,7 @@
name = "acvm"
description = "The virtual machine that processes ACIR given a backend/proof system."
# x-release-please-start-version
-version = "0.40.0"
+version = "0.41.0"
# x-release-please-end
authors.workspace = true
edition.workspace = true
diff --git a/acvm-repo/acvm_js/Cargo.toml b/acvm-repo/acvm_js/Cargo.toml
index 7ec814a72e5..63fca2bd32a 100644
--- a/acvm-repo/acvm_js/Cargo.toml
+++ b/acvm-repo/acvm_js/Cargo.toml
@@ -2,7 +2,7 @@
name = "acvm_js"
description = "Typescript wrapper around the ACVM allowing execution of ACIR code"
# x-release-please-start-version
-version = "0.40.0"
+version = "0.41.0"
# x-release-please-end
authors.workspace = true
edition.workspace = true
diff --git a/acvm-repo/acvm_js/package.json b/acvm-repo/acvm_js/package.json
index 876db9ccb62..0a9cd7235f5 100644
--- a/acvm-repo/acvm_js/package.json
+++ b/acvm-repo/acvm_js/package.json
@@ -1,6 +1,6 @@
{
"name": "@noir-lang/acvm_js",
- "version": "0.40.0",
+ "version": "0.41.0",
"publishConfig": {
"access": "public"
},
diff --git a/acvm-repo/blackbox_solver/Cargo.toml b/acvm-repo/blackbox_solver/Cargo.toml
index 0794b2dbe7e..a783193edba 100644
--- a/acvm-repo/blackbox_solver/Cargo.toml
+++ b/acvm-repo/blackbox_solver/Cargo.toml
@@ -2,7 +2,7 @@
name = "acvm_blackbox_solver"
description = "A solver for the blackbox functions found in ACIR and Brillig"
# x-release-please-start-version
-version = "0.40.0"
+version = "0.41.0"
# x-release-please-end
authors.workspace = true
edition.workspace = true
diff --git a/acvm-repo/brillig/Cargo.toml b/acvm-repo/brillig/Cargo.toml
index 8d91d19e117..57f89e091b4 100644
--- a/acvm-repo/brillig/Cargo.toml
+++ b/acvm-repo/brillig/Cargo.toml
@@ -2,7 +2,7 @@
name = "brillig"
description = "Brillig is the bytecode ACIR uses for non-determinism."
# x-release-please-start-version
-version = "0.40.0"
+version = "0.41.0"
# x-release-please-end
authors.workspace = true
edition.workspace = true
diff --git a/acvm-repo/brillig_vm/Cargo.toml b/acvm-repo/brillig_vm/Cargo.toml
index 272e8389413..1c7add5cb40 100644
--- a/acvm-repo/brillig_vm/Cargo.toml
+++ b/acvm-repo/brillig_vm/Cargo.toml
@@ -2,7 +2,7 @@
name = "brillig_vm"
description = "The virtual machine that processes Brillig bytecode, used to introduce non-determinism to the ACVM"
# x-release-please-start-version
-version = "0.40.0"
+version = "0.41.0"
# x-release-please-end
authors.workspace = true
edition.workspace = true
diff --git a/compiler/wasm/package.json b/compiler/wasm/package.json
index 67584a2def1..6dfa3215483 100644
--- a/compiler/wasm/package.json
+++ b/compiler/wasm/package.json
@@ -3,7 +3,7 @@
"contributors": [
"The Noir Team "
],
- "version": "0.24.0",
+ "version": "0.25.0",
"license": "(MIT OR Apache-2.0)",
"main": "dist/main.js",
"types": "./dist/types/src/index.d.cts",
diff --git a/docs/versioned_docs/version-v0.25.0/explainers/explainer-oracle.md b/docs/versioned_docs/version-v0.25.0/explainers/explainer-oracle.md
new file mode 100644
index 00000000000..b84ca5dd986
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/explainers/explainer-oracle.md
@@ -0,0 +1,57 @@
+---
+title: Oracles
+description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations.
+keywords:
+ - Noir Programming
+ - Oracles
+ - JSON-RPC
+ - Foreign Call Handlers
+ - Constrained Functions
+ - Blockchain Programming
+sidebar_position: 1
+---
+
+If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs.
+
+![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg)
+
+A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source?
+
+Oracles are functions that provide this feature.
+
+## Use cases
+
+An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method.
+
+Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md).
+
+In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles.
+
+## Constraining oracles
+
+Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information.
+
+To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have a oracle call like this:
+
+```rust
+#[oracle(getNoun)]
+unconstrained fn get_noun(address: Field) -> Field
+```
+
+This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract.
+
+In short, **Oracles don't prove anything. Your Noir program does.**
+
+:::danger
+
+If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained!
+
+:::
+
+## How to use Oracles
+
+On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running.
+
+In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they matches the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling.
+
+If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that.
diff --git a/docs/versioned_docs/version-v0.25.0/explainers/explainer-recursion.md b/docs/versioned_docs/version-v0.25.0/explainers/explainer-recursion.md
new file mode 100644
index 00000000000..18846176ca7
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/explainers/explainer-recursion.md
@@ -0,0 +1,176 @@
+---
+title: Recursive proofs
+description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency.
+
+keywords:
+ [
+ "Recursive Proofs",
+ "Zero-Knowledge Programming",
+ "Noir",
+ "EVM Blockchain",
+ "Smart Contracts",
+ "Recursion in Noir",
+ "Alice and Bob Guessing Game",
+ "Recursive Merkle Tree",
+ "Reusable Components",
+ "Optimizing Computational Resources",
+ "Improving Efficiency",
+ "Verification Key",
+ "Aggregation",
+ "Recursive zkSNARK schemes",
+ "PLONK",
+ "Proving and Verification Keys"
+ ]
+sidebar_position: 1
+pagination_next: how_to/how-to-recursion
+---
+
+In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number:
+
+```js
+function factorial(n) {
+ if (n === 0 || n === 1) {
+ return 1;
+ } else {
+ return n * factorial(n - 1);
+ }
+}
+```
+
+In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack:
+
+```md
+ Is `n` 1? <---------
+ /\ /
+ / \ n = n -1
+ / \ /
+ Yes No --------
+```
+
+In Zero-Knowledge, recursion has some similarities.
+
+It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid.
+
+This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism.
+
+## Examples
+
+Let us look at some of these examples
+
+### Alice and Bob - Guessing game
+
+Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins.
+
+So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed.
+
+This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it.
+
+As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?".
+
+She can then generate a proof that she verified his proof, and so on.
+
+```md
+ Did you fail? <--------------------------
+ / \ /
+ / \ n = n -1
+ / \ /
+ Yes No /
+ | | /
+ | | /
+ | You win /
+ | /
+ | /
+Generate proof of that /
+ + /
+ my own guess ----------------
+```
+
+### Charlie - Recursive merkle tree
+
+Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count!
+
+If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such:
+
+```md
+ abcd
+ __________|______________
+ | |
+ ab cd
+ _____|_____ ______|______
+ | | | |
+ alice bob charlie daniel
+```
+
+Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes.
+
+### Daniel - Reusable components
+
+Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit.
+
+He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup.
+
+## What params do I need
+
+As you can see in the [recursion reference](noir/standard_library/recursion.md), a simple recursive proof requires:
+
+- The proof to verify
+- The Verification Key of the circuit that generated the proof
+- A hash of this verification key, as it's needed for some backends
+- The public inputs for the proof
+
+:::info
+
+Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs.
+
+So, taking the example of Alice and Bob and their guessing game:
+
+- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit
+- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract.
+
+We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid.
+
+:::
+
+## Some architecture
+
+As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples:
+
+### Adding some logic to a proof verification
+
+This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections:
+
+- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify)
+- A `guessing` section, which is basically the logic part where the actual guessing happens
+
+In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on.
+
+### Aggregating proofs
+
+In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere.
+
+To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits:
+
+- A `main`, non-recursive circuit with some logic
+- A `recursive` circuit meant to verify two proofs in one proof
+
+The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day.
+
+### Recursively verifying different circuits
+
+Nothing prevents you from verifying different circuits in a recursive proof, for example:
+
+- A `circuit1` circuit
+- A `circuit2` circuit
+- A `recursive` circuit
+
+In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received)
+
+## How fast is it
+
+At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later.
+
+Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package.
+
+## How can I try it
+
+Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples).
diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/_category_.json b/docs/versioned_docs/version-v0.25.0/getting_started/_category_.json
new file mode 100644
index 00000000000..5d694210bbf
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/getting_started/_category_.json
@@ -0,0 +1,5 @@
+{
+ "position": 0,
+ "collapsible": true,
+ "collapsed": true
+}
diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/_category_.json b/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/_category_.json
new file mode 100644
index 00000000000..23b560f610b
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/_category_.json
@@ -0,0 +1,5 @@
+{
+ "position": 1,
+ "collapsible": true,
+ "collapsed": true
+}
diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/index.md b/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/index.md
new file mode 100644
index 00000000000..743c4d8d634
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/index.md
@@ -0,0 +1,142 @@
+---
+title: Creating a Project
+description:
+ Learn how to create and verify your first Noir program using Nargo, a programming language for
+ zero-knowledge proofs.
+keywords:
+ [
+ Nargo,
+ Noir,
+ zero-knowledge proofs,
+ programming language,
+ create Noir program,
+ verify Noir program,
+ step-by-step guide,
+ ]
+sidebar_position: 1
+
+---
+
+Now that we have installed Nargo, it is time to make our first hello world program!
+
+## Create a Project Directory
+
+Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home
+directory to house our Noir programs.
+
+For Linux, macOS, and Windows PowerShell, create the directory and change directory into it by
+running:
+
+```sh
+mkdir ~/projects
+cd ~/projects
+```
+
+## Create Our First Nargo Project
+
+Now that we are in the projects directory, create a new Nargo project by running:
+
+```sh
+nargo new hello_world
+```
+
+> **Note:** `hello_world` can be any arbitrary project name, we are simply using `hello_world` for
+> demonstration.
+>
+> In production, the common practice is to name the project folder as `circuits` for better
+> identifiability when sitting alongside other folders in the codebase (e.g. `contracts`, `scripts`,
+> `test`).
+
+A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and
+_Nargo.toml_ which contain the source code and environmental options of your Noir program
+respectively.
+
+### Intro to Noir Syntax
+
+Let us take a closer look at _main.nr_. The default _main.nr_ generated should look like this:
+
+```rust
+fn main(x : Field, y : pub Field) {
+ assert(x != y);
+}
+```
+
+The first line of the program specifies the program's inputs:
+
+```rust
+x : Field, y : pub Field
+```
+
+Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the
+keyword `pub` (e.g. `y`). To learn more about private and public values, check the
+[Data Types](../../noir/concepts/data_types/index.md) section.
+
+The next line of the program specifies its body:
+
+```rust
+assert(x != y);
+```
+
+The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages.
+
+For more Noir syntax, check the [Language Concepts](../../noir/concepts/comments.md) chapter.
+
+## Build In/Output Files
+
+Change directory into _hello_world_ and build in/output files for your Noir program by running:
+
+```sh
+cd hello_world
+nargo check
+```
+
+Two additional files would be generated in your project directory:
+
+_Prover.toml_ houses input values, and _Verifier.toml_ houses public values.
+
+## Prove Our Noir Program
+
+Now that the project is set up, we can create a proof of correct execution of our Noir program.
+
+Fill in input values for execution in the _Prover.toml_ file. For example:
+
+```toml
+x = "1"
+y = "2"
+```
+
+Prove the valid execution of your Noir program:
+
+```sh
+nargo prove
+```
+
+A new folder _proofs_ would then be generated in your project directory, containing the proof file
+`.proof`, where the project name is defined in Nargo.toml.
+
+The _Verifier.toml_ file would also be updated with the public values computed from program
+execution (in this case the value of `y`):
+
+```toml
+y = "0x0000000000000000000000000000000000000000000000000000000000000002"
+```
+
+> **Note:** Values in _Verifier.toml_ are computed as 32-byte hex values.
+
+## Verify Our Noir Program
+
+Once a proof is generated, we can verify correct execution of our Noir program by verifying the
+proof file.
+
+Verify your proof by running:
+
+```sh
+nargo verify
+```
+
+The verification will complete in silence if it is successful. If it fails, it will log the
+corresponding error instead.
+
+Congratulations, you have now created and verified a proof for your very first Noir program!
+
+In the [next section](./project_breakdown.md), we will go into more detail on each step performed.
diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/project_breakdown.md b/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/project_breakdown.md
new file mode 100644
index 00000000000..6160a102c6c
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/project_breakdown.md
@@ -0,0 +1,199 @@
+---
+title: Project Breakdown
+description:
+ Learn about the anatomy of a Nargo project, including the purpose of the Prover and Verifier TOML
+ files, and how to prove and verify your program.
+keywords:
+ [Nargo, Nargo project, Prover.toml, Verifier.toml, proof verification, private asset transfer]
+sidebar_position: 2
+---
+
+This section breaks down our hello world program from the previous section. We elaborate on the project
+structure and what the `prove` and `verify` commands did.
+
+## Anatomy of a Nargo Project
+
+Upon creating a new project with `nargo new` and building the in/output files with `nargo check`
+commands, you would get a minimal Nargo project of the following structure:
+
+ - src
+ - Prover.toml
+ - Verifier.toml
+ - Nargo.toml
+
+The source directory _src_ holds the source code for your Noir program. By default only a _main.nr_
+file will be generated within it.
+
+### Prover.toml
+
+_Prover.toml_ is used for specifying the input values for executing and proving the program. You can specify `toml` files with different names by using the `--prover-name` or `-p` flags, see the [Prover](#provertoml) section below. Optionally you may specify expected output values for prove-time checking as well.
+
+### Verifier.toml
+
+_Verifier.toml_ contains public in/output values computed when executing the Noir program.
+
+### Nargo.toml
+
+_Nargo.toml_ contains the environmental options of your project. It contains a "package" section and a "dependencies" section.
+
+Example Nargo.toml:
+
+```toml
+[package]
+name = "noir_starter"
+type = "bin"
+authors = ["Alice"]
+compiler_version = "0.9.0"
+description = "Getting started with Noir"
+entry = "circuit/main.nr"
+license = "MIT"
+
+[dependencies]
+ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"}
+```
+
+Nargo.toml for a [workspace](../../noir/modules_packages_crates/workspaces.md) will look a bit different. For example:
+
+```toml
+[workspace]
+members = ["crates/a", "crates/b"]
+default-member = "crates/a"
+```
+
+#### Package section
+
+The package section defines a number of fields including:
+
+- `name` (**required**) - the name of the package
+- `type` (**required**) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contract
+- `authors` (optional) - authors of the project
+- `compiler_version` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html)
+- `description` (optional)
+- `entry` (optional) - a relative filepath to use as the entry point into your package (overrides the default of `src/lib.nr` or `src/main.nr`)
+- `backend` (optional)
+- `license` (optional)
+
+#### Dependencies section
+
+This is where you will specify any dependencies for your project. See the [Dependencies page](../../noir/modules_packages_crates/dependencies.md) for more info.
+
+`./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or
+verifier contract respectively.
+
+### main.nr
+
+The _main.nr_ file contains a `main` method, this method is the entry point into your Noir program.
+
+In our sample program, _main.nr_ looks like this:
+
+```rust
+fn main(x : Field, y : Field) {
+ assert(x != y);
+}
+```
+
+The parameters `x` and `y` can be seen as the API for the program and must be supplied by the
+prover. Since neither `x` nor `y` is marked as public, the verifier does not supply any inputs, when
+verifying the proof.
+
+The prover supplies the values for `x` and `y` in the _Prover.toml_ file.
+
+As for the program body, `assert` ensures that the condition to be satisfied (e.g. `x != y`) is
+constrained by the proof of the execution of said program (i.e. if the condition was not met, the
+verifier would reject the proof as an invalid proof).
+
+### Prover.toml
+
+The _Prover.toml_ file is a file which the prover uses to supply his witness values(both private and
+public).
+
+In our hello world program the _Prover.toml_ file looks like this:
+
+```toml
+x = "1"
+y = "2"
+```
+
+When the command `nargo prove` is executed, two processes happen:
+
+1. Noir creates a proof that `x`, which holds the value of `1`, and `y`, which holds the value of `2`,
+ is not equal. This inequality constraint is due to the line `assert(x != y)`.
+
+2. Noir creates and stores the proof of this statement in the _proofs_ directory in a file called your-project.proof. So if your project is named "private_voting" (defined in the project Nargo.toml), the proof will be saved at `./proofs/private_voting.proof`. Opening this file will display the proof in hex format.
+
+#### Arrays of Structs
+
+The following code shows how to pass an array of structs to a Noir program to generate a proof.
+
+```rust
+// main.nr
+struct Foo {
+ bar: Field,
+ baz: Field,
+}
+
+fn main(foos: [Foo; 3]) -> pub Field {
+ foos[2].bar + foos[2].baz
+}
+```
+
+Prover.toml:
+
+```toml
+[[foos]] # foos[0]
+bar = 0
+baz = 0
+
+[[foos]] # foos[1]
+bar = 0
+baz = 0
+
+[[foos]] # foos[2]
+bar = 1
+baz = 2
+```
+
+#### Custom toml files
+
+You can specify a `toml` file with a different name to use for proving by using the `--prover-name` or `-p` flags.
+
+This command looks for proof inputs in the default **Prover.toml** and generates the proof and saves it at `./proofs/.proof`:
+
+```bash
+nargo prove
+```
+
+This command looks for proof inputs in the custom **OtherProver.toml** and generates proof and saves it at `./proofs/.proof`:
+
+```bash
+nargo prove -p OtherProver
+```
+
+## Verifying a Proof
+
+When the command `nargo verify` is executed, two processes happen:
+
+1. Noir checks in the _proofs_ directory for a proof file with the project name (eg. test_project.proof)
+
+2. If that file is found, the proof's validity is checked
+
+> **Note:** The validity of the proof is linked to the current Noir program; if the program is
+> changed and the verifier verifies the proof, it will fail because the proof is not valid for the
+> _modified_ Noir program.
+
+In production, the prover and the verifier are usually two separate entities. A prover would
+retrieve the necessary inputs, execute the Noir program, generate a proof and pass it to the
+verifier. The verifier would then retrieve the public inputs, usually from external sources, and
+verify the validity of the proof against it.
+
+Take a private asset transfer as an example:
+
+A person using a browser as the prover would retrieve private inputs locally (e.g. the user's private key) and
+public inputs (e.g. the user's encrypted balance on-chain), compute the transfer, generate a proof
+and submit it to the verifier smart contract.
+
+The verifier contract would then draw the user's encrypted balance directly from the blockchain and
+verify the proof submitted against it. If the verification passes, additional functions in the
+verifier contract could trigger (e.g. approve the asset transfer).
+
+Now that you understand the concepts, you'll probably want some editor feedback while you are writing more complex code.
diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/installation/_category_.json b/docs/versioned_docs/version-v0.25.0/getting_started/installation/_category_.json
new file mode 100644
index 00000000000..0c02fb5d4d7
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/getting_started/installation/_category_.json
@@ -0,0 +1,6 @@
+{
+ "position": 0,
+ "label": "Install Nargo",
+ "collapsible": true,
+ "collapsed": true
+}
diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/installation/index.md b/docs/versioned_docs/version-v0.25.0/getting_started/installation/index.md
new file mode 100644
index 00000000000..4ef86aa5914
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/getting_started/installation/index.md
@@ -0,0 +1,48 @@
+---
+title: Nargo Installation
+description:
+ nargo is a command line tool for interacting with Noir programs. This page is a quick guide on how to install Nargo through the most common and easy method, noirup
+keywords: [
+ Nargo
+ Noir
+ Rust
+ Cargo
+ Noirup
+ Installation
+ Terminal Commands
+ Version Check
+ Nightlies
+ Specific Versions
+ Branches
+ Noirup Repository
+]
+pagination_next: getting_started/hello_noir/index
+---
+
+`nargo` is the one-stop-shop for almost everything related with Noir. The name comes from our love for Rust and its package manager `cargo`.
+
+With `nargo`, you can start new projects, compile, execute, prove, verify, test, generate solidity contracts, and do pretty much all that is available in Noir.
+
+Similarly to `rustup`, we also maintain an easy installation method that covers most machines: `noirup`.
+
+## Installing Noirup
+
+Open a terminal on your machine, and write:
+
+```bash
+curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash
+```
+
+Close the terminal, open another one, and run
+
+```bash
+noirup
+```
+
+Done. That's it. You should have the latest version working. You can check with `nargo --version`.
+
+You can also install nightlies, specific versions
+or branches. Check out the [noirup repository](https://github.com/noir-lang/noirup) for more
+information.
+
+Now we're ready to start working on [our first Noir program!](../hello_noir/index.md)
diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/installation/other_install_methods.md b/docs/versioned_docs/version-v0.25.0/getting_started/installation/other_install_methods.md
new file mode 100644
index 00000000000..a35e34aaf9c
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/getting_started/installation/other_install_methods.md
@@ -0,0 +1,254 @@
+---
+title: Alternative Install Methods
+description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains other methods that don't rely on noirup, such as compiling from source, installing from binaries, and using WSL for windows
+keywords: [
+ Installation
+ Nargo
+ Noirup
+ Binaries
+ Compiling from Source
+ WSL for Windows
+ macOS
+ Linux
+ Nix
+ Direnv
+ Shell & editor experience
+ Building and testing
+ Uninstalling Nargo
+ Noir vs code extension,
+ ]
+sidebar_position: 1
+---
+
+## Encouraged Installation Method: Noirup
+
+Noirup is the endorsed method for installing Nargo, streamlining the process of fetching binaries or compiling from source. It supports a range of options to cater to your specific needs, from nightly builds and specific versions to compiling from various sources.
+
+### Installing Noirup
+
+First, ensure you have `noirup` installed:
+
+```sh
+curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash
+```
+
+### Fetching Binaries
+
+With `noirup`, you can easily switch between different Nargo versions, including nightly builds:
+
+- **Nightly Version**: Install the latest nightly build.
+
+ ```sh
+ noirup --version nightly
+ ```
+
+- **Specific Version**: Install a specific version of Nargo.
+ ```sh
+ noirup --version
+ ```
+
+### Compiling from Source
+
+`noirup` also enables compiling Nargo from various sources:
+
+- **From a Specific Branch**: Install from the latest commit on a branch.
+
+ ```sh
+ noirup --branch
+ ```
+
+- **From a Fork**: Install from the main branch of a fork.
+
+ ```sh
+ noirup --repo
+ ```
+
+- **From a Specific Branch in a Fork**: Install from a specific branch in a fork.
+
+ ```sh
+ noirup --repo --branch
+ ```
+
+- **From a Specific Pull Request**: Install from a specific PR.
+
+ ```sh
+ noirup --pr
+ ```
+
+- **From a Specific Commit**: Install from a specific commit.
+
+ ```sh
+ noirup -C
+ ```
+
+- **From Local Source**: Compile and install from a local directory.
+ ```sh
+ noirup --path ./path/to/local/source
+ ```
+
+## Alternate Installation Methods (No Longer Recommended)
+
+While the following methods are available, they are no longer recommended. We advise using noirup for a more efficient and flexible installation experience.
+
+However, there are other methods for installing Nargo:
+
+- [Binaries](#option-1-installing-from-binaries)
+- [Compiling from Source](#option-2-compile-from-source)
+- [WSL for Windows](#option-3-wsl-for-windows)
+
+### Option 1: Installing from Binaries
+
+See [GitHub Releases](https://github.com/noir-lang/noir/releases) for the latest and previous
+platform specific binaries.
+
+#### Step 1
+
+Paste and run the following in the terminal to extract and install the binary:
+
+> **macOS / Linux:** If you are prompted with `Permission denied` when running commands, prepend
+> `sudo` and re-run it.
+
+##### macOS (Apple Silicon)
+
+```bash
+mkdir -p $HOME/.nargo/bin && \
+curl -o $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.24.0/nargo-aarch64-apple-darwin.tar.gz && \
+tar -xvf $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \
+echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \
+source ~/.zshrc
+```
+
+##### macOS (Intel)
+
+```bash
+mkdir -p $HOME/.nargo/bin && \
+curl -o $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.24.0/nargo-x86_64-apple-darwin.tar.gz && \
+tar -xvf $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \
+echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \
+source ~/.zshrc
+```
+
+##### Linux (Bash)
+
+```bash
+mkdir -p $HOME/.nargo/bin && \
+curl -o $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.24.0/nargo-x86_64-unknown-linux-gnu.tar.gz && \
+tar -xvf $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -C $HOME/.nargo/bin/ && \
+echo -e '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.bashrc && \
+source ~/.bashrc
+```
+
+#### Step 2
+
+Check if the installation was successful by running `nargo --version`. You should get a version number.
+
+> **macOS:** If you are prompted with an OS alert, right-click and open the _nargo_ executable from
+> Finder. Close the new terminal popped up and `nargo` should now be accessible.
+
+### Option 2: Compile from Source
+
+Due to the large number of native dependencies, Noir projects uses [Nix](https://nixos.org/) and [direnv](https://direnv.net/) to streamline the development experience. It helps mitigating issues commonly associated with dependency management, such as conflicts between required package versions for different projects (often referred to as "dependency hell").
+
+Combined with direnv, which automatically sets or clears environment variables based on the directory, it further simplifies the development process by seamlessly integrating with the developer's shell, facilitating an efficient and reliable workflow for managing and deploying Noir projects with multiple dependencies.
+
+#### Setting up your environment
+
+For the best experience, please follow these instructions to setup your environment:
+
+1. Install Nix following [their guide](https://nixos.org/download.html) for your operating system.
+2. Create the file `~/.config/nix/nix.conf` with the contents:
+
+```ini
+experimental-features = nix-command
+extra-experimental-features = flakes
+```
+
+3. Install direnv into your Nix profile by running:
+
+```sh
+nix profile install nixpkgs#direnv
+```
+
+4. Add direnv to your shell following [their guide](https://direnv.net/docs/hook.html).
+ 1. For bash or zshell, add `eval "$(direnv hook bash)"` or `eval "$(direnv hook zsh)"` to your ~/.bashrc or ~/.zshrc file, respectively.
+5. Restart your shell.
+
+#### Shell & editor experience
+
+Now that your environment is set up, you can get to work on the project.
+
+1. Clone the repository, such as:
+
+```sh
+git clone git@github.com:noir-lang/noir
+```
+
+> Replacing `noir` with whichever repository you want to work on.
+
+2. Navigate to the directory:
+
+```sh
+cd noir
+```
+
+> Replacing `noir` with whichever repository you cloned.
+
+3. You should see a **direnv error** because projects aren't allowed by default. Make sure you've reviewed and trust our `.envrc` file, then you need to run:
+
+```sh
+direnv allow
+```
+
+4. Now, wait awhile for all the native dependencies to be built. This will take some time and direnv will warn you that it is taking a long time, but we just need to let it run.
+
+5. Once you are presented with your prompt again, you can start your editor within the project directory (we recommend [VSCode](https://code.visualstudio.com/)):
+
+```sh
+code .
+```
+
+6. (Recommended) When launching VSCode for the first time, you should be prompted to install our recommended plugins. We highly recommend installing these for the best development experience.
+
+#### Building and testing
+
+Assuming you are using `direnv` to populate your environment, building and testing the project can be done
+with the typical `cargo build`, `cargo test`, and `cargo clippy` commands. You'll notice that the `cargo` version matches the version we specify in `rust-toolchain.toml`, which is 1.73.0 at the time of this writing.
+
+If you want to build the entire project in an isolated sandbox, you can use Nix commands:
+
+1. `nix build .` (or `nix build . -L` for verbose output) to build the project in a Nix sandbox.
+2. `nix flake check` (or `nix flake check -L` for verbose output) to run clippy and tests in a Nix sandbox.
+
+#### Without `direnv`
+
+If you have hesitations with using direnv, you can launch a subshell with `nix develop` and then launch your editor from within the subshell. However, if VSCode was already launched in the project directory, the environment won't be updated.
+
+Advanced: If you aren't using direnv nor launching your editor within the subshell, you can try to install Barretenberg and other global dependencies the package needs. This is an advanced workflow and likely won't receive support!
+
+### Option 3: WSL (for Windows)
+
+The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. However, it is available by using Windows Subsystem for Linux (WSL).
+
+Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL.
+
+step 2: Follow the [Noirup instructions](#encouraged-installation-method-noirup).
+
+## Uninstalling Nargo
+
+### Noirup
+
+If you installed Nargo with `noirup` or through directly downloading binaries, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system.
+
+```bash
+rm -r ~/.nargo
+rm -r ~/nargo
+rm -r ~/noir_cache
+```
+
+### Nix
+
+If you installed Nargo with Nix or compiled it from source, you can remove the binary located at `~/.nix-profile/bin/nargo`.
+
+```bash
+rm ~/.nix-profile/bin/nargo
+```
diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/tooling/_category_.json b/docs/versioned_docs/version-v0.25.0/getting_started/tooling/_category_.json
new file mode 100644
index 00000000000..55804c03a71
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/getting_started/tooling/_category_.json
@@ -0,0 +1,6 @@
+{
+ "position": 2,
+ "label": "Tooling",
+ "collapsible": true,
+ "collapsed": true
+}
diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/tooling/index.mdx b/docs/versioned_docs/version-v0.25.0/getting_started/tooling/index.mdx
new file mode 100644
index 00000000000..ac480f3c9f5
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/getting_started/tooling/index.mdx
@@ -0,0 +1,38 @@
+---
+title: Tooling
+Description: This section provides information about the various tools and utilities available for Noir development. It covers the Noir playground, IDE tools, Codespaces, and community projects.
+Keywords: [Noir, Development, Playground, IDE Tools, Language Service Provider, VS Code Extension, Codespaces, noir-starter, Community Projects, Awesome Noir Repository, Developer Tooling]
+---
+
+Noir is meant to be easy to develop with. For that reason, a number of utilities have been put together to ease the development process as much as feasible in the zero-knowledge world.
+
+## Playground
+
+The Noir playground is an easy way to test small ideas, share snippets, and integrate in other websites. You can access it at [play.noir-lang.org](https://play.noir-lang.org).
+
+## IDE tools
+
+When you install Nargo, you're also installing a Language Service Provider (LSP), which can be used by IDEs to provide syntax highlighting, codelens, warnings, and more.
+
+The easiest way to use these tools is by installing the [Noir VS Code extension](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir).
+
+## Codespaces
+
+Some Noir repos have leveraged Codespaces in order to ease the development process. You can visit the [noir-starter](https://github.com/noir-lang/noir-starter) for an example.
+
+
+
+## GitHub Actions
+
+You can use `noirup` with GitHub Actions for CI/CD and automated testing. It is as simple as
+installing `noirup` and running tests in your GitHub Action `yml` file.
+
+See the
+[config file in the Noir repo](https://github.com/TomAFrench/noir-hashes/blob/master/.github/workflows/noir.yml) for an example usage.
+
+## Community projects
+
+As an open-source project, Noir has received many contributions over time. Some of them are related with developer tooling, and you can see some of them in [Awesome Noir repository](https://github.com/noir-lang/awesome-noir#dev-tools)
diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/tooling/language_server.md b/docs/versioned_docs/version-v0.25.0/getting_started/tooling/language_server.md
new file mode 100644
index 00000000000..81e0356ef8a
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/getting_started/tooling/language_server.md
@@ -0,0 +1,43 @@
+---
+title: Language Server
+description: Learn about the Noir Language Server, how to install the components, and configuration that may be required.
+keywords: [Nargo, Language Server, LSP, VSCode, Visual Studio Code]
+sidebar_position: 0
+---
+
+This section helps you install and configure the Noir Language Server.
+
+The Language Server Protocol (LSP) has two components, the [Server](#language-server) and the [Client](#language-client). Below we describe each in the context of Noir.
+
+## Language Server
+
+The Server component is provided by the Nargo command line tool that you installed at the beginning of this guide.
+As long as Nargo is installed and you've used it to run other commands in this guide, it should be good to go!
+
+If you'd like to verify that the `nargo lsp` command is available, you can run `nargo --help` and look for `lsp` in the list of commands. If you see it, you're using a version of Noir with LSP support.
+
+## Language Client
+
+The Client component is usually an editor plugin that launches the Server. It communicates LSP messages between the editor and the Server. For example, when you save a file, the Client will alert the Server, so it can try to compile the project and report any errors.
+
+Currently, Noir provides a Language Client for Visual Studio Code via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir).
+
+> **Note:** Noir's Language Server Protocol support currently assumes users' VSCode workspace root to be the same as users' Noir project root (i.e. where Nargo.toml lies).
+>
+> If LSP features seem to be missing / malfunctioning, make sure you are opening your Noir project directly (instead of as a sub-folder) in your VSCode instance.
+
+When your language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, measuring circuit size, execution, and tests:
+
+![Compile and Execute](@site/static/img/codelens_compile_execute.png)
+![Run test](@site/static/img/codelens_run_test.png)
+
+You should also see your tests in the `testing` panel:
+
+![Testing panel](@site/static/img/codelens_testing_panel.png)
+
+### Configuration
+
+- **Noir: Enable LSP** - If checked, the extension will launch the Language Server via `nargo lsp` and communicate with it.
+- **Noir: Nargo Flags** - Additional flags may be specified if you require them to be added when the extension calls `nargo lsp`.
+- **Noir: Nargo Path** - An absolute path to a Nargo binary with the `lsp` command. This may be useful if Nargo is not within the `PATH` of your editor.
+- **Noir > Trace: Server** - Setting this to `"messages"` or `"verbose"` will log LSP messages between the Client and Server. Useful for debugging.
diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/tooling/testing.md b/docs/versioned_docs/version-v0.25.0/getting_started/tooling/testing.md
new file mode 100644
index 00000000000..d3e0c522473
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/getting_started/tooling/testing.md
@@ -0,0 +1,62 @@
+---
+title: Testing in Noir
+description: Learn how to use Nargo to test your Noir program in a quick and easy way
+keywords: [Nargo, testing, Noir, compile, test]
+sidebar_position: 1
+---
+
+You can test your Noir programs using Noir circuits.
+
+Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if
+you run `nargo test`.
+
+For example if you have a program like:
+
+```rust
+fn add(x: u64, y: u64) -> u64 {
+ x + y
+}
+#[test]
+fn test_add() {
+ assert(add(2,2) == 4);
+ assert(add(0,1) == 1);
+ assert(add(1,0) == 1);
+}
+```
+
+Running `nargo test` will test that the `test_add` function can be executed while satisfying all
+the constraints which allows you to test that add returns the expected values. Test functions can't
+have any arguments currently.
+
+### Test fail
+
+You can write tests that are expected to fail by using the decorator `#[test(should_fail)]`. For example:
+
+```rust
+fn add(x: u64, y: u64) -> u64 {
+ x + y
+}
+#[test(should_fail)]
+fn test_add() {
+ assert(add(2,2) == 5);
+}
+```
+
+You can be more specific and make it fail with a specific reason by using `should_fail_with = "`:
+
+```rust
+fn main(african_swallow_avg_speed : Field) {
+ assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow");
+}
+
+#[test]
+fn test_king_arthur() {
+ main(65);
+}
+
+#[test(should_fail_with = "What is the airspeed velocity of an unladen swallow")]
+fn test_bridgekeeper() {
+ main(32);
+}
+
+```
diff --git a/docs/versioned_docs/version-v0.25.0/how_to/_category_.json b/docs/versioned_docs/version-v0.25.0/how_to/_category_.json
new file mode 100644
index 00000000000..23b560f610b
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/how_to/_category_.json
@@ -0,0 +1,5 @@
+{
+ "position": 1,
+ "collapsible": true,
+ "collapsed": true
+}
diff --git a/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md
new file mode 100644
index 00000000000..0d84d992320
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md
@@ -0,0 +1,280 @@
+---
+title: How to use Oracles
+description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS.
+keywords:
+ - Noir Programming
+ - Oracles
+ - Nargo
+ - NoirJS
+ - JSON RPC Server
+ - Foreign Call Handlers
+sidebar_position: 1
+---
+
+This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that:
+
+- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept.
+- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate.
+- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher.
+- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.).
+
+For reference, you can find the snippets used in this tutorial on the [Aztec DevRel Repository](https://github.com/AztecProtocol/dev-rel/tree/main/code-snippets/how-to-oracles).
+
+## Rundown
+
+This guide has 3 major steps:
+
+1. How to modify our Noir program to make use of oracle calls as unconstrained functions
+2. How to write a JSON RPC Server to resolve these oracle calls with Nargo
+3. How to use them in Nargo and how to provide a custom resolver in NoirJS
+
+## Step 1 - Modify your Noir program
+
+An oracle is defined in a Noir program by defining two methods:
+
+- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md).
+- A decorated oracle method - This tells the compiler that this method is an RPC call.
+
+An example of an oracle that returns a `Field` would be:
+
+```rust
+#[oracle(getSqrt)]
+unconstrained fn sqrt(number: Field) -> Field { }
+
+unconstrained fn get_sqrt(number: Field) -> Field {
+ sqrt(number)
+}
+```
+
+In this example, we're wrapping our oracle function in a unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function:
+
+```rust
+fn main(input: Field) {
+ let sqrt = get_sqrt(input);
+}
+```
+
+In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use.
+
+:::danger
+
+As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example:
+
+```rust
+fn main(input: Field) {
+ let sqrt = get_sqrt(input);
+ assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle!
+}
+```
+
+:::
+
+:::info
+
+Currently, oracles only work with single params or array params. For example:
+
+```rust
+#[oracle(getSqrt)]
+unconstrained fn sqrt([Field; 2]) -> [Field; 2] { }
+```
+
+:::
+
+## Step 2 - Write an RPC server
+
+Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript.
+
+Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots:
+
+```rust
+#[oracle(getSqrt)]
+unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { }
+
+unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] {
+ sqrt(input)
+}
+
+fn main(input: [Field; 2]) {
+ let sqrt = get_sqrt(input);
+ assert(sqrt[0].pow_32(2) as u64 == input[0] as u64);
+ assert(sqrt[1].pow_32(2) as u64 == input[1] as u64);
+}
+```
+
+:::info
+
+Why square root?
+
+In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values.
+
+:::
+
+Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example):
+
+```js
+import { JSONRPCServer } from "json-rpc-2.0";
+import express from "express";
+import bodyParser from "body-parser";
+
+const app = express();
+app.use(bodyParser.json());
+
+const server = new JSONRPCServer();
+app.post("/", (req, res) => {
+ const jsonRPCRequest = req.body;
+ server.receive(jsonRPCRequest).then((jsonRPCResponse) => {
+ if (jsonRPCResponse) {
+ res.json(jsonRPCResponse);
+ } else {
+ res.sendStatus(204);
+ }
+ });
+});
+
+app.listen(5555);
+```
+
+Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots:
+
+```js
+server.addMethod("getSqrt", async (params) => {
+ const values = params[0].Array.map(({ inner }) => {
+ return { inner: `${Math.sqrt(parseInt(inner, 16))}` };
+ });
+ return { values: [{ Array: values }] };
+});
+```
+
+:::tip
+
+Brillig expects an object with an array of values. Each value is an object declaring to be `Single` or `Array` and returning a `inner` property *as a string*. For example:
+
+```json
+{ "values": [{ "Array": [{ "inner": "1" }, { "inner": "2"}]}]}
+{ "values": [{ "Single": { "inner": "1" }}]}
+{ "values": [{ "Single": { "inner": "1" }}, { "Array": [{ "inner": "1", { "inner": "2" }}]}]}
+```
+
+If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow:
+
+```js
+interface Value {
+ inner: string,
+}
+
+interface SingleForeignCallParam {
+ Single: Value,
+}
+
+interface ArrayForeignCallParam {
+ Array: Value[],
+}
+
+type ForeignCallParam = SingleForeignCallParam | ArrayForeignCallParam;
+
+interface ForeignCallResult {
+ values: ForeignCallParam[],
+}
+```
+
+:::
+
+## Step 3 - Usage with Nargo
+
+Using the [`nargo` CLI tool](../getting_started/installation/index.md), you can use oracles in the `nargo test`, `nargo execute` and `nargo prove` commands by passing a value to `--oracle-resolver`. For example:
+
+```bash
+nargo test --oracle-resolver http://localhost:5555
+```
+
+This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator.
+
+## Step 4 - Usage with NoirJS
+
+In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything.
+
+For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app:
+
+```js
+const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc
+
+await noir.generateFinalProof(inputs, foreignCallHandler)
+```
+
+As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo.
+
+:::tip
+
+Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)?
+
+You don't technically have to, but then how would you run `nargo test` or `nargo prove`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server.
+
+:::
+
+In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client.
+
+For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code):
+
+```js
+import { JSONRPCClient } from "json-rpc-2.0";
+
+// declaring the JSONRPCClient
+const client = new JSONRPCClient((jsonRPCRequest) => {
+// hitting the same JSON RPC Server we coded above
+ return fetch("http://localhost:5555", {
+ method: "POST",
+ headers: {
+ "content-type": "application/json",
+ },
+ body: JSON.stringify(jsonRPCRequest),
+ }).then((response) => {
+ if (response.status === 200) {
+ return response
+ .json()
+ .then((jsonRPCResponse) => client.receive(jsonRPCResponse));
+ } else if (jsonRPCRequest.id !== undefined) {
+ return Promise.reject(new Error(response.statusText));
+ }
+ });
+});
+
+// declaring a function that takes the name of the foreign call (getSqrt) and the inputs
+const foreignCallHandler = async (name, input) => {
+ // notice that the "inputs" parameter contains *all* the inputs
+ // in this case we to make the RPC request with the first parameter "numbers", which would be input[0]
+ const oracleReturn = await client.request(name, [
+ { Array: input[0].map((i) => ({ inner: i.toString("hex") })) },
+ ]);
+ return [oracleReturn.values[0].Array.map((x) => x.inner)];
+};
+
+// the rest of your NoirJS code
+const input = { input: [4, 16] };
+const { witness } = await noir.execute(numbers, foreignCallHandler);
+```
+
+:::tip
+
+If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem:
+
+```bash
+yarn add cors
+```
+
+and use it as a middleware:
+
+```js
+import cors from "cors";
+
+const app = express();
+app.use(cors())
+```
+
+:::
+
+## Conclusion
+
+Hopefully by the end of this guide, you should be able to:
+
+- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands.
+- Provide custom foreign call handlers for NoirJS.
diff --git a/docs/versioned_docs/version-v0.25.0/how_to/how-to-recursion.md b/docs/versioned_docs/version-v0.25.0/how_to/how-to-recursion.md
new file mode 100644
index 00000000000..4c45bb87ae2
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/how_to/how-to-recursion.md
@@ -0,0 +1,179 @@
+---
+title: How to use recursion on NoirJS
+description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `backend_barretenberg`.
+keywords:
+ [
+ "NoirJS",
+ "EVM blockchain",
+ "smart contracts",
+ "recursion",
+ "solidity verifiers",
+ "Barretenberg backend",
+ "noir_js",
+ "backend_barretenberg",
+ "intermediate proofs",
+ "final proofs",
+ "nargo compile",
+ "json import",
+ "recursive circuit",
+ "recursive app"
+ ]
+sidebar_position: 1
+---
+
+This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that:
+
+- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md).
+- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md)
+- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.md), and understand how it works.
+
+It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`.
+
+:::info
+
+As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit.
+
+While "standard" usage of NoirJS packages abstracts final proofs, it currently lacks the necessary interface to abstract away intermediate proofs. This means that these proofs need to be created by using the backend directly.
+
+In short:
+
+- `noir_js` generates *only* final proofs
+- `backend_barretenberg` generates both types of proofs
+
+:::
+
+In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume the following:
+
+- `main`: a circuit of type `assert(x != y)`, where `main` is marked with a `#[recursive]` attribute. This attribute states that the backend should generate proofs that are friendly for verification within another circuit.
+- `recursive`: a circuit that verifies `main`
+
+For a full example on how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide.
+
+## Step 1: Setup
+
+In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface.
+
+For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object.
+
+It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency:
+
+```js
+const backend = new Backend(circuit, { threads: 8 })
+```
+
+:::tip
+You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores
+:::
+
+## Step 2: Generating the witness and the proof for `main`
+
+After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness.
+
+```js
+const noir = new Noir(circuit, backend)
+const { witness } = noir.execute(input)
+```
+
+With this witness, you are now able to generate the intermediate proof for the main circuit:
+
+```js
+const { proof, publicInputs } = await backend.generateProof(witness)
+```
+
+:::warning
+
+Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why!
+
+In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof.
+
+With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain.
+
+:::
+
+## Step 3 - Verification and proof artifacts
+
+Optionally, you are able to verify the intermediate proof:
+
+```js
+const verified = await backend.verifyProof({ proof, publicInputs })
+```
+
+This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate recursive proof artifacts that will be passed to the circuit that is verifying the proof we just generated. Instead of passing the proof and verification key as a byte array, we pass them as fields which makes it cheaper to verify in a circuit:
+
+```js
+const { proofAsFields, vkAsFields, vkHash } = await backend.generateRecursiveProofArtifacts( { publicInputs, proof }, publicInputsCount)
+```
+
+This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away.
+
+:::info
+
+The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`.
+
+:::
+
+:::warning
+
+One common mistake is to forget *who* makes this call.
+
+In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything!
+
+Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit.
+
+:::
+
+## Step 4 - Recursive proof generation
+
+With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it:
+
+```js
+const recursiveInputs = {
+ verification_key: vkAsFields, // array of length 114
+ proof: proofAsFields, // array of length 93 + size of public inputs
+ publicInputs: [mainInput.y], // using the example above, where `y` is the only public input
+ key_hash: vkHash,
+}
+
+const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now!
+const { proof, publicInputs } = backend.generateProof(witness)
+const verified = backend.verifyProof({ proof, publicInputs })
+```
+
+You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way!
+
+:::tip
+
+Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example:
+
+```js
+const circuits = {
+ main: mainJSON,
+ recursive: recursiveJSON
+}
+const backends = {
+ main: new BarretenbergBackend(circuits.main),
+ recursive: new BarretenbergBackend(circuits.recursive)
+}
+const noir_programs = {
+ main: new Noir(circuits.main, backends.main),
+ recursive: new Noir(circuits.recursive, backends.recursive)
+}
+```
+
+This allows you to neatly call exactly the method you want without conflicting names:
+
+```js
+// Alice runs this 👇
+const { witness: mainWitness } = await noir_programs.main.execute(input)
+const proof = await backends.main.generateProof(mainWitness)
+
+// Bob runs this 👇
+const verified = await backends.main.verifyProof(proof)
+const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateRecursiveProofArtifacts(
+ proof,
+ numPublicInputs,
+);
+const recursiveProof = await noir_programs.recursive.generateProof(recursiveInputs)
+```
+
+:::
diff --git a/docs/versioned_docs/version-v0.25.0/how_to/how-to-solidity-verifier.md b/docs/versioned_docs/version-v0.25.0/how_to/how-to-solidity-verifier.md
new file mode 100644
index 00000000000..e3c7c1065da
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/how_to/how-to-solidity-verifier.md
@@ -0,0 +1,231 @@
+---
+title: Generate a Solidity Verifier
+description:
+ Learn how to run the verifier as a smart contract on the blockchain. Compile a Solidity verifier
+ contract for your Noir program and deploy it on any EVM blockchain acting as a verifier smart
+ contract. Read more to find out
+keywords:
+ [
+ solidity verifier,
+ smart contract,
+ blockchain,
+ compiler,
+ plonk_vk.sol,
+ EVM blockchain,
+ verifying Noir programs,
+ proving backend,
+ Barretenberg,
+ ]
+sidebar_position: 0
+pagination_next: tutorials/noirjs_app
+---
+
+Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum.
+
+This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions.
+
+This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that:
+
+- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network
+- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/installation/index.md) with Nargo and the example Hello Noir circuit
+- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you.
+
+## Rundown
+
+Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide:
+
+1. How to generate a solidity smart contract
+2. How to compile the smart contract in the RemixIDE
+3. How to deploy it to a testnet
+
+## Step 1 - Generate a contract
+
+This is by far the most straight-forward step. Just run:
+
+```sh
+nargo codegen-verifier
+```
+
+A new `contract` folder would then be generated in your project directory, containing the Solidity
+file `plonk_vk.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract.
+
+:::info
+
+It is possible to generate verifier contracts of Noir programs for other smart contract platforms as long as the proving backend supplies an implementation.
+
+Barretenberg, the default proving backend for Nargo, supports generation of verifier contracts, for the time being these are only in Solidity.
+:::
+
+## Step 2 - Compiling
+
+We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open
+Remix and create a blank workspace.
+
+![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png)
+
+We will create a new file to contain the contract Nargo generated, and copy-paste its content.
+
+:::warning
+
+You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet.
+
+:::
+
+To compile our the verifier, we can navigate to the compilation tab:
+
+![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png)
+
+Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error:
+
+![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png)
+
+This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice.
+
+:::info
+
+This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly.
+
+:::
+
+![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png)
+
+## Step 3 - Deploying
+
+At this point we should have a compiled contract read to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM.
+
+Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together:
+
+- An `UltraVerificationKey` library which simply stores the verification key for our circuit.
+- An abstract contract `BaseUltraVerifier` containing most of the verifying logic.
+- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract.
+
+Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy":
+
+![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png)
+
+A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking the deployer contract is the correct one.
+
+:::note
+
+Why "UltraVerifier"?
+
+To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir.
+
+In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name.
+
+:::
+
+## Step 4 - Verifying
+
+To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract:
+
+```solidity
+function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool)
+```
+
+When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. For `_proof`, run `nargo prove` and use the string in `proof/.proof` (adding the hex `0x` prefix). We can also copy the public input from `Verifier.toml`, as it will be properly formatted as 32-byte strings:
+
+```
+0x...... , [0x0000.....02]
+```
+
+A programmatic example of how the `verify` function is called can be seen in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35):
+
+```solidity
+function castVote(bytes calldata proof, uint proposalId, uint vote, bytes32 nullifierHash) public returns (bool) {
+ // ...
+ bytes32[] memory publicInputs = new bytes32[](4);
+ publicInputs[0] = merkleRoot;
+ publicInputs[1] = bytes32(proposalId);
+ publicInputs[2] = bytes32(vote);
+ publicInputs[3] = nullifierHash;
+ require(verifier.verify(proof, publicInputs), "Invalid proof");
+```
+
+:::info[Return Values]
+
+A circuit doesn't have the concept of a return value. Return values are just syntactic sugar in
+Noir.
+
+Under the hood, the return value is passed as an input to the circuit and is checked at the end of
+the circuit program.
+
+For example, if you have Noir program like this:
+
+```rust
+fn main(
+ // Public inputs
+ pubkey_x: pub Field,
+ pubkey_y: pub Field,
+ // Private inputs
+ priv_key: Field,
+) -> pub Field
+```
+
+the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. Like before, these values are populated in Verifier.toml after running `nargo prove`.
+
+Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`.
+
+In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return]`.
+
+:::
+
+:::tip[Structs]
+
+You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array.
+
+For example, consider the following program:
+
+```rust
+struct Type1 {
+ val1: Field,
+ val2: Field,
+}
+
+struct Nested {
+ t1: Type1,
+ is_true: bool,
+}
+
+fn main(x: pub Field, nested: pub Nested, y: pub Field) {
+ //...
+}
+```
+
+The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]`
+
+:::
+
+The other function you can call is our entrypoint `verify` function, as defined above.
+
+:::tip
+
+It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas.
+
+This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas.
+
+It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage).
+
+:::
+
+## A Note on EVM chains
+
+ZK-SNARK verification depends on some precompiled cryptographic primitives such as Elliptic Curve Pairings (if you like complex math, you can read about EC Pairings [here](https://medium.com/@VitalikButerin/exploring-elliptic-curve-pairings-c73c1864e627)). Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them.
+
+For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work:
+
+- Optimism
+- Arbitrum
+- Polygon PoS
+- Scroll
+- Celo
+
+If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains.
+
+## What's next
+
+Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry).
+
+You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository.
+
+You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that.
diff --git a/docs/versioned_docs/version-v0.25.0/how_to/merkle-proof.mdx b/docs/versioned_docs/version-v0.25.0/how_to/merkle-proof.mdx
new file mode 100644
index 00000000000..34074659ac1
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/how_to/merkle-proof.mdx
@@ -0,0 +1,48 @@
+---
+title: Prove Merkle Tree Membership
+description:
+ Learn how to use merkle membership proof in Noir to prove that a given leaf is a member of a
+ merkle tree with a specified root, at a given index.
+keywords:
+ [merkle proof, merkle membership proof, Noir, rust, hash function, Pedersen, sha256, merkle tree]
+---
+
+Let's walk through an example of a merkle membership proof in Noir that proves that a given leaf is
+in a merkle tree.
+
+```rust
+use dep::std;
+
+fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) {
+ let leaf = std::hash::hash_to_field(message);
+ let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath);
+ assert(merkle_root == root);
+}
+
+```
+
+The message is hashed using `hash_to_field`. The specific hash function that is being used is chosen
+by the backend. The only requirement is that this hash function can heuristically be used as a
+random oracle. If only collision resistance is needed, then one can call `std::hash::pedersen_hash`
+instead.
+
+```rust
+let leaf = std::hash::hash_to_field(message);
+```
+
+The leaf is then passed to a compute_merkle_root function with the root, index and hashpath. The returned root can then be asserted to be the same as the provided root.
+
+```rust
+let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath);
+assert (merkle_root == root);
+```
+
+> **Note:** It is possible to re-implement the merkle tree implementation without standard library.
+> However, for most usecases, it is enough. In general, the standard library will always opt to be
+> as conservative as possible, while striking a balance with efficiency.
+
+An example, the merkle membership proof, only requires a hash function that has collision
+resistance, hence a hash function like Pedersen is allowed, which in most cases is more efficient
+than the even more conservative sha256.
+
+[View an example on the starter repo](https://github.com/noir-lang/noir-examples/blob/3ea09545cabfa464124ec2f3ea8e60c608abe6df/stealthdrop/circuits/src/main.nr#L20)
diff --git a/docs/versioned_docs/version-v0.25.0/how_to/using-devcontainers.mdx b/docs/versioned_docs/version-v0.25.0/how_to/using-devcontainers.mdx
new file mode 100644
index 00000000000..727ec6ca667
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/how_to/using-devcontainers.mdx
@@ -0,0 +1,110 @@
+---
+title: Developer Containers and Codespaces
+description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally."
+keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"]
+sidebar_position: 1
+---
+
+Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser.
+
+## What's a devcontainer after all?
+
+A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box.
+
+There are many advantages to this:
+
+- It's platform and architecture agnostic
+- You don't need to have an IDE installed, or Nargo, or use a terminal at all
+- It's safer for using on a public machine or public network
+
+One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use.
+Enter Codespaces.
+
+## Codespaces
+
+If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof?
+
+Nothing! Except perhaps the 30-40$ per hour it will cost you.
+
+The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick.
+
+Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool:
+
+- You can start coding Noir in less than a minute
+- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be
+- It makes it easy to share work with your frens
+- It's fully reusable, you can stop and restart whenever you need to
+
+:::info
+
+Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine.
+
+:::
+
+## Tell me it's _actually_ easy
+
+It is!
+
+Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide.
+
+
+
+8 simple steps:
+
+#### 1. Create a new repository on GitHub.
+
+#### 2. Click "Start coding with Codespaces". This will use the default image.
+
+#### 3. Create a folder called `.devcontainer` in the root of your repository.
+
+#### 4. Create a Dockerfile in that folder, and paste the following code:
+
+```docker
+FROM --platform=linux/amd64 node:lts-bookworm-slim
+SHELL ["/bin/bash", "-c"]
+RUN apt update && apt install -y curl bash git tar gzip libc++-dev
+RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash
+ENV PATH="/root/.nargo/bin:$PATH"
+RUN noirup
+ENTRYPOINT ["nargo"]
+```
+#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code:
+
+```json
+{
+ "name": "Noir on Codespaces",
+ "build": {
+ "context": ".",
+ "dockerfile": "Dockerfile"
+ },
+ "customizations": {
+ "vscode": {
+ "extensions": ["noir-lang.vscode-noir"]
+ }
+ }
+}
+```
+#### 6. Commit and push your changes
+
+This will pull the new image and build it, so it could take a minute or so
+
+#### 8. Done!
+Just wait for the build to finish, and there's your easy Noir environment.
+
+
+Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces.
+
+
+
+## How do I use it?
+
+Using the codespace is obviously much easier than setting it up.
+Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go.
+
+:::info
+
+If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more.
+Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info.
diff --git a/docs/versioned_docs/version-v0.25.0/index.mdx b/docs/versioned_docs/version-v0.25.0/index.mdx
new file mode 100644
index 00000000000..75086ddcdde
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/index.mdx
@@ -0,0 +1,67 @@
+---
+title: Noir Lang
+hide_title: true
+description:
+ Learn about the public alpha release of Noir, a domain specific language heavily influenced by Rust that compiles to
+ an intermediate language which can be compiled to an arithmetic circuit or a rank-1 constraint system.
+keywords:
+ [Noir,
+ Domain Specific Language,
+ Rust,
+ Intermediate Language,
+ Arithmetic Circuit,
+ Rank-1 Constraint System,
+ Ethereum Developers,
+ Protocol Developers,
+ Blockchain Developers,
+ Proving System,
+ Smart Contract Language]
+sidebar_position: 0
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+
+
+Noir is a Domain-Specific Language for SNARK proving systems developed by [Aztec Labs](https://aztec.network/). It allows you to generate complex Zero-Knowledge Programs (ZKP) by using simple and flexible syntax, requiring no previous knowledge on the underlying mathematics or cryptography.
+
+ZK programs are programs that can generate short proofs of a certain statement without revealing some details about it. You can read more about ZKPs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56).
+
+## What's new about Noir?
+
+Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend.
+
+:::info
+
+Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend.
+
+However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin.
+
+:::
+
+## Who is Noir for?
+
+Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used:
+
+
+
+
+
+ Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library.
+
+
+
+ Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/)
+
+
+ Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner.
+
+
+
+
+## Libraries
+
+Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust.
+The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community.
+Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information.
diff --git a/docs/versioned_docs/version-v0.25.0/migration_notes.md b/docs/versioned_docs/version-v0.25.0/migration_notes.md
new file mode 100644
index 00000000000..6bd740024e5
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/migration_notes.md
@@ -0,0 +1,105 @@
+---
+title: Migration notes
+description: Read about migration notes from previous versions, which could solve problems while updating
+keywords: [Noir, notes, migration, updating, upgrading]
+---
+
+Noir is in full-speed development. Things break fast, wild, and often. This page attempts to leave some notes on errors you might encounter when upgrading and how to resolve them until proper patches are built.
+
+### `backend encountered an error: libc++.so.1`
+
+Depending on your OS, you may encounter the following error when running `nargo prove` for the first time:
+
+```text
+The backend encountered an error: "/home/codespace/.nargo/backends/acvm-backend-barretenberg/backend_binary: error while loading shared libraries: libc++.so.1: cannot open shared object file: No such file or directory\n"
+```
+
+Install the `libc++-dev` library with:
+
+```bash
+sudo apt install libc++-dev
+```
+
+## ≥0.19
+
+### Enforcing `compiler_version`
+
+From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use.
+
+To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`.
+
+## ≥0.14
+
+The index of the [for loops](noir/concepts/control_flow.md#loops) is now of type `u64` instead of `Field`. An example refactor would be:
+
+```rust
+for i in 0..10 {
+ let i = i as Field;
+}
+```
+
+## ≥v0.11.0 and Nargo backend
+
+From this version onwards, Nargo starts managing backends through the `nargo backend` command. Upgrading to the versions per usual steps might lead to:
+
+### `backend encountered an error`
+
+This is likely due to the existing locally installed version of proving backend (e.g. barretenberg) is incompatible with the version of Nargo in use.
+
+To fix the issue:
+
+1. Uninstall the existing backend
+
+```bash
+nargo backend uninstall acvm-backend-barretenberg
+```
+
+You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends.
+
+2. Reinstall a compatible version of the proving backend.
+
+If you are using the default barretenberg backend, simply run:
+
+```
+nargo prove
+```
+
+with your Noir program.
+
+This will trigger the download and installation of the latest version of barretenberg compatible with your Nargo in use.
+
+### `backend encountered an error: illegal instruction`
+
+On certain Intel-based systems, an `illegal instruction` error may arise due to incompatibility of barretenberg with certain CPU instructions.
+
+To fix the issue:
+
+1. Uninstall the existing backend
+
+```bash
+nargo backend uninstall acvm-backend-barretenberg
+```
+
+You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends.
+
+2. Reinstall a compatible version of the proving backend.
+
+If you are using the default barretenberg backend, simply run:
+
+```
+nargo backend install acvm-backend-barretenberg https://github.com/noir-lang/barretenberg-js-binary/raw/master/run-bb.tar.gz
+```
+
+This downloads and installs a specific bb.js based version of barretenberg binary from GitHub.
+
+The gzipped file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), where we need to gzip it as the Nargo currently expect the backend to be zipped up.
+
+Then run:
+
+```
+DESIRED_BINARY_VERSION=0.8.1 nargo info
+```
+
+This overrides the bb native binary with a bb.js node application instead, which should be compatible with most if not all hardware. This does come with the drawback of being generally slower than native binary.
+
+0.8.1 indicates bb.js version 0.8.1, so if you change that it will update to a different version or the default version in the script if none was supplied.
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/_category_.json b/docs/versioned_docs/version-v0.25.0/noir/concepts/_category_.json
new file mode 100644
index 00000000000..7da08f8a8c5
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/_category_.json
@@ -0,0 +1,6 @@
+{
+ "label": "Concepts",
+ "position": 0,
+ "collapsible": true,
+ "collapsed": true
+}
\ No newline at end of file
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/assert.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/assert.md
new file mode 100644
index 00000000000..bcff613a695
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/assert.md
@@ -0,0 +1,45 @@
+---
+title: Assert Function
+description:
+ Learn about the assert function in Noir, which can be used to explicitly constrain the predicate or
+ comparison expression that follows to be true, and what happens if the expression is false at
+ runtime.
+keywords: [Noir programming language, assert statement, predicate expression, comparison expression]
+sidebar_position: 4
+---
+
+Noir includes a special `assert` function which will explicitly constrain the predicate/comparison
+expression that follows to be true. If this expression is false at runtime, the program will fail to
+be proven. Example:
+
+```rust
+fn main(x : Field, y : Field) {
+ assert(x == y);
+}
+```
+
+> Assertions only work for predicate operations, such as `==`. If there's any ambiguity on the operation, the program will fail to compile. For example, it is unclear if `assert(x + y)` would check for `x + y == 0` or simply would return `true`.
+
+You can optionally provide a message to be logged when the assertion fails:
+
+```rust
+assert(x == y, "x and y are not equal");
+```
+
+Aside string literals, the optional message can be a format string or any other type supported as input for Noir's [print](../standard_library/logging.md) functions. This feature lets you incorporate runtime variables into your failed assertion logs:
+
+```rust
+assert(x == y, f"Expected x == y, but got {x} == {y}");
+```
+
+Using a variable as an assertion message directly:
+
+```rust
+struct myStruct {
+ myField: Field
+}
+
+let s = myStruct { myField: y };
+assert(s.myField == x, s);
+```
+
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/comments.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/comments.md
new file mode 100644
index 00000000000..b51a85f5c94
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/comments.md
@@ -0,0 +1,33 @@
+---
+title: Comments
+description:
+ Learn how to write comments in Noir programming language. A comment is a line of code that is
+ ignored by the compiler, but it can be read by programmers. Single-line and multi-line comments
+ are supported in Noir.
+keywords: [Noir programming language, comments, single-line comments, multi-line comments]
+sidebar_position: 10
+---
+
+A comment is a line in your codebase which the compiler ignores, however it can be read by
+programmers.
+
+Here is a single line comment:
+
+```rust
+// This is a comment and is ignored
+```
+
+`//` is used to tell the compiler to ignore the rest of the line.
+
+Noir also supports multi-line block comments. Start a block comment with `/*` and end the block with `*/`.
+
+Noir does not natively support doc comments. You may be able to use [Rust doc comments](https://doc.rust-lang.org/reference/comments.html) in your code to leverage some Rust documentation build tools with Noir code.
+
+```rust
+/*
+ This is a block comment describing a complex function.
+*/
+fn main(x : Field, y : pub Field) {
+ assert(x != y);
+}
+```
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/control_flow.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/control_flow.md
new file mode 100644
index 00000000000..4ce65236db3
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/control_flow.md
@@ -0,0 +1,45 @@
+---
+title: Control Flow
+description:
+ Learn how to use loops and if expressions in the Noir programming language. Discover the syntax
+ and examples for for loops and if-else statements.
+keywords: [Noir programming language, loops, for loop, if-else statements, Rust syntax]
+sidebar_position: 2
+---
+
+## Loops
+
+Noir has one kind of loop: the `for` loop. `for` loops allow you to repeat a block of code multiple
+times.
+
+The following block of code between the braces is run 10 times.
+
+```rust
+for i in 0..10 {
+ // do something
+};
+```
+
+The index for loops is of type `u64`.
+
+## If Expressions
+
+Noir supports `if-else` statements. The syntax is most similar to Rust's where it is not required
+for the statement's conditional to be surrounded by parentheses.
+
+```rust
+let a = 0;
+let mut x: u32 = 0;
+
+if a == 0 {
+ if a != 0 {
+ x = 6;
+ } else {
+ x = 2;
+ }
+} else {
+ x = 5;
+ assert(x == 5);
+}
+assert(x == 2);
+```
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_bus.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_bus.md
new file mode 100644
index 00000000000..e54fc861257
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_bus.md
@@ -0,0 +1,21 @@
+---
+title: Data Bus
+sidebar_position: 13
+---
+**Disclaimer** this feature is experimental, do not use it!
+
+The data bus is an optimization that the backend can use to make recursion more efficient.
+In order to use it, you must define some inputs of the program entry points (usually the `main()`
+function) with the `call_data` modifier, and the return values with the `return_data` modifier.
+These modifiers are incompatible with `pub` and `mut` modifiers.
+
+## Example
+
+```rust
+fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 {
+ let a = z[x];
+ a+y
+}
+```
+
+As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process.
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/_category_.json b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/_category_.json
new file mode 100644
index 00000000000..5d694210bbf
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/_category_.json
@@ -0,0 +1,5 @@
+{
+ "position": 0,
+ "collapsible": true,
+ "collapsed": true
+}
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/arrays.md
new file mode 100644
index 00000000000..a8bd338e736
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/arrays.md
@@ -0,0 +1,251 @@
+---
+title: Arrays
+description:
+ Dive into the Array data type in Noir. Grasp its methods, practical examples, and best practices for efficiently using Arrays in your Noir code.
+keywords:
+ [
+ noir,
+ array type,
+ methods,
+ examples,
+ indexing,
+ ]
+sidebar_position: 4
+---
+
+An array is one way of grouping together values into one compound type. Array types can be inferred
+or explicitly specified via the syntax `[; ]`:
+
+```rust
+fn main(x : Field, y : Field) {
+ let my_arr = [x, y];
+ let your_arr: [Field; 2] = [x, y];
+}
+```
+
+Here, both `my_arr` and `your_arr` are instantiated as an array containing two `Field` elements.
+
+Array elements can be accessed using indexing:
+
+```rust
+fn main() {
+ let a = [1, 2, 3, 4, 5];
+
+ let first = a[0];
+ let second = a[1];
+}
+```
+
+All elements in an array must be of the same type (i.e. homogeneous). That is, an array cannot group
+a `Field` value and a `u8` value together for example.
+
+You can write mutable arrays, like:
+
+```rust
+fn main() {
+ let mut arr = [1, 2, 3, 4, 5];
+ assert(arr[0] == 1);
+
+ arr[0] = 42;
+ assert(arr[0] == 42);
+}
+```
+
+You can instantiate a new array of a fixed size with the same value repeated for each element. The following example instantiates an array of length 32 where each element is of type Field and has the value 0.
+
+```rust
+let array: [Field; 32] = [0; 32];
+```
+
+Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array:
+
+```rust
+let array: [Field; 32] = [0; 32];
+let sl = array.as_slice()
+```
+
+You can define multidimensional arrays:
+
+```rust
+let array : [[Field; 2]; 2];
+let element = array[0][0];
+```
+However, multidimensional slices are not supported. For example, the following code will error at compile time:
+```rust
+let slice : [[Field]] = [];
+```
+
+## Types
+
+You can create arrays of primitive types or structs. There is not yet support for nested arrays
+(arrays of arrays) or arrays of structs that contain arrays.
+
+## Methods
+
+For convenience, the STD provides some ready-to-use, common methods for arrays.
+Each of these functions are located within the generic impl `impl [T; N] {`.
+So anywhere `self` appears, it refers to the variable `self: [T; N]`.
+
+### len
+
+Returns the length of an array
+
+```rust
+fn len(self) -> Field
+```
+
+example
+
+```rust
+fn main() {
+ let array = [42, 42];
+ assert(array.len() == 2);
+}
+```
+
+### sort
+
+Returns a new sorted array. The original array remains untouched. Notice that this function will
+only work for arrays of fields or integers, not for any arbitrary type. This is because the sorting
+logic it uses internally is optimized specifically for these values. If you need a sort function to
+sort any type, you should use the function `sort_via` described below.
+
+```rust
+fn sort(self) -> [T; N]
+```
+
+example
+
+```rust
+fn main() {
+ let arr = [42, 32];
+ let sorted = arr.sort();
+ assert(sorted == [32, 42]);
+}
+```
+
+### sort_via
+
+Sorts the array with a custom comparison function
+
+```rust
+fn sort_via(self, ordering: fn(T, T) -> bool) -> [T; N]
+```
+
+example
+
+```rust
+fn main() {
+ let arr = [42, 32]
+ let sorted_ascending = arr.sort_via(|a, b| a < b);
+ assert(sorted_ascending == [32, 42]); // verifies
+
+ let sorted_descending = arr.sort_via(|a, b| a > b);
+ assert(sorted_descending == [32, 42]); // does not verify
+}
+```
+
+### map
+
+Applies a function to each element of the array, returning a new array containing the mapped elements.
+
+```rust
+fn map(self, f: fn(T) -> U) -> [U; N]
+```
+
+example
+
+```rust
+let a = [1, 2, 3];
+let b = a.map(|a| a * 2); // b is now [2, 4, 6]
+```
+
+### fold
+
+Applies a function to each element of the array, returning the final accumulated value. The first
+parameter is the initial value.
+
+```rust
+fn fold(self, mut accumulator: U, f: fn(U, T) -> U) -> U
+```
+
+This is a left fold, so the given function will be applied to the accumulator and first element of
+the array, then the second, and so on. For a given call the expected result would be equivalent to:
+
+```rust
+let a1 = [1];
+let a2 = [1, 2];
+let a3 = [1, 2, 3];
+
+let f = |a, b| a - b;
+a1.fold(10, f) //=> f(10, 1)
+a2.fold(10, f) //=> f(f(10, 1), 2)
+a3.fold(10, f) //=> f(f(f(10, 1), 2), 3)
+```
+
+example:
+
+```rust
+
+fn main() {
+ let arr = [2, 2, 2, 2, 2];
+ let folded = arr.fold(0, |a, b| a + b);
+ assert(folded == 10);
+}
+
+```
+
+### reduce
+
+Same as fold, but uses the first element as starting element.
+
+```rust
+fn reduce(self, f: fn(T, T) -> T) -> T
+```
+
+example:
+
+```rust
+fn main() {
+ let arr = [2, 2, 2, 2, 2];
+ let reduced = arr.reduce(|a, b| a + b);
+ assert(reduced == 10);
+}
+```
+
+### all
+
+Returns true if all the elements satisfy the given predicate
+
+```rust
+fn all(self, predicate: fn(T) -> bool) -> bool
+```
+
+example:
+
+```rust
+fn main() {
+ let arr = [2, 2, 2, 2, 2];
+ let all = arr.all(|a| a == 2);
+ assert(all);
+}
+```
+
+### any
+
+Returns true if any of the elements satisfy the given predicate
+
+```rust
+fn any(self, predicate: fn(T) -> bool) -> bool
+```
+
+example:
+
+```rust
+fn main() {
+ let arr = [2, 2, 2, 2, 5];
+ let any = arr.any(|a| a == 5);
+ assert(any);
+}
+
+```
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/booleans.md
new file mode 100644
index 00000000000..69826fcd724
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/booleans.md
@@ -0,0 +1,31 @@
+---
+title: Booleans
+description:
+ Delve into the Boolean data type in Noir. Understand its methods, practical examples, and best practices for using Booleans in your Noir programs.
+keywords:
+ [
+ noir,
+ boolean type,
+ methods,
+ examples,
+ logical operations,
+ ]
+sidebar_position: 2
+---
+
+
+The `bool` type in Noir has two possible values: `true` and `false`:
+
+```rust
+fn main() {
+ let t = true;
+ let f: bool = false;
+}
+```
+
+> **Note:** When returning a boolean value, it will show up as a value of 1 for `true` and 0 for
+> `false` in _Verifier.toml_.
+
+The boolean type is most commonly used in conditionals like `if` expressions and `assert`
+statements. More about conditionals is covered in the [Control Flow](../control_flow) and
+[Assert Function](../assert) sections.
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/fields.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/fields.md
new file mode 100644
index 00000000000..99b4aa63549
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/fields.md
@@ -0,0 +1,192 @@
+---
+title: Fields
+description:
+ Dive deep into the Field data type in Noir. Understand its methods, practical examples, and best practices to effectively use Fields in your Noir programs.
+keywords:
+ [
+ noir,
+ field type,
+ methods,
+ examples,
+ best practices,
+ ]
+sidebar_position: 0
+---
+
+The field type corresponds to the native field type of the proving backend.
+
+The size of a Noir field depends on the elliptic curve's finite field for the proving backend
+adopted. For example, a field would be a 254-bit integer when paired with the default backend that
+spans the Grumpkin curve.
+
+Fields support integer arithmetic and are often used as the default numeric type in Noir:
+
+```rust
+fn main(x : Field, y : Field) {
+ let z = x + y;
+}
+```
+
+`x`, `y` and `z` are all private fields in this example. Using the `let` keyword we defined a new
+private value `z` constrained to be equal to `x + y`.
+
+If proving efficiency is of priority, fields should be used as a default for solving problems.
+Smaller integer types (e.g. `u64`) incur extra range constraints.
+
+## Methods
+
+After declaring a Field, you can use these common methods on it:
+
+### to_le_bits
+
+Transforms the field into an array of bits, Little Endian.
+
+```rust
+fn to_le_bits(_x : Field, _bit_size: u32) -> [u1; N]
+```
+
+example:
+
+```rust
+fn main() {
+ let field = 2;
+ let bits = field.to_le_bits(32);
+}
+```
+
+### to_be_bits
+
+Transforms the field into an array of bits, Big Endian.
+
+```rust
+fn to_be_bits(_x : Field, _bit_size: u32) -> [u1; N]
+```
+
+example:
+
+```rust
+fn main() {
+ let field = 2;
+ let bits = field.to_be_bits(32);
+}
+```
+
+### to_le_bytes
+
+Transforms into an array of bytes, Little Endian
+
+```rust
+fn to_le_bytes(_x : Field, byte_size: u32) -> [u8]
+```
+
+example:
+
+```rust
+fn main() {
+ let field = 2;
+ let bytes = field.to_le_bytes(4);
+}
+```
+
+### to_be_bytes
+
+Transforms into an array of bytes, Big Endian
+
+```rust
+fn to_be_bytes(_x : Field, byte_size: u32) -> [u8]
+```
+
+example:
+
+```rust
+fn main() {
+ let field = 2;
+ let bytes = field.to_be_bytes(4);
+}
+```
+
+### to_le_radix
+
+Decomposes into a vector over the specified base, Little Endian
+
+```rust
+fn to_le_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8]
+```
+
+example:
+
+```rust
+fn main() {
+ let field = 2;
+ let radix = field.to_le_radix(256, 4);
+}
+```
+
+### to_be_radix
+
+Decomposes into a vector over the specified base, Big Endian
+
+```rust
+fn to_be_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8]
+```
+
+example:
+
+```rust
+fn main() {
+ let field = 2;
+ let radix = field.to_be_radix(256, 4);
+}
+```
+
+### pow_32
+
+Returns the value to the power of the specified exponent
+
+```rust
+fn pow_32(self, exponent: Field) -> Field
+```
+
+example:
+
+```rust
+fn main() {
+ let field = 2
+ let pow = field.pow_32(4);
+ assert(pow == 16);
+}
+```
+
+### assert_max_bit_size
+
+Adds a constraint to specify that the field can be represented with `bit_size` number of bits
+
+```rust
+fn assert_max_bit_size(self, bit_size: u32)
+```
+
+example:
+
+```rust
+fn main() {
+ let field = 2
+ field.assert_max_bit_size(32);
+}
+```
+
+### sgn0
+
+Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x ∈ \{0, ..., p-1\} is even, otherwise sgn0(x mod p) = 1.
+
+```rust
+fn sgn0(self) -> u1
+```
+
+
+### lt
+
+Returns true if the field is less than the other field
+
+```rust
+pub fn lt(self, another: Field) -> bool
+```
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/function_types.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/function_types.md
new file mode 100644
index 00000000000..f6121af17e2
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/function_types.md
@@ -0,0 +1,26 @@
+---
+title: Function types
+sidebar_position: 10
+---
+
+Noir supports higher-order functions. The syntax for a function type is as follows:
+
+```rust
+fn(arg1_type, arg2_type, ...) -> return_type
+```
+
+Example:
+
+```rust
+fn assert_returns_100(f: fn() -> Field) { // f takes no args and returns a Field
+ assert(f() == 100);
+}
+
+fn main() {
+ assert_returns_100(|| 100); // ok
+ assert_returns_100(|| 150); // fails
+}
+```
+
+A function type also has an optional capture environment - this is necessary to support closures.
+See [Lambdas](../lambdas.md) for more details.
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/index.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/index.md
new file mode 100644
index 00000000000..97b3b2cb094
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/index.md
@@ -0,0 +1,110 @@
+---
+title: Data Types
+description:
+ Get a clear understanding of the two categories of Noir data types - primitive types and compound
+ types. Learn about their characteristics, differences, and how to use them in your Noir
+ programming.
+keywords:
+ [
+ noir,
+ data types,
+ primitive types,
+ compound types,
+ private types,
+ public types,
+ ]
+---
+
+Every value in Noir has a type, which determines which operations are valid for it.
+
+All values in Noir are fundamentally composed of `Field` elements. For a more approachable
+developing experience, abstractions are added on top to introduce different data types in Noir.
+
+Noir has two category of data types: primitive types (e.g. `Field`, integers, `bool`) and compound
+types that group primitive types (e.g. arrays, tuples, structs). Each value can either be private or
+public.
+
+## Private & Public Types
+
+A **private value** is known only to the Prover, while a **public value** is known by both the
+Prover and Verifier. Mark values as `private` when the value should only be known to the prover. All
+primitive types (including individual fields of compound types) in Noir are private by default, and
+can be marked public when certain values are intended to be revealed to the Verifier.
+
+> **Note:** For public values defined in Noir programs paired with smart contract verifiers, once
+> the proofs are verified on-chain the values can be considered known to everyone that has access to
+> that blockchain.
+
+Public data types are treated no differently to private types apart from the fact that their values
+will be revealed in proofs generated. Simply changing the value of a public type will not change the
+circuit (where the same goes for changing values of private types as well).
+
+_Private values_ are also referred to as _witnesses_ sometimes.
+
+> **Note:** The terms private and public when applied to a type (e.g. `pub Field`) have a different
+> meaning than when applied to a function (e.g. `pub fn foo() {}`).
+>
+> The former is a visibility modifier for the Prover to interpret if a value should be made known to
+> the Verifier, while the latter is a visibility modifier for the compiler to interpret if a
+> function should be made accessible to external Noir programs like in other languages.
+
+### pub Modifier
+
+All data types in Noir are private by default. Types are explicitly declared as public using the
+`pub` modifier:
+
+```rust
+fn main(x : Field, y : pub Field) -> pub Field {
+ x + y
+}
+```
+
+In this example, `x` is **private** while `y` and `x + y` (the return value) are **public**. Note
+that visibility is handled **per variable**, so it is perfectly valid to have one input that is
+private and another that is public.
+
+> **Note:** Public types can only be declared through parameters on `main`.
+
+## Type Aliases
+
+A type alias is a new name for an existing type. Type aliases are declared with the keyword `type`:
+
+```rust
+type Id = u8;
+
+fn main() {
+ let id: Id = 1;
+ let zero: u8 = 0;
+ assert(zero + 1 == id);
+}
+```
+
+Type aliases can also be used with [generics](@site/docs/noir/concepts/generics.md):
+
+```rust
+type Id = Size;
+
+fn main() {
+ let id: Id = 1;
+ let zero: u32 = 0;
+ assert(zero + 1 == id);
+}
+```
+
+Type aliases can even refer to other aliases. An error will be issued if they form a cycle:
+
+```rust
+// Ok!
+type A = B;
+type B = Field;
+
+type Bad1 = Bad2;
+
+// error: Dependency cycle found
+type Bad2 = Bad1;
+// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2
+```
+
+### BigInt
+
+You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library.
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/integers.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/integers.md
new file mode 100644
index 00000000000..4d58d96fed5
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/integers.md
@@ -0,0 +1,155 @@
+---
+title: Integers
+description: Explore the Integer data type in Noir. Learn about its methods, see real-world examples, and grasp how to efficiently use Integers in your Noir code.
+keywords: [noir, integer types, methods, examples, arithmetic]
+sidebar_position: 1
+---
+
+An integer type is a range constrained field type. The Noir frontend supports both unsigned and signed integer types. The allowed sizes are 1, 8, 32 and 64 bits.
+
+:::info
+
+When an integer is defined in Noir without a specific type, it will default to `Field`.
+
+The one exception is for loop indices which default to `u64` since comparisons on `Field`s are not possible.
+
+:::
+
+## Unsigned Integers
+
+An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`):
+
+```rust
+fn main() {
+ let x: u8 = 1;
+ let y: u8 = 1;
+ let z = x + y;
+ assert (z == 2);
+}
+```
+
+The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$).
+
+## Signed Integers
+
+A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`):
+
+```rust
+fn main() {
+ let x: i8 = -1;
+ let y: i8 = -1;
+ let z = x + y;
+ assert (z == -2);
+}
+```
+
+The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$).
+
+## 128 bits Unsigned Integers
+
+The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind:
+- You cannot cast between a native integer and `U128`
+- There is a higher performance cost when using `U128`, compared to a native type.
+
+Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions.
+
+```rust
+fn main() {
+ let x = U128::from_integer(23);
+ let y = U128::from_hex("0x7");
+ let z = x + y;
+ assert(z.to_integer() == 30);
+}
+```
+
+`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication.
+You can construct a U128 from its limbs:
+```rust
+fn main(x: u64, y: u64) {
+ let x = U128::from_u64s_be(x,y);
+ assert(z.hi == x as Field);
+ assert(z.lo == y as Field);
+}
+```
+
+Note that the limbs are stored as Field elements in order to avoid unnecessary conversions.
+Apart from this, most operations will work as usual:
+
+```rust
+fn main(x: U128, y: U128) {
+ // multiplication
+ let c = x * y;
+ // addition and subtraction
+ let c = c - x + y;
+ // division
+ let c = x / y;
+ // bit operation;
+ let c = x & y | y;
+ // bit shift
+ let c = x << y;
+ // comparisons;
+ let c = x < y;
+ let c = x == y;
+}
+```
+
+## Overflows
+
+Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove:
+
+```rust
+fn main(x: u8, y: u8) {
+ let z = x + y;
+}
+```
+
+With:
+
+```toml
+x = "255"
+y = "1"
+```
+
+Would result in:
+
+```
+$ nargo prove
+error: Assertion failed: 'attempt to add with overflow'
+┌─ ~/src/main.nr:9:13
+│
+│ let z = x + y;
+│ -----
+│
+= Call stack:
+ ...
+```
+
+A similar error would happen with signed integers:
+
+```rust
+fn main() {
+ let x: i8 = -118;
+ let y: i8 = -11;
+ let z = x + y;
+}
+```
+
+### Wrapping methods
+
+Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations:
+
+```rust
+fn wrapping_add(x: T, y: T) -> T;
+fn wrapping_sub(x: T, y: T) -> T;
+fn wrapping_mul(x: T, y: T) -> T;
+```
+
+Example of how it is used:
+
+```rust
+use dep::std;
+
+fn main(x: u8, y: u8) -> pub u8 {
+ std::wrapping_add(x, y)
+}
+```
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/references.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/references.md
new file mode 100644
index 00000000000..a5293d11cfb
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/references.md
@@ -0,0 +1,23 @@
+---
+title: References
+sidebar_position: 9
+---
+
+Noir supports first-class references. References are a bit like pointers: they point to a specific address that can be followed to access the data stored at that address. You can use Rust-like syntax to use pointers in Noir: the `&` operator references the variable, the `*` operator dereferences it.
+
+Example:
+
+```rust
+fn main() {
+ let mut x = 2;
+
+ // you can reference x as &mut and pass it to multiplyBy2
+ multiplyBy2(&mut x);
+}
+
+// you can access &mut here
+fn multiplyBy2(x: &mut Field) {
+ // and dereference it with *
+ *x = *x * 2;
+}
+```
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/slices.mdx b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/slices.mdx
new file mode 100644
index 00000000000..4a6ee816aa2
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/slices.mdx
@@ -0,0 +1,147 @@
+---
+title: Slices
+description: Explore the Slice data type in Noir. Understand its methods, see real-world examples, and learn how to effectively use Slices in your Noir programs.
+keywords: [noir, slice type, methods, examples, subarrays]
+sidebar_position: 5
+---
+
+import Experimental from '@site/src/components/Notes/_experimental.mdx';
+
+
+
+A slice is a dynamically-sized view into a sequence of elements. They can be resized at runtime, but because they don't own the data, they cannot be returned from a circuit. You can treat slices as arrays without a constrained size.
+
+```rust
+use dep::std::slice;
+
+fn main() -> pub Field {
+ let mut slice: [Field] = [0; 2];
+
+ let mut new_slice = slice.push_back(6);
+ new_slice.len()
+}
+```
+
+View the corresponding test file [here][test-file].
+
+[test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr
+
+## Methods
+
+For convenience, the STD provides some ready-to-use, common methods for slices:
+
+### push_back
+
+Pushes a new element to the end of the slice, returning a new slice with a length one greater than the original unmodified slice.
+
+```rust
+fn push_back(_self: [T], _elem: T) -> [T]
+```
+
+example:
+
+```rust
+fn main() -> pub Field {
+ let mut slice: [Field] = [0; 2];
+
+ let mut new_slice = slice.push_back(6);
+ new_slice.len()
+}
+```
+
+View the corresponding test file [here][test-file].
+
+### push_front
+
+Returns a new array with the specified element inserted at index 0. The existing elements indexes are incremented by 1.
+
+```rust
+fn push_front(_self: Self, _elem: T) -> Self
+```
+
+Example:
+
+```rust
+let mut new_slice: [Field] = [];
+new_slice = new_slice.push_front(20);
+assert(new_slice[0] == 20); // returns true
+```
+
+View the corresponding test file [here][test-file].
+
+### pop_front
+
+Returns a tuple of two items, the first element of the array and the rest of the array.
+
+```rust
+fn pop_front(_self: Self) -> (T, Self)
+```
+
+Example:
+
+```rust
+let (first_elem, rest_of_slice) = slice.pop_front();
+```
+
+View the corresponding test file [here][test-file].
+
+### pop_back
+
+Returns a tuple of two items, the beginning of the array with the last element omitted and the last element.
+
+```rust
+fn pop_back(_self: Self) -> (Self, T)
+```
+
+Example:
+
+```rust
+let (popped_slice, last_elem) = slice.pop_back();
+```
+
+View the corresponding test file [here][test-file].
+
+### append
+
+Loops over a slice and adds it to the end of another.
+
+```rust
+fn append(mut self, other: Self) -> Self
+```
+
+Example:
+
+```rust
+let append = [1, 2].append([3, 4, 5]);
+```
+
+### insert
+
+Inserts an element at a specified index and shifts all following elements by 1.
+
+```rust
+fn insert(_self: Self, _index: Field, _elem: T) -> Self
+```
+
+Example:
+
+```rust
+new_slice = rest_of_slice.insert(2, 100);
+assert(new_slice[2] == 100);
+```
+
+View the corresponding test file [here][test-file].
+
+### remove
+
+Remove an element at a specified index, shifting all elements after it to the left, returning the altered slice and the removed element.
+
+```rust
+fn remove(_self: Self, _index: Field) -> (Self, T)
+```
+
+Example:
+
+```rust
+let (remove_slice, removed_elem) = slice.remove(3);
+```
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/strings.md
new file mode 100644
index 00000000000..311dfd64416
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/strings.md
@@ -0,0 +1,80 @@
+---
+title: Strings
+description:
+ Discover the String data type in Noir. Learn about its methods, see real-world examples, and understand how to effectively manipulate and use Strings in Noir.
+keywords:
+ [
+ noir,
+ string type,
+ methods,
+ examples,
+ concatenation,
+ ]
+sidebar_position: 3
+---
+
+
+The string type is a fixed length value defined with `str`.
+
+You can use strings in `assert()` functions or print them with
+`println()`. See more about [Logging](../../standard_library/logging).
+
+```rust
+use dep::std;
+
+fn main(message : pub str<11>, hex_as_string : str<4>) {
+ println(message);
+ assert(message == "hello world");
+ assert(hex_as_string == "0x41");
+}
+```
+
+You can convert a `str` to a byte array by calling `as_bytes()`
+or a vector by calling `as_bytes_vec()`.
+
+```rust
+fn main() {
+ let message = "hello world";
+ let message_bytes = message.as_bytes();
+ let mut message_vec = message.as_bytes_vec();
+ assert(message_bytes.len() == 11);
+ assert(message_bytes[0] == 104);
+ assert(message_bytes[0] == message_vec.get(0));
+}
+```
+
+## Escape characters
+
+You can use escape characters for your strings:
+
+| Escape Sequence | Description |
+|-----------------|-----------------|
+| `\r` | Carriage Return |
+| `\n` | Newline |
+| `\t` | Tab |
+| `\0` | Null Character |
+| `\"` | Double Quote |
+| `\\` | Backslash |
+
+Example:
+
+```rust
+let s = "Hello \"world" // prints "Hello "world"
+let s = "hey \tyou"; // prints "hey you"
+```
+
+## Raw strings
+
+A raw string begins with the letter `r` and is optionally delimited by a number of hashes `#`.
+
+Escape characters are *not* processed within raw strings. All contents are interpreted literally.
+
+Example:
+
+```rust
+let s = r"Hello world";
+let s = r#"Simon says "hello world""#;
+
+// Any number of hashes may be used (>= 1) as long as the string also terminates with the same number of hashes
+let s = r#####"One "#, Two "##, Three "###, Four "####, Five will end the string."#####;
+```
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/structs.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/structs.md
new file mode 100644
index 00000000000..dbf68c99813
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/structs.md
@@ -0,0 +1,70 @@
+---
+title: Structs
+description:
+ Explore the Struct data type in Noir. Learn about its methods, see real-world examples, and grasp how to effectively define and use Structs in your Noir programs.
+keywords:
+ [
+ noir,
+ struct type,
+ methods,
+ examples,
+ data structures,
+ ]
+sidebar_position: 8
+---
+
+A struct also allows for grouping multiple values of different types. Unlike tuples, we can also
+name each field.
+
+> **Note:** The usage of _field_ here refers to each element of the struct and is unrelated to the
+> field type of Noir.
+
+Defining a struct requires giving it a name and listing each field within as `: ` pairs:
+
+```rust
+struct Animal {
+ hands: Field,
+ legs: Field,
+ eyes: u8,
+}
+```
+
+An instance of a struct can then be created with actual values in `: ` pairs in any
+order. Struct fields are accessible using their given names:
+
+```rust
+fn main() {
+ let legs = 4;
+
+ let dog = Animal {
+ eyes: 2,
+ hands: 0,
+ legs,
+ };
+
+ let zero = dog.hands;
+}
+```
+
+Structs can also be destructured in a pattern, binding each field to a new variable:
+
+```rust
+fn main() {
+ let Animal { hands, legs: feet, eyes } = get_octopus();
+
+ let ten = hands + feet + eyes as u8;
+}
+
+fn get_octopus() -> Animal {
+ let octopus = Animal {
+ hands: 0,
+ legs: 8,
+ eyes: 2,
+ };
+
+ octopus
+}
+```
+
+The new variables can be bound with names different from the original struct field names, as
+showcased in the `legs --> feet` binding in the example above.
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/tuples.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/tuples.md
new file mode 100644
index 00000000000..2ec5c9c4113
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/tuples.md
@@ -0,0 +1,48 @@
+---
+title: Tuples
+description:
+ Dive into the Tuple data type in Noir. Understand its methods, practical examples, and best practices for efficiently using Tuples in your Noir code.
+keywords:
+ [
+ noir,
+ tuple type,
+ methods,
+ examples,
+ multi-value containers,
+ ]
+sidebar_position: 7
+---
+
+A tuple collects multiple values like an array, but with the added ability to collect values of
+different types:
+
+```rust
+fn main() {
+ let tup: (u8, u64, Field) = (255, 500, 1000);
+}
+```
+
+One way to access tuple elements is via destructuring using pattern matching:
+
+```rust
+fn main() {
+ let tup = (1, 2);
+
+ let (one, two) = tup;
+
+ let three = one + two;
+}
+```
+
+Another way to access tuple elements is via direct member access, using a period (`.`) followed by
+the index of the element we want to access. Index `0` corresponds to the first tuple element, `1` to
+the second and so on:
+
+```rust
+fn main() {
+ let tup = (5, 6, 7, 8);
+
+ let five = tup.0;
+ let eight = tup.3;
+}
+```
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/distinct.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/distinct.md
new file mode 100644
index 00000000000..6c993b8b5e0
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/distinct.md
@@ -0,0 +1,64 @@
+---
+title: Distinct Witnesses
+sidebar_position: 11
+---
+
+The `distinct` keyword prevents repetitions of witness indices in the program's ABI. This ensures
+that the witnesses being returned as public inputs are all unique.
+
+The `distinct` keyword is only used for return values on program entry points (usually the `main()`
+function).
+
+When using `distinct` and `pub` simultaneously, `distinct` comes first. See the example below.
+
+You can read more about the problem this solves
+[here](https://github.com/noir-lang/noir/issues/1183).
+
+## Example
+
+Without the `distinct` keyword, the following program
+
+```rust
+fn main(x : pub Field, y : pub Field) -> pub [Field; 4] {
+ let a = 1;
+ let b = 1;
+ [x + 1, y, a, b]
+}
+```
+
+compiles to
+
+```json
+{
+ //...
+ "abi": {
+ //...
+ "param_witnesses": { "x": [1], "y": [2] },
+ "return_witnesses": [3, 2, 4, 4]
+ }
+}
+```
+
+Whereas (with the `distinct` keyword)
+
+```rust
+fn main(x : pub Field, y : pub Field) -> distinct pub [Field; 4] {
+ let a = 1;
+ let b = 1;
+ [x + 1, y, a, b]
+}
+```
+
+compiles to
+
+```json
+{
+ //...
+ "abi": {
+ //...
+ "param_witnesses": { "x": [1], "y": [2] },
+ //...
+ "return_witnesses": [3, 4, 5, 6]
+ }
+}
+```
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/functions.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/functions.md
new file mode 100644
index 00000000000..48aba9cd058
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/functions.md
@@ -0,0 +1,226 @@
+---
+title: Functions
+description:
+ Learn how to declare functions and methods in Noir, a programming language with Rust semantics.
+ This guide covers parameter declaration, return types, call expressions, and more.
+keywords: [Noir, Rust, functions, methods, parameter declaration, return types, call expressions]
+sidebar_position: 1
+---
+
+Functions in Noir follow the same semantics of Rust, though Noir does not support early returns.
+
+To declare a function the `fn` keyword is used.
+
+```rust
+fn foo() {}
+```
+
+By default, functions are visible only within the package they are defined. To make them visible outside of that package (for example, as part of a [library](../modules_packages_crates/crates_and_packages.md#libraries)), you should mark them as `pub`:
+
+```rust
+pub fn foo() {}
+```
+
+You can also restrict the visibility of the function to only the crate it was defined in, by specifying `pub(crate)`:
+
+```rust
+pub(crate) fn foo() {} //foo can only be called within its crate
+```
+
+All parameters in a function must have a type and all types are known at compile time. The parameter
+is pre-pended with a colon and the parameter type. Multiple parameters are separated using a comma.
+
+```rust
+fn foo(x : Field, y : Field){}
+```
+
+The return type of a function can be stated by using the `->` arrow notation. The function below
+states that the foo function must return a `Field`. If the function returns no value, then the arrow
+is omitted.
+
+```rust
+fn foo(x : Field, y : Field) -> Field {
+ x + y
+}
+```
+
+Note that a `return` keyword is unneeded in this case - the last expression in a function's body is
+returned.
+
+## Main function
+
+If you're writing a binary, the `main` function is the starting point of your program. You can pass all types of expressions to it, as long as they have a fixed size at compile time:
+
+```rust
+fn main(x : Field) // this is fine: passing a Field
+fn main(x : [Field; 2]) // this is also fine: passing a Field with known size at compile-time
+fn main(x : (Field, bool)) // 👌: passing a (Field, bool) tuple means size 2
+fn main(x : str<5>) // this is fine, as long as you pass a string of size 5
+
+fn main(x : Vec) // can't compile, has variable size
+fn main(x : [Field]) // can't compile, has variable size
+fn main(....// i think you got it by now
+```
+
+Keep in mind [tests](../../getting_started/tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove:
+
+```rust
+fn main(x : [Field]) {
+ assert(x[0] == 1);
+}
+
+#[test]
+fn test_one() {
+ main([1, 2]);
+}
+```
+
+```bash
+$ nargo test
+[testing] Running 1 test functions
+[testing] Testing test_one... ok
+[testing] All tests passed
+
+$ nargo check
+The application panicked (crashed).
+Message: Cannot have variable sized arrays as a parameter to main
+```
+
+## Call Expressions
+
+Calling a function in Noir is executed by using the function name and passing in the necessary
+arguments.
+
+Below we show how to call the `foo` function from the `main` function using a call expression:
+
+```rust
+fn main(x : Field, y : Field) {
+ let z = foo(x);
+}
+
+fn foo(x : Field) -> Field {
+ x + x
+}
+```
+
+## Methods
+
+You can define methods in Noir on any struct type in scope.
+
+```rust
+struct MyStruct {
+ foo: Field,
+ bar: Field,
+}
+
+impl MyStruct {
+ fn new(foo: Field) -> MyStruct {
+ MyStruct {
+ foo,
+ bar: 2,
+ }
+ }
+
+ fn sum(self) -> Field {
+ self.foo + self.bar
+ }
+}
+
+fn main() {
+ let s = MyStruct::new(40);
+ assert(s.sum() == 42);
+}
+```
+
+Methods are just syntactic sugar for functions, so if we wanted to we could also call `sum` as
+follows:
+
+```rust
+assert(MyStruct::sum(s) == 42);
+```
+
+It is also possible to specialize which method is chosen depending on the [generic](./generics.md) type that is used. In this example, the `foo` function returns different values depending on its type:
+
+```rust
+struct Foo {}
+
+impl Foo {
+ fn foo(self) -> Field { 1 }
+}
+
+impl Foo {
+ fn foo(self) -> Field { 2 }
+}
+
+fn main() {
+ let f1: Foo = Foo{};
+ let f2: Foo = Foo{};
+ assert(f1.foo() + f2.foo() == 3);
+}
+```
+
+Also note that impls with the same method name defined in them cannot overlap. For example, if we already have `foo` defined for `Foo` and `Foo` like we do above, we cannot also define `foo` in an `impl Foo` since it would be ambiguous which version of `foo` to choose.
+
+```rust
+// Including this impl in the same project as the above snippet would
+// cause an overlapping impls error
+impl Foo {
+ fn foo(self) -> Field { 3 }
+}
+```
+
+## Lambdas
+
+Lambdas are anonymous functions. They follow the syntax of Rust - `|arg1, arg2, ..., argN| return_expression`.
+
+```rust
+let add_50 = |val| val + 50;
+assert(add_50(100) == 150);
+```
+
+See [Lambdas](./lambdas.md) for more details.
+
+## Attributes
+
+Attributes are metadata that can be applied to a function, using the following syntax: `#[attribute(value)]`.
+
+Supported attributes include:
+
+- **builtin**: the function is implemented by the compiler, for efficiency purposes.
+- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function`
+- **field**: Used to enable conditional compilation of code depending on the field size. See below for more details
+- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details.
+- **test**: mark the function as unit tests. See [Tests](../../getting_started/tooling/testing.md) for more details
+
+### Field Attribute
+
+The field attribute defines which field the function is compatible for. The function is conditionally compiled, under the condition that the field attribute matches the Noir native field.
+The field can be defined implicitly, by using the name of the elliptic curve usually associated to it - for instance bn254, bls12_381 - or explicitly by using the field (prime) order, in decimal or hexadecimal form.
+As a result, it is possible to define multiple versions of a function with each version specialized for a different field attribute. This can be useful when a function requires different parameters depending on the underlying elliptic curve.
+
+Example: we define the function `foo()` three times below. Once for the default Noir bn254 curve, once for the field $\mathbb F_{23}$, which will normally never be used by Noir, and once again for the bls12_381 curve.
+
+```rust
+#[field(bn254)]
+fn foo() -> u32 {
+ 1
+}
+
+#[field(23)]
+fn foo() -> u32 {
+ 2
+}
+
+// This commented code would not compile as foo would be defined twice because it is the same field as bn254
+// #[field(21888242871839275222246405745257275088548364400416034343698204186575808495617)]
+// fn foo() -> u32 {
+// 2
+// }
+
+#[field(bls12_381)]
+fn foo() -> u32 {
+ 3
+}
+```
+
+If the field name is not known to Noir, it will discard the function. Field names are case insensitive.
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/generics.md
new file mode 100644
index 00000000000..ddd42bf1f9b
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/generics.md
@@ -0,0 +1,106 @@
+---
+title: Generics
+description: Learn how to use Generics in Noir
+keywords: [Noir, Rust, generics, functions, structs]
+sidebar_position: 7
+---
+
+Generics allow you to use the same functions with multiple different concrete data types. You can
+read more about the concept of generics in the Rust documentation
+[here](https://doc.rust-lang.org/book/ch10-01-syntax.html).
+
+Here is a trivial example showing the identity function that supports any type. In Rust, it is
+common to refer to the most general type as `T`. We follow the same convention in Noir.
+
+```rust
+fn id(x: T) -> T {
+ x
+}
+```
+
+## In Structs
+
+Generics are useful for specifying types in structs. For example, we can specify that a field in a
+struct will be of a certain generic type. In this case `value` is of type `T`.
+
+```rust
+struct RepeatedValue {
+ value: T,
+ count: Field,
+}
+
+impl RepeatedValue {
+ fn print(self) {
+ for _i in 0 .. self.count {
+ println(self.value);
+ }
+ }
+}
+
+fn main() {
+ let repeated = RepeatedValue { value: "Hello!", count: 2 };
+ repeated.print();
+}
+```
+
+The `print` function will print `Hello!` an arbitrary number of times, twice in this case.
+
+If we want to be generic over array lengths (which are type-level integers), we can use numeric
+generics. Using these looks just like using regular generics, but these generics can resolve to
+integers at compile-time, rather than resolving to types. Here's an example of a struct that is
+generic over the size of the array it contains internally:
+
+```rust
+struct BigInt {
+ limbs: [u32; N],
+}
+
+impl BigInt {
+ // `N` is in scope of all methods in the impl
+ fn first(first: BigInt, second: BigInt) -> Self {
+ assert(first.limbs != second.limbs);
+ first
+
+ fn second(first: BigInt, second: Self) -> Self {
+ assert(first.limbs != second.limbs);
+ second
+ }
+}
+```
+
+## Calling functions on generic parameters
+
+Since a generic type `T` can represent any type, how can we call functions on the underlying type?
+In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?"
+
+This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over
+any type `T` that implements the `Eq` trait for equality:
+
+```rust
+fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool
+ where T: Eq
+{
+ if (array1.len() == 0) | (array2.len() == 0) {
+ true
+ } else {
+ array1[0] == array2[0]
+ }
+}
+
+fn main() {
+ assert(first_element_is_equal([1, 2, 3], [1, 5, 6]));
+
+ // We can use first_element_is_equal for arrays of any type
+ // as long as we have an Eq impl for the types we pass in
+ let array = [MyStruct::new(), MyStruct::new()];
+ assert(array_eq(array, array, MyStruct::eq));
+}
+
+impl Eq for MyStruct {
+ fn eq(self, other: MyStruct) -> bool {
+ self.foo == other.foo
+ }
+}
+```
+
+You can find more details on traits and trait implementations on the [traits page](../concepts/traits).
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/globals.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/globals.md
new file mode 100644
index 00000000000..063a3d89248
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/globals.md
@@ -0,0 +1,72 @@
+---
+title: Global Variables
+description:
+ Learn about global variables in Noir. Discover how
+ to declare, modify, and use them in your programs.
+keywords: [noir programming language, globals, global variables, constants]
+sidebar_position: 8
+---
+
+## Globals
+
+
+Noir supports global variables. The global's type can be inferred by the compiler entirely:
+
+```rust
+global N = 5; // Same as `global N: Field = 5`
+
+global TUPLE = (3, 2);
+
+fn main() {
+ assert(N == 5);
+ assert(N == TUPLE.0 + TUPLE.1);
+}
+```
+
+:::info
+
+Globals can be defined as any expression, so long as they don't depend on themselves - otherwise there would be a dependency cycle! For example:
+
+```rust
+global T = foo(T); // dependency error
+```
+
+:::
+
+
+If they are initialized to a literal integer, globals can be used to specify an array's length:
+
+```rust
+global N: Field = 2;
+
+fn main(y : [Field; N]) {
+ assert(y[0] == y[1])
+}
+```
+
+A global from another module can be imported or referenced externally like any other name:
+
+```rust
+global N = 20;
+
+fn main() {
+ assert(my_submodule::N != N);
+}
+
+mod my_submodule {
+ global N: Field = 10;
+}
+```
+
+When a global is used, Noir replaces the name with its definition on each occurrence.
+This means globals defined using function calls will repeat the call each time they're used:
+
+```rust
+global RESULT = foo();
+
+fn foo() -> [Field; 100] { ... }
+```
+
+This is usually fine since Noir will generally optimize any function call that does not
+refer to a program input into a constant. It should be kept in mind however, if the called
+function performs side-effects like `println`, as these will still occur on each use.
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/lambdas.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/lambdas.md
new file mode 100644
index 00000000000..be3c7e0b5ca
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/lambdas.md
@@ -0,0 +1,81 @@
+---
+title: Lambdas
+description: Learn how to use anonymous functions in Noir programming language.
+keywords: [Noir programming language, lambda, closure, function, anonymous function]
+sidebar_position: 9
+---
+
+## Introduction
+
+Lambdas are anonymous functions. The syntax is `|arg1, arg2, ..., argN| return_expression`.
+
+```rust
+let add_50 = |val| val + 50;
+assert(add_50(100) == 150);
+```
+
+A block can be used as the body of a lambda, allowing you to declare local variables inside it:
+
+```rust
+let cool = || {
+ let x = 100;
+ let y = 100;
+ x + y
+}
+
+assert(cool() == 200);
+```
+
+## Closures
+
+Inside the body of a lambda, you can use variables defined in the enclosing function. Such lambdas are called **closures**. In this example `x` is defined inside `main` and is accessed from within the lambda:
+
+```rust
+fn main() {
+ let x = 100;
+ let closure = || x + 150;
+ assert(closure() == 250);
+}
+```
+
+## Passing closures to higher-order functions
+
+It may catch you by surprise that the following code fails to compile:
+
+```rust
+fn foo(f: fn () -> Field) -> Field {
+ f()
+}
+
+fn main() {
+ let (x, y) = (50, 50);
+ assert(foo(|| x + y) == 100); // error :(
+}
+```
+
+The reason is that the closure's capture environment affects its type - we have a closure that captures two Fields and `foo`
+expects a regular function as an argument - those are incompatible.
+:::note
+
+Variables contained within the `||` are the closure's parameters, and the expression that follows it is the closure's body. The capture environment is comprised of any variables used in the closure's body that are not parameters.
+
+E.g. in |x| x + y, y would be a captured variable, but x would not be, since it is a parameter of the closure.
+
+:::
+The syntax for the type of a closure is `fn[env](args) -> ret_type`, where `env` is the capture environment of the closure -
+in this example that's `(Field, Field)`.
+
+The best solution in our case is to make `foo` generic over the environment type of its parameter, so that it can be called
+with closures with any environment, as well as with regular functions:
+
+```rust
+fn foo(f: fn[Env]() -> Field) -> Field {
+ f()
+}
+
+fn main() {
+ let (x, y) = (50, 50);
+ assert(foo(|| x + y) == 100); // compiles fine
+ assert(foo(|| 60) == 60); // compiles fine
+}
+```
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/mutability.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/mutability.md
new file mode 100644
index 00000000000..fdeef6a87c5
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/mutability.md
@@ -0,0 +1,121 @@
+---
+title: Mutability
+description:
+ Learn about mutable variables in Noir. Discover how
+ to declare, modify, and use them in your programs.
+keywords: [noir programming language, mutability in noir, mutable variables]
+sidebar_position: 8
+---
+
+Variables in noir can be declared mutable via the `mut` keyword. Mutable variables can be reassigned
+to via an assignment expression.
+
+```rust
+let x = 2;
+x = 3; // error: x must be mutable to be assigned to
+
+let mut y = 3;
+let y = 4; // OK
+```
+
+The `mut` modifier can also apply to patterns:
+
+```rust
+let (a, mut b) = (1, 2);
+a = 11; // error: a must be mutable to be assigned to
+b = 12; // OK
+
+let mut (c, d) = (3, 4);
+c = 13; // OK
+d = 14; // OK
+
+// etc.
+let MyStruct { x: mut y } = MyStruct { x: a };
+// y is now in scope
+```
+
+Note that mutability in noir is local and everything is passed by value, so if a called function
+mutates its parameters then the parent function will keep the old value of the parameters.
+
+```rust
+fn main() -> pub Field {
+ let x = 3;
+ helper(x);
+ x // x is still 3
+}
+
+fn helper(mut x: i32) {
+ x = 4;
+}
+```
+
+## Non-local mutability
+
+Non-local mutability can be achieved through the mutable reference type `&mut T`:
+
+```rust
+fn set_to_zero(x: &mut Field) {
+ *x = 0;
+}
+
+fn main() {
+ let mut y = 42;
+ set_to_zero(&mut y);
+ assert(*y == 0);
+}
+```
+
+When creating a mutable reference, the original variable being referred to (`y` in this
+example) must also be mutable. Since mutable references are a reference type, they must
+be explicitly dereferenced via `*` to retrieve the underlying value. Note that this yields
+a copy of the value, so mutating this copy will not change the original value behind the
+reference:
+
+```rust
+fn main() {
+ let mut x = 1;
+ let x_ref = &mut x;
+
+ let mut y = *x_ref;
+ let y_ref = &mut y;
+
+ x = 2;
+ *x_ref = 3;
+
+ y = 4;
+ *y_ref = 5;
+
+ assert(x == 3);
+ assert(*x_ref == 3);
+ assert(y == 5);
+ assert(*y_ref == 5);
+}
+```
+
+Note that types in Noir are actually deeply immutable so the copy that occurs when
+dereferencing is only a conceptual copy - no additional constraints will occur.
+
+Mutable references can also be stored within structs. Note that there is also
+no lifetime parameter on these unlike rust. This is because the allocated memory
+always lasts the entire program - as if it were an array of one element.
+
+```rust
+struct Foo {
+ x: &mut Field
+}
+
+impl Foo {
+ fn incr(mut self) {
+ *self.x += 1;
+ }
+}
+
+fn main() {
+ let foo = Foo { x: &mut 0 };
+ foo.incr();
+ assert(*foo.x == 1);
+}
+```
+
+In general, you should avoid non-local & shared mutability unless it is needed. Sticking
+to only local mutability will improve readability and potentially improve compiler optimizations as well.
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/ops.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/ops.md
new file mode 100644
index 00000000000..60425cb8994
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/ops.md
@@ -0,0 +1,98 @@
+---
+title: Logical Operations
+description:
+ Learn about the supported arithmetic and logical operations in the Noir programming language.
+ Discover how to perform operations on private input types, integers, and booleans.
+keywords:
+ [
+ Noir programming language,
+ supported operations,
+ arithmetic operations,
+ logical operations,
+ predicate operators,
+ bitwise operations,
+ short-circuiting,
+ backend,
+ ]
+sidebar_position: 3
+---
+
+# Operations
+
+## Table of Supported Operations
+
+| Operation | Description | Requirements |
+| :-------- | :------------------------------------------------------------: | -------------------------------------: |
+| + | Adds two private input types together | Types must be private input |
+| - | Subtracts two private input types together | Types must be private input |
+| \* | Multiplies two private input types together | Types must be private input |
+| / | Divides two private input types together | Types must be private input |
+| ^ | XOR two private input types together | Types must be integer |
+| & | AND two private input types together | Types must be integer |
+| \| | OR two private input types together | Types must be integer |
+| \<\< | Left shift an integer by another integer amount | Types must be integer |
+| >> | Right shift an integer by another integer amount | Types must be integer |
+| ! | Bitwise not of a value | Type must be integer or boolean |
+| \< | returns a bool if one value is less than the other | Upper bound must have a known bit size |
+| \<= | returns a bool if one value is less than or equal to the other | Upper bound must have a known bit size |
+| > | returns a bool if one value is more than the other | Upper bound must have a known bit size |
+| >= | returns a bool if one value is more than or equal to the other | Upper bound must have a known bit size |
+| == | returns a bool if one value is equal to the other | Both types must not be constants |
+| != | returns a bool if one value is not equal to the other | Both types must not be constants |
+
+### Predicate Operators
+
+`<,<=, !=, == , >, >=` are known as predicate/comparison operations because they compare two values.
+This differs from the operations such as `+` where the operands are used in _computation_.
+
+### Bitwise Operations Example
+
+```rust
+fn main(x : Field) {
+ let y = x as u32;
+ let z = y & y;
+}
+```
+
+`z` is implicitly constrained to be the result of `y & y`. The `&` operand is used to denote bitwise
+`&`.
+
+> `x & x` would not compile as `x` is a `Field` and not an integer type.
+
+### Logical Operators
+
+Noir has no support for the logical operators `||` and `&&`. This is because encoding the
+short-circuiting that these operators require can be inefficient for Noir's backend. Instead you can
+use the bitwise operators `|` and `&` which operate identically for booleans, just without the
+short-circuiting.
+
+```rust
+let my_val = 5;
+
+let mut flag = 1;
+if (my_val > 6) | (my_val == 0) {
+ flag = 0;
+}
+assert(flag == 1);
+
+if (my_val != 10) & (my_val < 50) {
+ flag = 0;
+}
+assert(flag == 0);
+```
+
+### Shorthand operators
+
+Noir shorthand operators for most of the above operators, namely `+=, -=, *=, /=, %=, &=, |=, ^=, <<=`, and `>>=`. These allow for more concise syntax. For example:
+
+```rust
+let mut i = 0;
+i = i + 1;
+```
+
+could be written as:
+
+```rust
+let mut i = 0;
+i += 1;
+```
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/oracles.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/oracles.md
new file mode 100644
index 00000000000..2e6a6818d48
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/oracles.md
@@ -0,0 +1,23 @@
+---
+title: Oracles
+description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide.
+keywords:
+ - Noir
+ - Oracles
+ - RPC Calls
+ - Unconstrained Functions
+ - Programming
+ - Blockchain
+sidebar_position: 6
+---
+
+Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation.
+
+Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md)
+
+You can declare an Oracle through the `#[oracle()]` flag. Example:
+
+```rust
+#[oracle(get_number_sequence)]
+unconstrained fn get_number_sequence(_size: Field) -> [Field] {}
+```
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/shadowing.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/shadowing.md
new file mode 100644
index 00000000000..5ce6130d201
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/shadowing.md
@@ -0,0 +1,44 @@
+---
+title: Shadowing
+sidebar_position: 12
+---
+
+Noir allows for inheriting variables' values and re-declaring them with the same name similar to Rust, known as shadowing.
+
+For example, the following function is valid in Noir:
+
+```rust
+fn main() {
+ let x = 5;
+
+ {
+ let x = x * 2;
+ assert (x == 10);
+ }
+
+ assert (x == 5);
+}
+```
+
+In this example, a variable x is first defined with the value 5.
+
+The local scope that follows shadows the original x, i.e. creates a local mutable x based on the value of the original x. It is given a value of 2 times the original x.
+
+When we return to the main scope, x once again refers to just the original x, which stays at the value of 5.
+
+## Temporal mutability
+
+One way that shadowing is useful, in addition to ergonomics across scopes, is for temporarily mutating variables.
+
+```rust
+fn main() {
+ let age = 30;
+ // age = age + 5; // Would error as `age` is immutable by default.
+
+ let mut age = age + 5; // Temporarily mutates `age` with a new value.
+
+ let age = age; // Locks `age`'s mutability again.
+
+ assert (age == 35);
+}
+```
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/traits.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/traits.md
new file mode 100644
index 00000000000..ef1445a5907
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/traits.md
@@ -0,0 +1,389 @@
+---
+title: Traits
+description:
+ Traits in Noir can be used to abstract out a common interface for functions across
+ several data types.
+keywords: [noir programming language, traits, interfaces, generic, protocol]
+sidebar_position: 14
+---
+
+## Overview
+
+Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines
+the interface of several methods contained within the trait. Types can then implement this trait by providing
+implementations for these methods. For example in the program:
+
+```rust
+struct Rectangle {
+ width: Field,
+ height: Field,
+}
+
+impl Rectangle {
+ fn area(self) -> Field {
+ self.width * self.height
+ }
+}
+
+fn log_area(r: Rectangle) {
+ println(r.area());
+}
+```
+
+We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this
+function to work on `Triangle`s as well?:
+
+```rust
+struct Triangle {
+ width: Field,
+ height: Field,
+}
+
+impl Triangle {
+ fn area(self) -> Field {
+ self.width * self.height / 2
+ }
+}
+```
+
+Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can
+introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`:
+
+```rust
+trait Area {
+ fn area(self) -> Field;
+}
+
+fn log_area(shape: T) where T: Area {
+ println(shape.area());
+}
+```
+
+We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing
+impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined
+by the `Area` trait.
+
+```rust
+impl Area for Rectangle {
+ fn area(self) -> Field {
+ self.width * self.height
+ }
+}
+
+impl Area for Triangle {
+ fn area(self) -> Field {
+ self.width * self.height / 2
+ }
+}
+```
+
+Now we have a working program that is generic over any type of Shape that is used! Others can even use this program
+as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types.
+
+## Where Clauses
+
+As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements
+a trait, we can add a where clause to the generic function.
+
+```rust
+fn log_area(shape: T) where T: Area {
+ println(shape.area());
+}
+```
+
+It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+`
+operator. Similarly, we can have multiple trait constraints by separating each with a comma:
+
+```rust
+fn foo(elements: [T], thing: U) where
+ T: Default + Add + Eq,
+ U: Bar,
+{
+ let mut sum = T::default();
+
+ for element in elements {
+ sum += element;
+ }
+
+ if sum == T::default() {
+ thing.bar();
+ }
+}
+```
+
+## Generic Implementations
+
+You can add generics to a trait implementation by adding the generic list after the `impl` keyword:
+
+```rust
+trait Second {
+ fn second(self) -> Field;
+}
+
+impl Second for (T, Field) {
+ fn second(self) -> Field {
+ self.1
+ }
+}
+```
+
+You can also implement a trait for every type this way:
+
+```rust
+trait Debug {
+ fn debug(self);
+}
+
+impl Debug for T {
+ fn debug(self) {
+ println(self);
+ }
+}
+
+fn main() {
+ 1.debug();
+}
+```
+
+### Generic Trait Implementations With Where Clauses
+
+Where clauses can also be placed on trait implementations themselves to restrict generics in a similar way.
+For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar`
+will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types.
+For example, here is the implementation for array equality:
+
+```rust
+impl Eq for [T; N] where T: Eq {
+ // Test if two arrays have the same elements.
+ // Because both arrays must have length N, we know their lengths already match.
+ fn eq(self, other: Self) -> bool {
+ let mut result = true;
+
+ for i in 0 .. self.len() {
+ // The T: Eq constraint is needed to call == on the array elements here
+ result &= self[i] == other[i];
+ }
+
+ result
+ }
+}
+```
+
+## Generic Traits
+
+Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in
+scope of every item within the trait.
+
+```rust
+trait Into {
+ // Convert `self` to type `T`
+ fn into(self) -> T;
+}
+```
+
+When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime
+when referencing a generic trait (e.g. in a `where` clause).
+
+```rust
+struct MyStruct {
+ array: [Field; 2],
+}
+
+impl Into<[Field; 2]> for MyStruct {
+ fn into(self) -> [Field; 2] {
+ self.array
+ }
+}
+
+fn as_array(x: T) -> [Field; 2]
+ where T: Into<[Field; 2]>
+{
+ x.into()
+}
+
+fn main() {
+ let array = [1, 2];
+ let my_struct = MyStruct { array };
+
+ assert_eq(as_array(my_struct), array);
+}
+```
+
+## Trait Methods With No `self`
+
+A trait can contain any number of methods, each of which have access to the `Self` type which represents each type
+that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used.
+For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type
+but doesn't need to take any parameters:
+
+```rust
+trait Default {
+ fn default() -> Self;
+}
+```
+
+Implementing this trait can be done similarly to any other trait:
+
+```rust
+impl Default for Field {
+ fn default() -> Field {
+ 0
+ }
+}
+
+struct MyType {}
+
+impl Default for MyType {
+ fn default() -> Field {
+ MyType {}
+ }
+}
+```
+
+However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`.
+Instead, we'll need to refer to the function directly. This can be done either by referring to the
+specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later
+case, type inference determines the impl that is selected.
+
+```rust
+let my_struct = MyStruct::default();
+
+let x: Field = Default::default();
+let result = x + Default::default();
+```
+
+:::warning
+
+```rust
+let _ = Default::default();
+```
+
+If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be
+arbitrarily selected. This occurs most often when the result of a trait function call with no parameters
+is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type,
+always refer to it via the implementation type's namespace - e.g. `MyType::default()`.
+This is set to change to an error in future Noir versions.
+
+:::
+
+## Default Method Implementations
+
+A trait can also have default implementations of its methods by giving a body to the desired functions.
+Note that this body must be valid for all types that may implement the trait. As a result, the only
+valid operations on `self` will be operations valid for any type or other operations on the trait itself.
+
+```rust
+trait Numeric {
+ fn add(self, other: Self) -> Self;
+
+ // Default implementation of double is (self + self)
+ fn double(self) -> Self {
+ self.add(self)
+ }
+}
+```
+
+When implementing a trait with default functions, a type may choose to implement only the required functions:
+
+```rust
+impl Numeric for Field {
+ fn add(self, other: Field) -> Field {
+ self + other
+ }
+}
+```
+
+Or it may implement the optional methods as well:
+
+```rust
+impl Numeric for u32 {
+ fn add(self, other: u32) -> u32 {
+ self + other
+ }
+
+ fn double(self) -> u32 {
+ self * 2
+ }
+}
+```
+
+## Impl Specialization
+
+When implementing traits for a generic type it is possible to implement the trait for only a certain combination
+of generics. This can be either as an optimization or because those specific generics are required to implement the trait.
+
+```rust
+trait Sub {
+ fn sub(self, other: Self) -> Self;
+}
+
+struct NonZero {
+ value: T,
+}
+
+impl Sub for NonZero {
+ fn sub(self, other: Self) -> Self {
+ let value = self.value - other.value;
+ assert(value != 0);
+ NonZero { value }
+ }
+}
+```
+
+## Overlapping Implementations
+
+Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous.
+This means if a trait `Foo` is already implemented
+by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other
+type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create
+any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given
+method call.
+
+```rust
+trait Trait {}
+
+// Previous impl defined here
+impl Trait for (A, B) {}
+
+// error: Impl for type `(Field, Field)` overlaps with existing impl
+impl Trait for (Field, Field) {}
+```
+
+## Trait Coherence
+
+Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create
+impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same
+program.
+
+The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared
+in the crate the impl is in.
+
+In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does
+not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate.
+While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its
+own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the
+library your choices are to either submit a patch to the library or use the newtype pattern.
+
+### The Newtype Pattern
+
+The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type
+that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create
+impls for any trait we need on it.
+
+```rust
+struct Wrapper {
+ foo: dep::some_library::Foo,
+}
+
+impl Default for Wrapper {
+ fn default() -> Wrapper {
+ Wrapper {
+ foo: dep::some_library::Foo::new(),
+ }
+ }
+}
+```
+
+Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated
+to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and
+unwrapping of values when converting to and from the `Wrapper` and `Foo` types.
diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/unconstrained.md
new file mode 100644
index 00000000000..89d12c1c971
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/unconstrained.md
@@ -0,0 +1,95 @@
+---
+title: Unconstrained Functions
+description: "Learn about what unconstrained functions in Noir are, how to use them and when you'd want to."
+
+keywords: [Noir programming language, unconstrained, open]
+sidebar_position: 5
+---
+
+Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic computation.
+
+## Why?
+
+Zero-knowledge (ZK) domain-specific languages (DSL) enable developers to generate ZK proofs from their programs by compiling code down to the constraints of an NP complete language (such as R1CS or PLONKish languages). However, the hard bounds of a constraint system can be very limiting to the functionality of a ZK DSL.
+
+Enabling a circuit language to perform unconstrained execution is a powerful tool. Said another way, unconstrained execution lets developers generate witnesses from code that does not generate any constraints. Being able to execute logic outside of a circuit is critical for both circuit performance and constructing proofs on information that is external to a circuit.
+
+Fetching information from somewhere external to a circuit can also be used to enable developers to improve circuit efficiency.
+
+A ZK DSL does not just prove computation, but proves that some computation was handled correctly. Thus, it is necessary that when we switch from performing some operation directly inside of a circuit to inside of an unconstrained environment that the appropriate constraints are still laid down elsewhere in the circuit.
+
+## Example
+
+An in depth example might help drive the point home. This example comes from the excellent [post](https://discord.com/channels/1113924620781883405/1124022445054111926/1128747641853972590) by Tom in the Noir Discord.
+
+Let's look at how we can optimize a function to turn a `u72` into an array of `u8`s.
+
+```rust
+fn main(num: u72) -> pub [u8; 8] {
+ let mut out: [u8; 8] = [0; 8];
+ for i in 0..8 {
+ out[i] = (num >> (56 - (i * 8)) as u72 & 0xff) as u8;
+ }
+
+ out
+}
+```
+
+```
+Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 91
+Backend circuit size: 3619
+```
+
+A lot of the operations in this function are optimized away by the compiler (all the bit-shifts turn into divisions by constants). However we can save a bunch of gates by casting to u8 a bit earlier. This automatically truncates the bit-shifted value to fit in a u8 which allows us to remove the AND against 0xff. This saves us ~480 gates in total.
+
+```rust
+fn main(num: u72) -> pub [u8; 8] {
+ let mut out: [u8; 8] = [0; 8];
+ for i in 0..8 {
+ out[i] = (num >> (56 - (i * 8)) as u8;
+ }
+
+ out
+}
+```
+
+```
+Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 75
+Backend circuit size: 3143
+```
+
+Those are some nice savings already but we can do better. This code is all constrained so we're proving every step of calculating out using num, but we don't actually care about how we calculate this, just that it's correct. This is where brillig comes in.
+
+It turns out that truncating a u72 into a u8 is hard to do inside a snark, each time we do as u8 we lay down 4 ACIR opcodes which get converted into multiple gates. It's actually much easier to calculate num from out than the other way around. All we need to do is multiply each element of out by a constant and add them all together, both relatively easy operations inside a snark.
+
+We can then run u72_to_u8 as unconstrained brillig code in order to calculate out, then use that result in our constrained function and assert that if we were to do the reverse calculation we'd get back num. This looks a little like the below:
+
+```rust
+fn main(num: u72) -> pub [u8; 8] {
+ let out = u72_to_u8(num);
+
+ let mut reconstructed_num: u72 = 0;
+ for i in 0..8 {
+ reconstructed_num += (out[i] as u72 << (56 - (8 * i)));
+ }
+ assert(num == reconstructed_num);
+ out
+}
+
+unconstrained fn u72_to_u8(num: u72) -> [u8; 8] {
+ let mut out: [u8; 8] = [0; 8];
+ for i in 0..8 {
+ out[i] = (num >> (56 - (i * 8))) as u8;
+ }
+ out
+}
+```
+
+```
+Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 78
+Backend circuit size: 2902
+```
+
+This ends up taking off another ~250 gates from our circuit! We've ended up with more ACIR opcodes than before but they're easier for the backend to prove (resulting in fewer gates).
+
+Generally we want to use brillig whenever there's something that's easy to verify but hard to compute within the circuit. For example, if you wanted to calculate a square root of a number it'll be a much better idea to calculate this in brillig and then assert that if you square the result you get back your number.
diff --git a/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/_category_.json b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/_category_.json
new file mode 100644
index 00000000000..1debcfe7675
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/_category_.json
@@ -0,0 +1,6 @@
+{
+ "label": "Modules, Packages and Crates",
+ "position": 2,
+ "collapsible": true,
+ "collapsed": true
+}
diff --git a/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/crates_and_packages.md
new file mode 100644
index 00000000000..95ee9f52ab2
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/crates_and_packages.md
@@ -0,0 +1,43 @@
+---
+title: Crates and Packages
+description: Learn how to use Crates and Packages in your Noir project
+keywords: [Nargo, dependencies, package management, crates, package]
+sidebar_position: 0
+---
+
+## Crates
+
+A crate is the smallest amount of code that the Noir compiler considers at a time.
+Crates can contain modules, and the modules may be defined in other files that get compiled with the crate, as we’ll see in the coming sections.
+
+### Crate Types
+
+A Noir crate can come in several forms: binaries, libraries or contracts.
+
+#### Binaries
+
+_Binary crates_ are programs which you can compile to an ACIR circuit which you can then create proofs against. Each must have a function called `main` that defines the ACIR circuit which is to be proved.
+
+#### Libraries
+
+_Library crates_ don't have a `main` function and they don't compile down to ACIR. Instead they define functionality intended to be shared with multiple projects, and eventually included in a binary crate.
+
+#### Contracts
+
+Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts).
+
+### Crate Root
+
+Every crate has a root, which is the source file that the compiler starts, this is also known as the root module. The Noir compiler does not enforce any conditions on the name of the file which is the crate root, however if you are compiling via Nargo the crate root must be called `lib.nr` or `main.nr` for library or binary crates respectively.
+
+## Packages
+
+A Nargo _package_ is a collection of one of more crates that provides a set of functionality. A package must include a Nargo.toml file.
+
+A package _must_ contain either a library or a binary crate, but not both.
+
+### Differences from Cargo Packages
+
+One notable difference between Rust's Cargo and Noir's Nargo is that while Cargo allows a package to contain an unlimited number of binary crates and a single library crate, Nargo currently only allows a package to contain a single crate.
+
+In future this restriction may be lifted to allow a Nargo package to contain both a binary and library crate or multiple binary crates.
diff --git a/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/dependencies.md
new file mode 100644
index 00000000000..04c1703d929
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/dependencies.md
@@ -0,0 +1,124 @@
+---
+title: Dependencies
+description:
+ Learn how to specify and manage dependencies in Nargo, allowing you to upload packages to GitHub
+ and use them easily in your project.
+keywords: [Nargo, dependencies, GitHub, package management, versioning]
+sidebar_position: 1
+---
+
+Nargo allows you to upload packages to GitHub and use them as dependencies.
+
+## Specifying a dependency
+
+Specifying a dependency requires a tag to a specific commit and the git url to the url containing
+the package.
+
+Currently, there are no requirements on the tag contents. If requirements are added, it would follow
+semver 2.0 guidelines.
+
+> Note: Without a `tag` , there would be no versioning and dependencies would change each time you
+> compile your project.
+
+For example, to add the [ecrecover-noir library](https://github.com/colinnielsen/ecrecover-noir) to your project, add it to `Nargo.toml`:
+
+```toml
+# Nargo.toml
+
+[dependencies]
+ecrecover = {tag = "v0.8.0", git = "https://github.com/colinnielsen/ecrecover-noir"}
+```
+
+If the module is in a subdirectory, you can define a subdirectory in your git repository, for example:
+
+```toml
+# Nargo.toml
+
+[dependencies]
+easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "noir-contracts/contracts/easy_private_token_contract"}
+```
+
+## Specifying a local dependency
+
+You can also specify dependencies that are local to your machine.
+
+For example, this file structure has a library and binary crate
+
+```tree
+├── binary_crate
+│ ├── Nargo.toml
+│ └── src
+│ └── main.nr
+└── lib_a
+ ├── Nargo.toml
+ └── src
+ └── lib.nr
+```
+
+Inside of the binary crate, you can specify:
+
+```toml
+# Nargo.toml
+
+[dependencies]
+lib_a = { path = "../lib_a" }
+```
+
+## Importing dependencies
+
+You can import a dependency to a Noir file using the following syntax. For example, to import the
+ecrecover-noir library and local lib_a referenced above:
+
+```rust
+use dep::ecrecover;
+use dep::lib_a;
+```
+
+You can also import only the specific parts of dependency that you want to use, like so:
+
+```rust
+use dep::std::hash::sha256;
+use dep::std::scalar_mul::fixed_base_embedded_curve;
+```
+
+Lastly, as demonstrated in the
+[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you
+can import multiple items in the same line by enclosing them in curly braces:
+
+```rust
+use dep::std::ec::tecurve::affine::{Curve, Point};
+```
+
+We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now.
+
+Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml.
+
+## Dependencies of Dependencies
+
+Note that when you import a dependency, you also get access to all of the dependencies of that package.
+
+For example, the [phy_vector](https://github.com/resurgencelabs/phy_vector) library imports an [fraction](https://github.com/resurgencelabs/fraction) library. If you're importing the phy_vector library, then you can access the functions in fractions library like so:
+
+```rust
+use dep::phy_vector;
+
+fn main(x : Field, y : pub Field) {
+ //...
+ let f = phy_vector::fraction::toFraction(true, 2, 1);
+ //...
+}
+```
+
+## Available Libraries
+
+Noir does not currently have an official package manager. You can find a list of available Noir libraries in the [awesome-noir repo here](https://github.com/noir-lang/awesome-noir#libraries).
+
+Some libraries that are available today include:
+
+- [Standard Library](https://github.com/noir-lang/noir/tree/master/noir_stdlib) - the Noir Standard Library
+- [Ethereum Storage Proof Verification](https://github.com/aragonzkresearch/noir-trie-proofs) - a library that contains the primitives necessary for RLP decoding (in the form of look-up table construction) and Ethereum state and storage proof verification (or verification of any trie proof involving 32-byte long keys)
+- [BigInt](https://github.com/shuklaayush/noir-bigint) - a library that provides a custom BigUint56 data type, allowing for computations on large unsigned integers
+- [ECrecover](https://github.com/colinnielsen/ecrecover-noir/tree/main) - a library to verify an ECDSA signature and return the source Ethereum address
+- [Sparse Merkle Tree Verifier](https://github.com/vocdoni/smtverifier-noir/tree/main) - a library for verification of sparse Merkle trees
+- [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir
+- [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers
diff --git a/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/modules.md
new file mode 100644
index 00000000000..ae822a1cff4
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/modules.md
@@ -0,0 +1,105 @@
+---
+title: Modules
+description:
+ Learn how to organize your files using modules in Noir, following the same convention as Rust's
+ module system. Examples included.
+keywords: [Noir, Rust, modules, organizing files, sub-modules]
+sidebar_position: 2
+---
+
+Noir's module system follows the same convention as the _newer_ version of Rust's module system.
+
+## Purpose of Modules
+
+Modules are used to organize files. Without modules all of your code would need to live in a single
+file. In Noir, the compiler does not automatically scan all of your files to detect modules. This
+must be done explicitly by the developer.
+
+## Examples
+
+### Importing a module in the crate root
+
+Filename : `src/main.nr`
+
+```rust
+mod foo;
+
+fn main() {
+ foo::hello_world();
+}
+```
+
+Filename : `src/foo.nr`
+
+```rust
+fn from_foo() {}
+```
+
+In the above snippet, the crate root is the `src/main.nr` file. The compiler sees the module
+declaration `mod foo` which prompts it to look for a foo.nr file.
+
+Visually this module hierarchy looks like the following :
+
+```
+crate
+ ├── main
+ │
+ └── foo
+ └── from_foo
+
+```
+
+### Importing a module throughout the tree
+
+All modules are accessible from the `crate::` namespace.
+
+```
+crate
+ ├── bar
+ ├── foo
+ └── main
+
+```
+
+In the above snippet, if `bar` would like to use functions in `foo`, it can do so by `use crate::foo::function_name`.
+
+### Sub-modules
+
+Filename : `src/main.nr`
+
+```rust
+mod foo;
+
+fn main() {
+ foo::from_foo();
+}
+```
+
+Filename : `src/foo.nr`
+
+```rust
+mod bar;
+fn from_foo() {}
+```
+
+Filename : `src/foo/bar.nr`
+
+```rust
+fn from_bar() {}
+```
+
+In the above snippet, we have added an extra module to the module tree; `bar`. `bar` is a submodule
+of `foo` hence we declare bar in `foo.nr` with `mod bar`. Since `foo` is not the crate root, the
+compiler looks for the file associated with the `bar` module in `src/foo/bar.nr`
+
+Visually the module hierarchy looks as follows:
+
+```
+crate
+ ├── main
+ │
+ └── foo
+ ├── from_foo
+ └── bar
+ └── from_bar
+```
diff --git a/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/workspaces.md
new file mode 100644
index 00000000000..67a1dafa372
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/workspaces.md
@@ -0,0 +1,40 @@
+---
+title: Workspaces
+sidebar_position: 3
+---
+
+Workspaces are a feature of nargo that allow you to manage multiple related Noir packages in a single repository. A workspace is essentially a group of related projects that share common build output directories and configurations.
+
+Each Noir project (with it's own Nargo.toml file) can be thought of as a package. Each package is expected to contain exactly one "named circuit", being the "name" defined in Nargo.toml with the program logic defined in `./src/main.nr`.
+
+For a project with the following structure:
+
+```tree
+├── crates
+│ ├── a
+│ │ ├── Nargo.toml
+│ │ └── src
+│ │ └── main.nr
+│ └── b
+│ ├── Nargo.toml
+│ └── src
+│ └── main.nr
+├── Nargo.toml
+└── Prover.toml
+```
+
+You can define a workspace in Nargo.toml like so:
+
+```toml
+[workspace]
+members = ["crates/a", "crates/b"]
+default-member = "crates/a"
+```
+
+`members` indicates which packages are included in the workspace. As such, all member packages of a workspace will be processed when the `--workspace` flag is used with various commands or if a `default-member` is not specified.
+
+`default-member` indicates which package various commands process by default.
+
+Libraries can be defined in a workspace. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml.
+
+Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml.
diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/_category_.json b/docs/versioned_docs/version-v0.25.0/noir/standard_library/_category_.json
new file mode 100644
index 00000000000..af04c0933fd
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/_category_.json
@@ -0,0 +1,6 @@
+{
+ "label": "Standard Library",
+ "position": 1,
+ "collapsible": true,
+ "collapsed": true
+}
diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/black_box_fns.md
new file mode 100644
index 00000000000..eae8744abf0
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/black_box_fns.md
@@ -0,0 +1,31 @@
+---
+title: Black Box Functions
+description: Black box functions are functions in Noir that rely on backends implementing support for specialized constraints.
+keywords: [noir, black box functions]
+---
+
+Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. This makes certain zk-snark unfriendly computations cheaper than if they were implemented in Noir.
+
+The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not.
+
+## Function list
+
+Here is a list of the current black box functions:
+
+- [SHA256](./cryptographic_primitives/hashes#sha256)
+- [Schnorr signature verification](./cryptographic_primitives/schnorr)
+- [Blake2s](./cryptographic_primitives/hashes#blake2s)
+- [Blake3](./cryptographic_primitives/hashes#blake3)
+- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash)
+- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment)
+- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification)
+- [Fixed base scalar multiplication](./cryptographic_primitives/scalar)
+- AND
+- XOR
+- RANGE
+- [Keccak256](./cryptographic_primitives/hashes#keccak256)
+- [Recursive proof verification](./recursion)
+
+Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function.
+
+You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs).
diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/bn254.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/bn254.md
new file mode 100644
index 00000000000..3294f005dbb
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/bn254.md
@@ -0,0 +1,46 @@
+---
+title: Bn254 Field Library
+---
+
+Noir provides a module in standard library with some optimized functions for bn254 Fr in `std::field::bn254`.
+
+## decompose
+
+```rust
+fn decompose(x: Field) -> (Field, Field) {}
+```
+
+Decomposes a single field into two fields, low and high. The low field contains the lower 16 bytes of the input field and the high field contains the upper 16 bytes of the input field. Both field results are range checked to 128 bits.
+
+
+## assert_gt
+
+```rust
+fn assert_gt(a: Field, b: Field) {}
+```
+
+Asserts that a > b. This will generate less constraints than using `assert(gt(a, b))`.
+
+## assert_lt
+
+```rust
+fn assert_lt(a: Field, b: Field) {}
+```
+
+Asserts that a < b. This will generate less constraints than using `assert(lt(a, b))`.
+
+## gt
+
+```rust
+fn gt(a: Field, b: Field) -> bool {}
+```
+
+Returns true if a > b.
+
+## lt
+
+```rust
+fn lt(a: Field, b: Field) -> bool {}
+```
+
+Returns true if a < b.
\ No newline at end of file
diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/boundedvec.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/boundedvec.md
new file mode 100644
index 00000000000..ce4529f6e57
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/boundedvec.md
@@ -0,0 +1,326 @@
+---
+title: Bounded Vectors
+keywords: [noir, vector, bounded vector, slice]
+sidebar_position: 1
+---
+
+A `BoundedVec` is a growable storage similar to a `Vec` except that it
+is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented
+via slices and thus is not subject to the same restrictions slices are (notably, nested
+slices - and thus nested vectors as well - are disallowed).
+
+Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by
+pushing an additional element is also more efficient - the length only needs to be increased
+by one.
+
+For these reasons `BoundedVec` should generally be preferred over `Vec` when there
+is a reasonable maximum bound that can be placed on the vector.
+
+Example:
+
+```rust
+let mut vector: BoundedVec = BoundedVec::new();
+for i in 0..5 {
+ vector.push(i);
+}
+assert(vector.len() == 5);
+assert(vector.max_len() == 10);
+```
+
+## Methods
+
+### new
+
+```rust
+pub fn new() -> Self
+```
+
+Creates a new, empty vector of length zero.
+
+Since this container is backed by an array internally, it still needs an initial value
+to give each element. To resolve this, each element is zeroed internally. This value
+is guaranteed to be inaccessible unless `get_unchecked` is used.
+
+Example:
+
+```rust
+let empty_vector: BoundedVec = BoundedVec::new();
+assert(empty_vector.len() == 0);
+```
+
+Note that whenever calling `new` the maximum length of the vector should always be specified
+via a type signature:
+
+```rust title="new_example" showLineNumbers
+fn foo() -> BoundedVec {
+ // Ok! MaxLen is specified with a type annotation
+ let v1: BoundedVec = BoundedVec::new();
+ let v2 = BoundedVec::new();
+
+ // Ok! MaxLen is known from the type of foo's return value
+ v2
+}
+
+fn bad() {
+ let mut v3 = BoundedVec::new();
+
+ // Not Ok! We don't know if v3's MaxLen is at least 1, and the compiler often infers 0 by default.
+ v3.push(5);
+}
+```
+> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L11-L27
+
+
+This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions
+but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to.
+
+### get
+
+```rust
+pub fn get(mut self: Self, index: u64) -> T {
+```
+
+Retrieves an element from the vector at the given index, starting from zero.
+
+If the given index is equal to or greater than the length of the vector, this
+will issue a constraint failure.
+
+Example:
+
+```rust
+fn foo(v: BoundedVec) {
+ let first = v.get(0);
+ let last = v.get(v.len() - 1);
+ assert(first != last);
+}
+```
+
+### get_unchecked
+
+```rust
+pub fn get_unchecked(mut self: Self, index: u64) -> T {
+```
+
+Retrieves an element from the vector at the given index, starting from zero, without
+performing a bounds check.
+
+Since this function does not perform a bounds check on length before accessing the element,
+it is unsafe! Use at your own risk!
+
+Example:
+
+```rust title="get_unchecked_example" showLineNumbers
+fn sum_of_first_three(v: BoundedVec) -> u32 {
+ // Always ensure the length is larger than the largest
+ // index passed to get_unchecked
+ assert(v.len() > 2);
+ let first = v.get_unchecked(0);
+ let second = v.get_unchecked(1);
+ let third = v.get_unchecked(2);
+ first + second + third
+}
+```
+> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L54-L64
+
+
+
+### push
+
+```rust
+pub fn push(&mut self, elem: T) {
+```
+
+Pushes an element to the end of the vector. This increases the length
+of the vector by one.
+
+Panics if the new length of the vector will be greater than the max length.
+
+Example:
+
+```rust title="bounded-vec-push-example" showLineNumbers
+let mut v: BoundedVec = BoundedVec::new();
+
+ v.push(1);
+ v.push(2);
+
+ // Panics with failed assertion "push out of bounds"
+ v.push(3);
+```
+> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L68-L76
+
+
+### pop
+
+```rust
+pub fn pop(&mut self) -> T
+```
+
+Pops the element at the end of the vector. This will decrease the length
+of the vector by one.
+
+Panics if the vector is empty.
+
+Example:
+
+```rust title="bounded-vec-pop-example" showLineNumbers
+let mut v: BoundedVec = BoundedVec::new();
+ v.push(1);
+ v.push(2);
+
+ let two = v.pop();
+ let one = v.pop();
+
+ assert(two == 2);
+ assert(one == 1);
+ // error: cannot pop from an empty vector
+ // let _ = v.pop();
+```
+> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L81-L93
+
+
+### len
+
+```rust
+pub fn len(self) -> u64 {
+```
+
+Returns the current length of this vector
+
+Example:
+
+```rust title="bounded-vec-len-example" showLineNumbers
+let mut v: BoundedVec = BoundedVec::new();
+ assert(v.len() == 0);
+
+ v.push(100);
+ assert(v.len() == 1);
+
+ v.push(200);
+ v.push(300);
+ v.push(400);
+ assert(v.len() == 4);
+
+ let _ = v.pop();
+ let _ = v.pop();
+ assert(v.len() == 2);
+```
+> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L98-L113
+
+
+### max_len
+
+```rust
+pub fn max_len(_self: BoundedVec) -> u64 {
+```
+
+Returns the maximum length of this vector. This is always
+equal to the `MaxLen` parameter this vector was initialized with.
+
+Example:
+
+```rust title="bounded-vec-max-len-example" showLineNumbers
+let mut v: BoundedVec = BoundedVec::new();
+
+ assert(v.max_len() == 5);
+ v.push(10);
+ assert(v.max_len() == 5);
+```
+> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L118-L124
+
+
+### storage
+
+```rust
+pub fn storage(self) -> [T; MaxLen] {
+```
+
+Returns the internal array within this vector.
+Since arrays in Noir are immutable, mutating the returned storage array will not mutate
+the storage held internally by this vector.
+
+Note that uninitialized elements may be zeroed out!
+
+Example:
+
+```rust title="bounded-vec-storage-example" showLineNumbers
+let mut v: BoundedVec = BoundedVec::new();
+
+ assert(v.storage() == [0, 0, 0, 0, 0]);
+
+ v.push(57);
+ assert(v.storage() == [57, 0, 0, 0, 0]);
+```
+> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L129-L136
+
+
+### extend_from_array
+
+```rust
+pub fn extend_from_array(&mut self, array: [T; Len])
+```
+
+Pushes each element from the given array to this vector.
+
+Panics if pushing each element would cause the length of this vector
+to exceed the maximum length.
+
+Example:
+
+```rust title="bounded-vec-extend-from-array-example" showLineNumbers
+let mut vec: BoundedVec = BoundedVec::new();
+ vec.extend_from_array([2, 4]);
+
+ assert(vec.len == 2);
+ assert(vec.get(0) == 2);
+ assert(vec.get(1) == 4);
+```
+> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L141-L148
+
+
+### extend_from_bounded_vec
+
+```rust
+pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec)
+```
+
+Pushes each element from the other vector to this vector. The length of
+the other vector is left unchanged.
+
+Panics if pushing each element would cause the length of this vector
+to exceed the maximum length.
+
+Example:
+
+```rust title="bounded-vec-extend-from-bounded-vec-example" showLineNumbers
+let mut v1: BoundedVec = BoundedVec::new();
+ let mut v2: BoundedVec = BoundedVec::new();
+
+ v2.extend_from_array([1, 2, 3]);
+ v1.extend_from_bounded_vec(v2);
+
+ assert(v1.storage() == [1, 2, 3, 0, 0]);
+ assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]);
+```
+> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L153-L162
+
+
+### any
+
+```rust
+pub fn any(self, predicate: fn[Env](T) -> bool) -> bool
+```
+
+Returns true if the given predicate returns true for any element
+in this vector.
+
+Example:
+
+```rust title="bounded-vec-any-example" showLineNumbers
+let mut v: BoundedVec = BoundedVec::new();
+ v.extend_from_array([2, 4, 6]);
+
+ let all_even = !v.any(|elem: u32| elem % 2 != 0);
+ assert(all_even);
+```
+> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L229-L235
+
diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/hashmap.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/hashmap.md
new file mode 100644
index 00000000000..91604af765d
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/hashmap.md
@@ -0,0 +1,569 @@
+---
+title: HashMap
+keywords: [noir, map, hash, hashmap]
+sidebar_position: 1
+---
+
+`HashMap` is used to efficiently store and look up key-value pairs.
+
+`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements.
+Note that due to hash collisions, the actual maximum number of elements stored by any particular
+hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since
+every hash value will be performed modulo `MaxLen`.
+
+When creating `HashMap`s, the `MaxLen` generic should always be specified if it is not already
+known. Otherwise, the compiler may infer a different value for `MaxLen` (such as zero), which
+will likely change the result of the program. This behavior is set to become an error in future
+versions instead.
+
+Example:
+
+```rust
+// Create a mapping from Fields to u32s with a maximum length of 12
+// using a pedersen hash
+let mut map: HashMap> = HashMap::default();
+
+map.insert(1, 2);
+map.insert(3, 4);
+
+let two = map.get(1).unwrap();
+```
+
+## Methods
+
+### default
+
+```rust title="default" showLineNumbers
+impl Default for HashMap
+where
+ B: BuildHasher + Default,
+ H: Hasher + Default
+{
+ fn default() -> Self {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L462-L469
+
+
+Creates a fresh, empty HashMap.
+
+When using this function, always make sure to specify the maximum size of the hash map.
+
+This is the same `default` from the `Default` implementation given further below. It is
+repeated here for convenience since it is the recommended way to create a hashmap.
+
+Example:
+
+```rust title="default_example" showLineNumbers
+let hashmap: HashMap> = HashMap::default();
+ assert(hashmap.is_empty());
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L202-L205
+
+
+Because `HashMap` has so many generic arguments that are likely to be the same throughout
+your program, it may be helpful to create a type alias:
+
+```rust title="type_alias" showLineNumbers
+type MyMap = HashMap>;
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L196-L198
+
+
+### with_hasher
+
+```rust title="with_hasher" showLineNumbers
+pub fn with_hasher(_build_hasher: B) -> Self
+ where
+ B: BuildHasher {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L82-L86
+
+
+Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple
+hashmaps are created with the same hasher instance.
+
+Example:
+
+```rust title="with_hasher_example" showLineNumbers
+let my_hasher: BuildHasherDefault = Default::default();
+ let hashmap: HashMap> = HashMap::with_hasher(my_hasher);
+ assert(hashmap.is_empty());
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L207-L211
+
+
+### get
+
+```rust title="get" showLineNumbers
+pub fn get(
+ self,
+ key: K
+ ) -> Option
+ where
+ K: Eq + Hash,
+ B: BuildHasher,
+ H: Hasher {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L278-L287
+
+
+Retrieves a value from the hashmap, returning `Option::none()` if it was not found.
+
+Example:
+
+```rust title="get_example" showLineNumbers
+fn get_example(map: HashMap>) {
+ let x = map.get(12);
+
+ if x.is_some() {
+ assert(x.unwrap() == 42);
+ }
+}
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L299-L307
+
+
+### insert
+
+```rust title="insert" showLineNumbers
+pub fn insert(
+ &mut self,
+ key: K,
+ value: V
+ )
+ where
+ K: Eq + Hash,
+ B: BuildHasher,
+ H: Hasher {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L313-L323
+
+
+Inserts a new key-value pair into the map. If the key was already in the map, its
+previous value will be overridden with the newly provided one.
+
+Example:
+
+```rust title="insert_example" showLineNumbers
+let mut map: HashMap> = HashMap::default();
+ map.insert(12, 42);
+ assert(map.len() == 1);
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L213-L217
+
+
+### remove
+
+```rust title="remove" showLineNumbers
+pub fn remove(
+ &mut self,
+ key: K
+ )
+ where
+ K: Eq + Hash,
+ B: BuildHasher,
+ H: Hasher {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L356-L365
+
+
+Removes the given key-value pair from the map. If the key was not already present
+in the map, this does nothing.
+
+Example:
+
+```rust title="remove_example" showLineNumbers
+map.remove(12);
+ assert(map.is_empty());
+
+ // If a key was not present in the map, remove does nothing
+ map.remove(12);
+ assert(map.is_empty());
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L221-L228
+
+
+### is_empty
+
+```rust title="is_empty" showLineNumbers
+pub fn is_empty(self) -> bool {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L115-L117
+
+
+True if the length of the hash map is empty.
+
+Example:
+
+```rust title="is_empty_example" showLineNumbers
+assert(map.is_empty());
+
+ map.insert(1, 2);
+ assert(!map.is_empty());
+
+ map.remove(1);
+ assert(map.is_empty());
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L230-L238
+
+
+### len
+
+```rust title="len" showLineNumbers
+pub fn len(self) -> u64 {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L264-L266
+
+
+Returns the current length of this hash map.
+
+Example:
+
+```rust title="len_example" showLineNumbers
+// This is equivalent to checking map.is_empty()
+ assert(map.len() == 0);
+
+ map.insert(1, 2);
+ map.insert(3, 4);
+ map.insert(5, 6);
+ assert(map.len() == 3);
+
+ // 3 was already present as a key in the hash map, so the length is unchanged
+ map.insert(3, 7);
+ assert(map.len() == 3);
+
+ map.remove(1);
+ assert(map.len() == 2);
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L240-L255
+
+
+### capacity
+
+```rust title="capacity" showLineNumbers
+pub fn capacity(_self: Self) -> u64 {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L271-L273
+
+
+Returns the maximum capacity of this hashmap. This is always equal to the capacity
+specified in the hashmap's type.
+
+Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a
+static capacity that does not increase as the map grows larger. Thus, this capacity
+is also the maximum possible element count that can be inserted into the hashmap.
+Due to hash collisions (modulo the hashmap length), it is likely the actual maximum
+element count will be lower than the full capacity.
+
+Example:
+
+```rust title="capacity_example" showLineNumbers
+let empty_map: HashMap> = HashMap::default();
+ assert(empty_map.len() == 0);
+ assert(empty_map.capacity() == 42);
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L257-L261
+
+
+### clear
+
+```rust title="clear" showLineNumbers
+pub fn clear(&mut self) {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L93-L95
+
+
+Clears the hashmap, removing all key-value pairs from it.
+
+Example:
+
+```rust title="clear_example" showLineNumbers
+assert(!map.is_empty());
+ map.clear();
+ assert(map.is_empty());
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L263-L267
+
+
+### contains_key
+
+```rust title="contains_key" showLineNumbers
+pub fn contains_key(
+ self,
+ key: K
+ ) -> bool
+ where
+ K: Hash + Eq,
+ B: BuildHasher,
+ H: Hasher {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L101-L110
+
+
+True if the hashmap contains the given key. Unlike `get`, this will not also return
+the value associated with the key.
+
+Example:
+
+```rust title="contains_key_example" showLineNumbers
+if map.contains_key(7) {
+ let value = map.get(7);
+ assert(value.is_some());
+ } else {
+ println("No value for key 7!");
+ }
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L269-L276
+
+
+### entries
+
+```rust title="entries" showLineNumbers
+pub fn entries(self) -> BoundedVec<(K, V), N> {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L123-L125
+
+
+Returns a vector of each key-value pair present in the hashmap.
+
+The length of the returned vector is always equal to the length of the hashmap.
+
+Example:
+
+```rust title="entries_example" showLineNumbers
+let entries = map.entries();
+
+ // The length of a hashmap may not be compile-time known, so we
+ // need to loop over its capacity instead
+ for i in 0..map.capacity() {
+ if i < entries.len() {
+ let (key, value) = entries.get(i);
+ println(f"{key} -> {value}");
+ }
+ }
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L310-L321
+
+
+### keys
+
+```rust title="keys" showLineNumbers
+pub fn keys(self) -> BoundedVec {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L144-L146
+
+
+Returns a vector of each key present in the hashmap.
+
+The length of the returned vector is always equal to the length of the hashmap.
+
+Example:
+
+```rust title="keys_example" showLineNumbers
+let keys = map.keys();
+
+ for i in 0..keys.max_len() {
+ if i < keys.len() {
+ let key = keys.get_unchecked(i);
+ let value = map.get(key).unwrap_unchecked();
+ println(f"{key} -> {value}");
+ }
+ }
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L323-L333
+
+
+### values
+
+```rust title="values" showLineNumbers
+pub fn values(self) -> BoundedVec {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L164-L166
+
+
+Returns a vector of each value present in the hashmap.
+
+The length of the returned vector is always equal to the length of the hashmap.
+
+Example:
+
+```rust title="values_example" showLineNumbers
+let values = map.values();
+
+ for i in 0..values.max_len() {
+ if i < values.len() {
+ let value = values.get_unchecked(i);
+ println(f"Found value {value}");
+ }
+ }
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L335-L344
+
+
+### iter_mut
+
+```rust title="iter_mut" showLineNumbers
+pub fn iter_mut(
+ &mut self,
+ f: fn(K, V) -> (K, V)
+ )
+ where
+ K: Eq + Hash,
+ B: BuildHasher,
+ H: Hasher {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L183-L192
+
+
+Iterates through each key-value pair of the HashMap, setting each key-value pair to the
+result returned from the given function.
+
+Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated
+through. If this is not desired, use `iter_values_mut` if only values need to be mutated,
+or `entries` if neither keys nor values need to be mutated.
+
+The iteration order is left unspecified. As a result, if two keys are mutated to become
+equal, which of the two values that will be present for the key in the resulting map is also unspecified.
+
+Example:
+
+```rust title="iter_mut_example" showLineNumbers
+// Add 1 to each key in the map, and double the value associated with that key.
+ map.iter_mut(|k, v| (k + 1, v * 2));
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L348-L351
+
+
+### iter_keys_mut
+
+```rust title="iter_keys_mut" showLineNumbers
+pub fn iter_keys_mut(
+ &mut self,
+ f: fn(K) -> K
+ )
+ where
+ K: Eq + Hash,
+ B: BuildHasher,
+ H: Hasher {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L208-L217
+
+
+Iterates through the HashMap, mutating each key to the result returned from
+the given function.
+
+Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated
+through. If only iteration is desired and the keys are not intended to be mutated,
+prefer using `entries` instead.
+
+The iteration order is left unspecified. As a result, if two keys are mutated to become
+equal, which of the two values that will be present for the key in the resulting map is also unspecified.
+
+Example:
+
+```rust title="iter_keys_mut_example" showLineNumbers
+// Double each key, leaving the value associated with that key untouched
+ map.iter_keys_mut(|k| k * 2);
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L353-L356
+
+
+### iter_values_mut
+
+```rust title="iter_values_mut" showLineNumbers
+pub fn iter_values_mut(&mut self, f: fn(V) -> V) {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L233-L235
+
+
+Iterates through the HashMap, applying the given function to each value and mutating the
+value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut`
+because the keys are untouched and the underlying hashmap thus does not need to be reordered.
+
+Example:
+
+```rust title="iter_values_mut_example" showLineNumbers
+// Halve each value
+ map.iter_values_mut(|v| v / 2);
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L358-L361
+
+
+### retain
+
+```rust title="retain" showLineNumbers
+pub fn retain(&mut self, f: fn(K, V) -> bool) {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L247-L249
+
+
+Retains only the key-value pairs for which the given function returns true.
+Any key-value pairs for which the function returns false will be removed from the map.
+
+Example:
+
+```rust title="retain_example" showLineNumbers
+map.retain(|k, v| (k != 0) & (v != 0));
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L281-L283
+
+
+## Trait Implementations
+
+### default
+
+```rust title="default" showLineNumbers
+impl Default for HashMap
+where
+ B: BuildHasher + Default,
+ H: Hasher + Default
+{
+ fn default() -> Self {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L462-L469
+
+
+Constructs an empty HashMap.
+
+Example:
+
+```rust title="default_example" showLineNumbers
+let hashmap: HashMap> = HashMap::default();
+ assert(hashmap.is_empty());
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L202-L205
+
+
+### eq
+
+```rust title="eq" showLineNumbers
+impl Eq for HashMap
+where
+ K: Eq + Hash,
+ V: Eq,
+ B: BuildHasher,
+ H: Hasher
+{
+ fn eq(self, other: HashMap) -> bool {
+```
+> Source code: noir_stdlib/src/collections/map.nr#L426-L435
+
+
+Checks if two HashMaps are equal.
+
+Example:
+
+```rust title="eq_example" showLineNumbers
+let mut map1: HashMap> = HashMap::default();
+ let mut map2: HashMap> = HashMap::default();
+
+ map1.insert(1, 2);
+ map1.insert(3, 4);
+
+ map2.insert(3, 4);
+ map2.insert(1, 2);
+
+ assert(map1 == map2);
+```
+> Source code: test_programs/execution_success/hashmap/src/main.nr#L285-L296
+
diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/index.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/index.md
new file mode 100644
index 00000000000..ea84c6d5c21
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/index.md
@@ -0,0 +1,5 @@
+---
+title: Containers
+description: Container types provided by Noir's standard library for storing and retrieving data
+keywords: [containers, data types, vec, hashmap]
+---
diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/vec.mdx b/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/vec.mdx
new file mode 100644
index 00000000000..1954f05bc76
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/vec.mdx
@@ -0,0 +1,151 @@
+---
+title: Vectors
+description: Delve into the Vec data type in Noir. Learn about its methods, practical examples, and best practices for using Vectors in your Noir code.
+keywords: [noir, vector type, methods, examples, dynamic arrays]
+sidebar_position: 6
+---
+
+import Experimental from '@site/src/components/Notes/_experimental.mdx';
+
+
+
+A vector is a collection type similar to Rust's `Vec` type. In Noir, it is a convenient way to use slices as mutable arrays.
+
+Example:
+
+```rust
+let mut vector: Vec = Vec::new();
+for i in 0..5 {
+ vector.push(i);
+}
+assert(vector.len() == 5);
+```
+
+## Methods
+
+### new
+
+Creates a new, empty vector.
+
+```rust
+pub fn new() -> Self
+```
+
+Example:
+
+```rust
+let empty_vector: Vec = Vec::new();
+assert(empty_vector.len() == 0);
+```
+
+### from_slice
+
+Creates a vector containing each element from a given slice. Mutations to the resulting vector will not affect the original slice.
+
+```rust
+pub fn from_slice(slice: [T]) -> Self
+```
+
+Example:
+
+```rust
+let arr: [Field] = [1, 2, 3];
+let vector_from_slice = Vec::from_slice(arr);
+assert(vector_from_slice.len() == 3);
+```
+
+### len
+
+Returns the number of elements in the vector.
+
+```rust
+pub fn len(self) -> Field
+```
+
+Example:
+
+```rust
+let empty_vector: Vec = Vec::new();
+assert(empty_vector.len() == 0);
+```
+
+### get
+
+Retrieves an element from the vector at a given index. Panics if the index points beyond the vector's end.
+
+```rust
+pub fn get(self, index: Field) -> T
+```
+
+Example:
+
+```rust
+let vector: Vec = Vec::from_slice([10, 20, 30]);
+assert(vector.get(1) == 20);
+```
+
+### push
+
+Adds a new element to the vector's end, returning a new vector with a length one greater than the original unmodified vector.
+
+```rust
+pub fn push(&mut self, elem: T)
+```
+
+Example:
+
+```rust
+let mut vector: Vec = Vec::new();
+vector.push(10);
+assert(vector.len() == 1);
+```
+
+### pop
+
+Removes an element from the vector's end, returning a new vector with a length one less than the original vector, along with the removed element. Panics if the vector's length is zero.
+
+```rust
+pub fn pop(&mut self) -> T
+```
+
+Example:
+
+```rust
+let mut vector = Vec::from_slice([10, 20]);
+let popped_elem = vector.pop();
+assert(popped_elem == 20);
+assert(vector.len() == 1);
+```
+
+### insert
+
+Inserts an element at a specified index, shifting subsequent elements to the right.
+
+```rust
+pub fn insert(&mut self, index: Field, elem: T)
+```
+
+Example:
+
+```rust
+let mut vector = Vec::from_slice([10, 30]);
+vector.insert(1, 20);
+assert(vector.get(1) == 20);
+```
+
+### remove
+
+Removes an element at a specified index, shifting subsequent elements to the left, and returns the removed element.
+
+```rust
+pub fn remove(&mut self, index: Field) -> T
+```
+
+Example:
+
+```rust
+let mut vector = Vec::from_slice([10, 20, 30]);
+let removed_elem = vector.remove(1);
+assert(removed_elem == 20);
+assert(vector.len() == 2);
+```
diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/_category_.json b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/_category_.json
new file mode 100644
index 00000000000..5d694210bbf
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/_category_.json
@@ -0,0 +1,5 @@
+{
+ "position": 0,
+ "collapsible": true,
+ "collapsed": true
+}
diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/ec_primitives.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/ec_primitives.md
new file mode 100644
index 00000000000..d2b42d67b7c
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/ec_primitives.md
@@ -0,0 +1,102 @@
+---
+title: Elliptic Curve Primitives
+keywords: [cryptographic primitives, Noir project]
+sidebar_position: 4
+---
+
+Data structures and methods on them that allow you to carry out computations involving elliptic
+curves over the (mathematical) field corresponding to `Field`. For the field currently at our
+disposal, applications would involve a curve embedded in BN254, e.g. the
+[Baby Jubjub curve](https://eips.ethereum.org/EIPS/eip-2494).
+
+## Data structures
+
+### Elliptic curve configurations
+
+(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Curve`), i.e. the specific elliptic
+curve you want to use, which would be specified using any one of the methods
+`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::new` which take the coefficients in the
+defining equation together with a generator point as parameters. You can find more detail in the
+comments in
+[`noir_stdlib/src/ec.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr), but
+the gist of it is that the elliptic curves of interest are usually expressed in one of the standard
+forms implemented here (Twisted Edwards, Montgomery and Short Weierstraß), and in addition to that,
+you could choose to use `affine` coordinates (Cartesian coordinates - the usual (x,y) - possibly
+together with a point at infinity) or `curvegroup` coordinates (some form of projective coordinates
+requiring more coordinates but allowing for more efficient implementations of elliptic curve
+operations). Conversions between all of these forms are provided, and under the hood these
+conversions are done whenever an operation is more efficient in a different representation (or a
+mixed coordinate representation is employed).
+
+### Points
+
+(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Point`), i.e. points lying on the
+elliptic curve. For a curve configuration `c` and a point `p`, it may be checked that `p`
+does indeed lie on `c` by calling `c.contains(p1)`.
+
+## Methods
+
+(given a choice of curve representation, e.g. use `std::ec::tecurve::affine::Curve` and use
+`std::ec::tecurve::affine::Point`)
+
+- The **zero element** is given by `Point::zero()`, and we can verify whether a point `p: Point` is
+ zero by calling `p.is_zero()`.
+- **Equality**: Points `p1: Point` and `p2: Point` may be checked for equality by calling
+ `p1.eq(p2)`.
+- **Addition**: For `c: Curve` and points `p1: Point` and `p2: Point` on the curve, adding these two
+ points is accomplished by calling `c.add(p1,p2)`.
+- **Negation**: For a point `p: Point`, `p.negate()` is its negation.
+- **Subtraction**: For `c` and `p1`, `p2` as above, subtracting `p2` from `p1` is accomplished by
+ calling `c.subtract(p1,p2)`.
+- **Scalar multiplication**: For `c` as above, `p: Point` a point on the curve and `n: Field`,
+ scalar multiplication is given by `c.mul(n,p)`. If instead `n :: [u1; N]`, i.e. `n` is a bit
+ array, the `bit_mul` method may be used instead: `c.bit_mul(n,p)`
+- **Multi-scalar multiplication**: For `c` as above and arrays `n: [Field; N]` and `p: [Point; N]`,
+ multi-scalar multiplication is given by `c.msm(n,p)`.
+- **Coordinate representation conversions**: The `into_group` method converts a point or curve
+ configuration in the affine representation to one in the CurveGroup representation, and
+ `into_affine` goes in the other direction.
+- **Curve representation conversions**: `tecurve` and `montcurve` curves and points are equivalent
+ and may be converted between one another by calling `into_montcurve` or `into_tecurve` on their
+ configurations or points. `swcurve` is more general and a curve c of one of the other two types
+ may be converted to this representation by calling `c.into_swcurve()`, whereas a point `p` lying
+ on the curve given by `c` may be mapped to its corresponding `swcurve` point by calling
+ `c.map_into_swcurve(p)`.
+- **Map-to-curve methods**: The Elligator 2 method of mapping a field element `n: Field` into a
+ `tecurve` or `montcurve` with configuration `c` may be called as `c.elligator2_map(n)`. For all of
+ the curve configurations, the SWU map-to-curve method may be called as `c.swu_map(z,n)`, where
+ `z: Field` depends on `Field` and `c` and must be chosen by the user (the conditions it needs to
+ satisfy are specified in the comments
+ [here](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr)).
+
+## Examples
+
+The
+[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr)
+illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more
+interesting examples in Noir would be:
+
+Public-key cryptography: Given an elliptic curve and a 'base point' on it, determine the public key
+from the private key. This is a matter of using scalar multiplication. In the case of Baby Jubjub,
+for example, this code would do:
+
+```rust
+use dep::std::ec::tecurve::affine::{Curve, Point};
+
+fn bjj_pub_key(priv_key: Field) -> Point
+{
+
+ let bjj = Curve::new(168700, 168696, G::new(995203441582195749578291179787384436505546430278305826713579947235728471134,5472060717959818805561601436314318772137091100104008585924551046643952123905));
+
+ let base_pt = Point::new(5299619240641551281634865583518297030282874472190772894086521144482721001553, 16950150798460657717958625567821834550301663161624707787222815936182638968203);
+
+ bjj.mul(priv_key,base_pt)
+}
+```
+
+This would come in handy in a Merkle proof.
+
+- EdDSA signature verification: This is a matter of combining these primitives with a suitable hash
+ function. See
+ [feat(stdlib): EdDSA sig verification noir#1136](https://github.com/noir-lang/noir/pull/1136) for
+ the case of Baby Jubjub and the Poseidon hash function.
diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx
new file mode 100644
index 00000000000..4bf09cef178
--- /dev/null
+++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx
@@ -0,0 +1,60 @@
+---
+title: ECDSA Signature Verification
+description: Learn about the cryptographic primitives regarding ECDSA over the secp256k1 and secp256r1 curves
+keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, signatures]
+sidebar_position: 3
+---
+
+import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx';
+
+Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves.
+
+## ecdsa_secp256k1::verify_signature
+
+Verifier for ECDSA Secp256k1 signatures
+
+```rust title="ecdsa_secp256k1" showLineNumbers
+pub fn verify_signature(
+ public_key_x: [u8; 32],
+ public_key_y: [u8; 32],
+ signature: [u8; 64],
+ message_hash: [u8; N]
+) -> bool
+```
+>