diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 234574c30e2..c4cb4386deb 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -51,7 +51,7 @@ body: attributes: label: Node Version description: Your Node.js version - placeholder: e.g. 24.15.0 + placeholder: e.g. 26.12.0 - type: textarea id: additional-context attributes: diff --git a/.github/workflows/nightly-release-tag.yml b/.github/workflows/nightly-release-tag.yml index 55fcfb2f381..0a99ba0bceb 100644 --- a/.github/workflows/nightly-release-tag.yml +++ b/.github/workflows/nightly-release-tag.yml @@ -23,7 +23,7 @@ jobs: strategy: fail-fast: false matrix: - branch: ${{ fromJSON('["next", "v5-next"]') }} + branch: ${{ fromJSON('["next", "v5-next", "v6"]') }} concurrency: group: ${{ github.workflow }}-${{ github.repository }}-${{ matrix.branch }} steps: diff --git a/.nvmrc b/.nvmrc index f3c88209af5..3635429b74f 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v24.15.0 +v26.12.0 diff --git a/Makefile b/Makefile index eb1d841e6fa..74f3c636546 100644 --- a/Makefile +++ b/Makefile @@ -263,7 +263,7 @@ bb-ts-cross-copy: bb-ts bb-cpp-cross # the same barretenberg/ts node_modules. bb-ts additionally emits the bb.js test commands, # which the test engine runs concurrently with the rest of the build out of that same tree, # so an unordered npm_install_deps here swaps files under a running node (SIGBUS). -bb-avm-sim: ipc-codegen ipc-runtime bb-cpp-native bb-ts +bb-avm-sim: ipc-codegen ipc-runtime bb-cpp-native bb-cpp-wasm bb-ts $(call build,$@,barretenberg/ts,build_bb_avm_sim) # Ordered after bb-cdb (and after bb-ts-cross-copy, which builds bb.js-api's cross copies) @@ -312,6 +312,9 @@ bb-sol: bb-cpp-native bb-crs l1-contracts-solc bb-cpp-native-tests: bb-cpp-native bb-cpp-chonk-inputs $(call test,$@,barretenberg/cpp,native) +bb-cpp-wasm-tests: bb-cpp-wasm + $(call test,$@,barretenberg/cpp,wasm) + bb-cpp-wasm-threads-tests: bb-cpp-wasm-threads $(call test,$@,barretenberg/cpp,wasm_threads) @@ -327,6 +330,11 @@ bb-acir-tests: bb-acir bb-ts-tests: bb-ts $(call test,$@,barretenberg/ts) +# After bb-cdb, the last of the barretenberg/ts targets to install into node_modules, which the +# tests run out of. +bb-avm-sim-tests: bb-avm-sim bb-cdb + $(call test,$@,barretenberg/ts,bb_avm_sim) + bb-sol-tests: bb-sol $(call test,$@,barretenberg/sol) @@ -339,7 +347,7 @@ bb-bbup-tests: bb-bbup bb-rs-tests: bb-rs $(call test,$@,barretenberg/rust) -bb-tests: bb-cpp-native-tests bb-acir-tests bb-ts-tests bb-sol-tests bb-bbup-tests bb-docs-tests bb-rs-tests +bb-tests: bb-cpp-native-tests bb-cpp-wasm-tests bb-acir-tests bb-ts-tests bb-avm-sim-tests bb-sol-tests bb-bbup-tests bb-docs-tests bb-rs-tests bb-full-tests: bb-cpp-wasm-threads-tests bb-cpp-asan-tests bb-cpp-smt-tests diff --git a/avm-transpiler/Cargo.lock b/avm-transpiler/Cargo.lock index 04d5f592973..6ab0b8d8552 100644 --- a/avm-transpiler/Cargo.lock +++ b/avm-transpiler/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "acir" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "acir_field", "base64 0.23.0", @@ -24,7 +24,7 @@ dependencies = [ [[package]] name = "acir_field" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "ark-bn254", "ark-ff", @@ -38,7 +38,7 @@ dependencies = [ [[package]] name = "acvm" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "acir", "acvm_blackbox_solver", @@ -53,7 +53,7 @@ dependencies = [ [[package]] name = "acvm_blackbox_solver" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "acir", "aes", @@ -447,7 +447,7 @@ dependencies = [ [[package]] name = "bn254_blackbox_solver" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "acir", "acvm_blackbox_solver", @@ -470,7 +470,7 @@ dependencies = [ [[package]] name = "brillig" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "acir_field", "itertools 0.15.0", @@ -480,7 +480,7 @@ dependencies = [ [[package]] name = "brillig_vm" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "acir", "acvm_blackbox_solver", @@ -947,7 +947,7 @@ dependencies = [ [[package]] name = "fm" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "codespan-reporting", "iter-extended", @@ -1169,7 +1169,7 @@ checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "iter-extended" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" [[package]] name = "itertools" @@ -1290,7 +1290,7 @@ dependencies = [ [[package]] name = "msgpack_tagged" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "msgpack_tagged_derive", "rmp", @@ -1301,7 +1301,7 @@ dependencies = [ [[package]] name = "msgpack_tagged_derive" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "proc-macro2", "quote", @@ -1310,7 +1310,7 @@ dependencies = [ [[package]] name = "noirc_abi" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "acvm", "iter-extended", @@ -1326,11 +1326,11 @@ dependencies = [ [[package]] name = "noirc_arena" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" [[package]] name = "noirc_artifacts" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "acir", "acvm", @@ -1349,7 +1349,7 @@ dependencies = [ [[package]] name = "noirc_errors" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "codespan-reporting", "fm", @@ -1361,7 +1361,7 @@ dependencies = [ [[package]] name = "noirc_evaluator" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "acvm", "bit-vec", @@ -1394,7 +1394,7 @@ dependencies = [ [[package]] name = "noirc_frontend" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "acvm", "bn254_blackbox_solver", @@ -1426,7 +1426,7 @@ dependencies = [ [[package]] name = "noirc_printable_type" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "acvm", "iter-extended", @@ -1437,7 +1437,7 @@ dependencies = [ [[package]] name = "noirc_span" -version = "1.0.0-rc.3" +version = "1.0.0-rc.5" dependencies = [ "codespan", "serde", diff --git a/barretenberg/.claude/skills/benchmark-chonk/SKILL.md b/barretenberg/.claude/skills/benchmark-chonk/SKILL.md index ff46ce600a6..6e7eb9f674d 100644 --- a/barretenberg/.claude/skills/benchmark-chonk/SKILL.md +++ b/barretenberg/.claude/skills/benchmark-chonk/SKILL.md @@ -131,7 +131,7 @@ HARDWARE_CONCURRENCY=8 BB_BENCH=1 \ ``` The wasmtime wrapper sets: -- `-Wthreads=y -Sthreads=y` — enable WASM threads and shared memory +- `-Wthreads=y -Wshared-memory=y -Wexceptions=y -Sthreads=y` — enable WASM threads, shared memory and exception handling - `--env HARDWARE_CONCURRENCY` — thread count - `--env BB_BENCH` — enable operation counting (requires configuring with `-DENABLE_WASM_BENCH=ON`; it is OFF by default in the `wasm-threads` preset to keep the shipped wasm fast) - `--dir=$HOME/.bb-crs --dir=.` — filesystem access for CRS and working directory diff --git a/barretenberg/.claude/skills/profile-chonk/SKILL.md b/barretenberg/.claude/skills/profile-chonk/SKILL.md index df2977eb6c7..6d382439b44 100644 --- a/barretenberg/.claude/skills/profile-chonk/SKILL.md +++ b/barretenberg/.claude/skills/profile-chonk/SKILL.md @@ -138,7 +138,7 @@ ssh $BB_SSH_KEY $BB_SSH_INSTANCE " cd $REMOTE_DIR HARDWARE_CONCURRENCY=$HARDWARE_CONCURRENCY \ /home/ubuntu/.wasmtime/bin/wasmtime run \ - -Wthreads=y -Sthreads=y -Wshared-memory=y \ + -Wthreads=y -Wexceptions=y -Sthreads=y -Wshared-memory=y \ --env HARDWARE_CONCURRENCY --env HOME --env BB_BENCH=1 \ --dir=\$HOME/.bb-crs --dir=. \ ./bin/bb prove \ diff --git a/barretenberg/.claude/skills/remote-bench/SKILL.md b/barretenberg/.claude/skills/remote-bench/SKILL.md index a4845bc42d7..f34e43f4217 100644 --- a/barretenberg/.claude/skills/remote-bench/SKILL.md +++ b/barretenberg/.claude/skills/remote-bench/SKILL.md @@ -203,7 +203,7 @@ ssh $BB_SSH_KEY $BB_SSH_INSTANCE \ "cd $BB_SSH_CPP_PATH/build-wasm-threads && \ HARDWARE_CONCURRENCY=16 \ /home/ubuntu/.wasmtime/bin/wasmtime run \ - -Wthreads=y -Sthreads=y \ + -Wthreads=y -Wshared-memory=y -Wexceptions=y -Sthreads=y \ --env HARDWARE_CONCURRENCY \ --env HOME \ --env BB_BENCH=1 \ diff --git a/barretenberg/acir_tests/headless-test/package.json b/barretenberg/acir_tests/headless-test/package.json index 1f16ffb506e..0db3ad32e87 100644 --- a/barretenberg/acir_tests/headless-test/package.json +++ b/barretenberg/acir_tests/headless-test/package.json @@ -10,7 +10,7 @@ "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0", - "playwright": "1.49.0", + "playwright": "1.63.0", "puppeteer": "^24.22.3" }, "devDependencies": { diff --git a/barretenberg/acir_tests/yarn.lock b/barretenberg/acir_tests/yarn.lock index 0116166b998..8ee1b237708 100644 --- a/barretenberg/acir_tests/yarn.lock +++ b/barretenberg/acir_tests/yarn.lock @@ -2514,16 +2514,6 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:2.3.2": - version: 2.3.2 - resolution: "fsevents@npm:2.3.2" - dependencies: - node-gyp: "npm:latest" - checksum: 10c0/be78a3efa3e181cda3cf7a4637cb527bcebb0bd0ea0440105a3bb45b86f9245b307dc10a2507e8f4498a7d4ec349d1910f4d73e4d4495b16103106e07eee735b - conditions: os=darwin - languageName: node - linkType: hard - "fsevents@npm:~2.3.2": version: 2.3.3 resolution: "fsevents@npm:2.3.3" @@ -2534,15 +2524,6 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@npm%3A2.3.2#optional!builtin": - version: 2.3.2 - resolution: "fsevents@patch:fsevents@npm%3A2.3.2#optional!builtin::version=2.3.2&hash=df0bf1" - dependencies: - node-gyp: "npm:latest" - conditions: os=darwin - languageName: node - linkType: hard - "fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": version: 2.3.3 resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" @@ -2723,7 +2704,7 @@ __metadata: dependencies: chalk: "npm:^5.3.0" commander: "npm:^12.1.0" - playwright: "npm:1.49.0" + playwright: "npm:1.63.0" puppeteer: "npm:^24.22.3" ts-node: "npm:^10.9.2" typescript: "npm:^5.4.2" @@ -4145,27 +4126,23 @@ __metadata: languageName: node linkType: hard -"playwright-core@npm:1.49.0": - version: 1.49.0 - resolution: "playwright-core@npm:1.49.0" +"playwright-core@npm:1.63.0": + version: 1.63.0 + resolution: "playwright-core@npm:1.63.0" bin: playwright-core: cli.js - checksum: 10c0/22c1a72fabdcc87bd1cd4d40a032d2c5b94cf94ba7484dc182048c3fa1c8ec26180b559d8cac4ca9870e8fd6bdf5ef9d9f54e7a31fd60d67d098fcffc5e4253b + checksum: 10c0/39c1ebae4029ca2e9371e00a5588d962038e24b6e0bdb7d0d27374dde04838f349b83ae0c8b3b648653d0935cba7fb9011f1201adaab9dd0db7801ad6c75e9aa languageName: node linkType: hard -"playwright@npm:1.49.0": - version: 1.49.0 - resolution: "playwright@npm:1.49.0" +"playwright@npm:1.63.0": + version: 1.63.0 + resolution: "playwright@npm:1.63.0" dependencies: - fsevents: "npm:2.3.2" - playwright-core: "npm:1.49.0" - dependenciesMeta: - fsevents: - optional: true + playwright-core: "npm:1.63.0" bin: playwright: cli.js - checksum: 10c0/e94d662747cd147d0573570fec90dadc013c1097595714036fc8934a075c5a82ab04a49111b03b1f762ea86429bdb7c94460901896901e20970b30ce817cc93f + checksum: 10c0/d1fd048a89be27c9aba6757dc4f70eff4a5163b49f439ad691767be8b2a482327d0dd1928b4f73c8fe0d8b33aab2c9dc2670424ab60f112d9e98e91b94e3b090 languageName: node linkType: hard diff --git a/barretenberg/cpp/CLAUDE.md b/barretenberg/cpp/CLAUDE.md index a31f266ece4..3e1cd891153 100644 --- a/barretenberg/cpp/CLAUDE.md +++ b/barretenberg/cpp/CLAUDE.md @@ -89,7 +89,7 @@ clang-format-20 -i These are load-bearing: violating them will compile on native but break WASM, or skew test output in CI. Use the listed alternative unconditionally. - **Logging: use `info(...)` / `vinfo(...)` from `barretenberg/common/log.hpp`, never `std::cout` or `std::cerr`.** The macros route through `log_function` and respect `bb_log_level`; direct `std::cout` is unfiltered, uncaptured in CI logs, and skews benchmark output. -- **Aborts and exceptions: use `throw_or_abort(msg)` from `barretenberg/common/throw_or_abort.hpp`, never bare `throw` or `std::abort()`.** WASM builds set `BB_NO_EXCEPTIONS`, which turns `throw` into `abort()` — bare `throw` compiles differently and misformats the message, and raw `std::abort()` drops the message entirely. For header-only libraries that must use exception syntax, use the `THROW`/`RETHROW` macros from `common/try_catch_shim.hpp`. +- **Aborts and exceptions: use `throw_or_abort(msg)` from `barretenberg/common/throw_or_abort.hpp`, never bare `throw` or `std::abort()`.** Builds that set `BB_NO_EXCEPTIONS` (the `clang20-no-exceptions` preset; WASM builds now use wasm exception handling) turn `throw_or_abort` into `abort()`, and raw `std::abort()` drops the message entirely. For header-only libraries that must use exception syntax, use the `THROW`/`RETHROW` macros from `common/try_catch_shim.hpp`. - **Assertions: use `BB_ASSERT(cond, msg)` / `BB_ASSERT_EQ` / `BB_ASSERT_NEQ` / `BB_ASSERT_GT` / etc. from `barretenberg/common/assert.hpp`, never bare `assert(...)`.** `BB_ASSERT` hooks into the benchmark-assertion framework and has distinct debug/release behavior; `assert` is stripped in release builds and silently diverges in WASM. - **Release builds: do not reach for the `release` CMake preset unless the user is reproducing a performance issue.** Debug/default presets are much faster to compile and sufficient for correctness work. diff --git a/barretenberg/cpp/CMakePresets.json b/barretenberg/cpp/CMakePresets.json index bcb0a8b9d32..5dfec5c889a 100644 --- a/barretenberg/cpp/CMakePresets.json +++ b/barretenberg/cpp/CMakePresets.json @@ -422,7 +422,7 @@ "CMAKE_C_COMPILER_WORKS": "ON", "CMAKE_CXX_COMPILER_WORKS": "ON", "MULTITHREADING": "OFF", - "CMAKE_CXX_FLAGS": "-DBB_NO_EXCEPTIONS" + "AVM": "OFF" } }, { @@ -768,7 +768,9 @@ "targets": [ "barretenberg.wasm", "barretenberg.wasm.gz", - "barretenberg-debug.wasm" + "barretenberg-debug.wasm", + "bb-avm-sim.wasm", + "avm_ffi_tests" ] }, { diff --git a/barretenberg/cpp/bootstrap.sh b/barretenberg/cpp/bootstrap.sh index 58478b1f6a7..6528a27f1e1 100755 --- a/barretenberg/cpp/bootstrap.sh +++ b/barretenberg/cpp/bootstrap.sh @@ -358,6 +358,11 @@ function test_cmds_wasm_threads { echo "$hash:CPUS=8 HARDWARE_CONCURRENCY=8 barretenberg/cpp/scripts/wasmtime.sh barretenberg/cpp/build-wasm-threads/bin/ecc_tests" } +function test_cmds_wasm { + # The AVM simulator's in-process service, from the single-threaded build bb-avm-sim.wasm comes from. + echo "$hash SINGLE_THREADED=1 barretenberg/cpp/scripts/wasmtime.sh barretenberg/cpp/build-wasm/bin/avm_ffi_tests" +} + function test_cmds_asan { local prefix="$hash:CPUS=4:MEM=8g" @@ -395,6 +400,7 @@ function test_cmds_nightly { function test_cmds { if [ -z "${1:-}" ]; then test_cmds_native + test_cmds_wasm if [ "$CI_FULL" -eq 1 ]; then test_cmds_wasm_threads test_cmds_asan diff --git a/barretenberg/cpp/cmake/arch.cmake b/barretenberg/cpp/cmake/arch.cmake index c8158dfb8cc..41132e9ea16 100644 --- a/barretenberg/cpp/cmake/arch.cmake +++ b/barretenberg/cpp/cmake/arch.cmake @@ -2,7 +2,11 @@ if(WASM) # Disable SLP vectorization on WASM as it's brokenly slow. To give an idea, with this off it still takes # 2m:18s to compile scalar_multiplication.cpp, and with it on I estimate it's 50-100 times longer. I never # had the patience to wait it out... - add_compile_options(-fno-exceptions -fno-slp-vectorize) + add_compile_options(-fno-slp-vectorize) + # Standard (exnref) wasm exception handling. wasi-sdk 33+ ships a sysroot built with it, selected by + # -fwasm-exceptions at link time; libunwind provides the unwinder. + add_compile_options(-fwasm-exceptions -mllvm -wasm-use-legacy-eh=false) + add_link_options(-fwasm-exceptions -lunwind) endif() # Target skylake on x86 for AVX2 etc. ARM is handled by the zig wrapper scripts diff --git a/barretenberg/cpp/cmake/benchmark.cmake b/barretenberg/cpp/cmake/benchmark.cmake index fb72f700766..d18755e9d5d 100644 --- a/barretenberg/cpp/cmake/benchmark.cmake +++ b/barretenberg/cpp/cmake/benchmark.cmake @@ -18,3 +18,7 @@ if(NOT benchmark_FOUND) set_property(DIRECTORY ${benchmark_SOURCE_DIR} PROPERTY EXCLUDE_FROM_ALL) set_property(DIRECTORY ${benchmark_BINARY_DIR} PROPERTY EXCLUDE_FROM_ALL) endif() +# The library builds with -pedantic-errors, and clang 23 (wasi-sdk 34) flags its __COUNTER__ as a C2y extension. +if(TARGET benchmark AND CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 23) + target_compile_options(benchmark PRIVATE -Wno-c2y-extensions) +endif() diff --git a/barretenberg/cpp/cmake/threading.cmake b/barretenberg/cpp/cmake/threading.cmake index 5092e8064c6..99c25b93768 100644 --- a/barretenberg/cpp/cmake/threading.cmake +++ b/barretenberg/cpp/cmake/threading.cmake @@ -3,8 +3,8 @@ if(MULTITHREADING) add_compile_options(-pthread) add_link_options(-pthread) if(WASM) - add_compile_options(--target=wasm32-wasi-threads) - add_link_options(--target=wasm32-wasi-threads -Wl,--shared-memory) + add_compile_options(--target=wasm32-wasip1-threads) + add_link_options(--target=wasm32-wasip1-threads -Wl,--shared-memory) # Prevent indirect call type mismatch errors in thread_local destructors # (without this the benchmark flow fails at destruction point for WASM) add_compile_options(-fno-c++-static-destructors) diff --git a/barretenberg/cpp/scripts/benchmark_wasm.sh b/barretenberg/cpp/scripts/benchmark_wasm.sh index 0b4483c7c09..a307fe3ffe7 100755 --- a/barretenberg/cpp/scripts/benchmark_wasm.sh +++ b/barretenberg/cpp/scripts/benchmark_wasm.sh @@ -17,4 +17,4 @@ cmake --build --preset wasm-threads --target $BENCHMARK cd build-wasm-threads # Consistency with _wasm.sh targets / shorter $COMMAND. cp ./bin/$BENCHMARK . -wasmtime run --env HARDWARE_CONCURRENCY=$HARDWARE_CONCURRENCY -Wthreads=y -Sthreads=y --dir=.. $COMMAND \ No newline at end of file +wasmtime run --env HARDWARE_CONCURRENCY=$HARDWARE_CONCURRENCY -Wthreads=y -Wshared-memory=y -Wexceptions=y -Sthreads=y --dir=.. $COMMAND \ No newline at end of file diff --git a/barretenberg/cpp/scripts/benchmark_wasm_remote.sh b/barretenberg/cpp/scripts/benchmark_wasm_remote.sh index 62213556579..18ce7aa0543 100755 --- a/barretenberg/cpp/scripts/benchmark_wasm_remote.sh +++ b/barretenberg/cpp/scripts/benchmark_wasm_remote.sh @@ -27,4 +27,4 @@ ssh $BB_SSH_KEY $BB_SSH_INSTANCE "mkdir -p $BB_SSH_CPP_PATH/build-wasm-threads" scp $BB_SSH_KEY ./bin/$BENCHMARK $BB_SSH_INSTANCE:$BB_SSH_CPP_PATH/build-wasm-threads # run wasm benchmarking ssh $BB_SSH_KEY $BB_SSH_INSTANCE \ - "cd $BB_SSH_CPP_PATH/build-wasm-threads ; /home/ubuntu/.wasmtime/bin/wasmtime run --env HARDWARE_CONCURRENCY=$HARDWARE_CONCURRENCY -Wthreads=y -Sthreads=y --dir=.. $COMMAND" + "cd $BB_SSH_CPP_PATH/build-wasm-threads ; /home/ubuntu/.wasmtime/bin/wasmtime run --env HARDWARE_CONCURRENCY=$HARDWARE_CONCURRENCY -Wthreads=y -Wshared-memory=y -Wexceptions=y -Sthreads=y --dir=.. $COMMAND" diff --git a/barretenberg/cpp/scripts/profile_wasm_samply.sh b/barretenberg/cpp/scripts/profile_wasm_samply.sh index 218ed2b8f89..27d0e333f30 100755 --- a/barretenberg/cpp/scripts/profile_wasm_samply.sh +++ b/barretenberg/cpp/scripts/profile_wasm_samply.sh @@ -16,4 +16,4 @@ cmake --build --preset wasm-threads --target $BENCHMARK cd build-wasm-threads # Consistency with _wasm.sh targets / shorter $COMMAND. cp ./bin/$BENCHMARK . -samply record wasmtime run --profile=perfmap --env HARDWARE_CONCURRENCY=$HARDWARE_CONCURRENCY -Wthreads=y -Sthreads=y --dir=.. $COMMAND \ No newline at end of file +samply record wasmtime run --profile=perfmap --env HARDWARE_CONCURRENCY=$HARDWARE_CONCURRENCY -Wthreads=y -Wshared-memory=y -Wexceptions=y -Sthreads=y --dir=.. $COMMAND \ No newline at end of file diff --git a/barretenberg/cpp/scripts/wasm_env_memory.wat b/barretenberg/cpp/scripts/wasm_env_memory.wat new file mode 100644 index 00000000000..2af340e87de --- /dev/null +++ b/barretenberg/cpp/scripts/wasm_env_memory.wat @@ -0,0 +1,3 @@ +;; The linear memory a single-threaded bb wasm imports as env.memory (bb links with --import-memory, so its host +;; sizes the memory). The wasmtime CLI fills only the imports WASI defines, so wasmtime.sh preloads this as `env`. +(module (memory (export "memory") 512 65536)) diff --git a/barretenberg/cpp/scripts/wasmtime.sh b/barretenberg/cpp/scripts/wasmtime.sh index 3ad657d5798..4a847407fd4 100755 --- a/barretenberg/cpp/scripts/wasmtime.sh +++ b/barretenberg/cpp/scripts/wasmtime.sh @@ -1,11 +1,20 @@ #!/usr/bin/env bash # Helper for passing environment variables to wasm and common config. # Allows accessing ~/.bb-crs and ./ (more can be added as parameters to this script). +# SINGLE_THREADED=1 runs a single-threaded build: wasi-threads would reject its unshared memory import, so it gets +# the memory from wasm_env_memory.wat instead. set -eu export WASMTIME_BACKTRACE_DETAILS=1 +if [ "${SINGLE_THREADED:-0}" -eq 1 ]; then + memory_args=(--preload env=$(dirname $0)/wasm_env_memory.wat) +else + memory_args=(-Sthreads=y) +fi exec wasmtime run \ -Wthreads=y \ - -Sthreads=y \ + -Wshared-memory=y \ + -Wexceptions=y \ + "${memory_args[@]}" \ ${HARDWARE_CONCURRENCY:+--env HARDWARE_CONCURRENCY} \ --env HOME \ ${MAIN_ARGS:+--env MAIN_ARGS} \ diff --git a/barretenberg/cpp/src/CMakeLists.txt b/barretenberg/cpp/src/CMakeLists.txt index ad492f8682b..caf89a6243a 100644 --- a/barretenberg/cpp/src/CMakeLists.txt +++ b/barretenberg/cpp/src/CMakeLists.txt @@ -119,6 +119,11 @@ add_subdirectory(barretenberg/vm2_stub) # consumes wasi_objects, so there is nothing to build here otherwise. if(WASM) add_subdirectory(barretenberg/wasi) + # The AVM simulator, without the AVM prover (the wasm presets set AVM off), as bb-avm-sim.wasm. + add_subdirectory(barretenberg/aztec) + add_subdirectory(barretenberg/world_state_reference) + add_subdirectory(barretenberg/vm2) + add_subdirectory(barretenberg/avm) endif() if(NOT FUZZING AND NOT WASM AND NOT BB_LITE) diff --git a/barretenberg/cpp/src/barretenberg/avm/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/avm/CMakeLists.txt index d05f6c59131..dfe69d9bfde 100644 --- a/barretenberg/cpp/src/barretenberg/avm/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/avm/CMakeLists.txt @@ -1,6 +1,16 @@ +# bb-avm-sim: the AVM simulator as a service over avm_schema.json. Two artifacts carry it to +# @aztec-foundation/bb-avm-sim (barretenberg/ts/bb-avm-sim): +# - the bb-avm-sim binary (native builds), served over IPC, reaching world state and contract +# data through its wsdb and cdb IPC clients; +# - bb-avm-sim.wasm, a WASI reactor (wasm builds) exporting the generated FFI entry +# (avm_ipc_ffi_entry). It has no route to world state or contract data yet, so it serves +# SimulateWithHints alone. set(AVM_SCHEMA ${CMAKE_CURRENT_SOURCE_DIR}/avm_schema.json) set(AVM_GEN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/generated) set(AVM_GEN_OUTPUTS + ${AVM_GEN_DIR}/avm_dispatch.hpp + ${AVM_GEN_DIR}/avm_ffi.cpp + ${AVM_GEN_DIR}/avm_ffi.hpp ${AVM_GEN_DIR}/avm_ipc_server.hpp ${AVM_GEN_DIR}/avm_types.hpp ${AVM_GEN_DIR}/ipc_codegen/msgpack_adaptor.hpp @@ -20,16 +30,68 @@ add_custom_command( --lang cpp --out ${AVM_GEN_DIR} --server + --ffi --cpp-namespace bb::avm --cpp-include-dir barretenberg/avm/generated --prefix Avm --strip-method-prefix DEPENDS ${AVM_SCHEMA} ${IPC_CODEGEN_SRC} - COMMENT "Generating AVM IPC server from avm_schema.json" + COMMENT "Generating the AVM IPC server and FFI entry from avm_schema.json" VERBATIM ) add_custom_target(avm_ipc_generated DEPENDS ${AVM_GEN_OUTPUTS}) +# The tests read vm2's minimal transaction fixture, embedded so the wasm build needs no filesystem. +set(MINIMAL_TX_FIXTURE ${CMAKE_CURRENT_SOURCE_DIR}/../vm2/testing/minimal_tx.testdata.bin) +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${MINIMAL_TX_FIXTURE}) +file(READ ${MINIMAL_TX_FIXTURE} MINIMAL_TX_HEX HEX) +string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," MINIMAL_TX_BYTES "${MINIMAL_TX_HEX}") +set(AVM_TEST_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/include) +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/minimal_tx_fixture.hpp.tmp + "#pragma once\n// Generated by barretenberg/avm/CMakeLists.txt from vm2/testing/minimal_tx.testdata.bin.\n" + "inline constexpr unsigned char MINIMAL_TX_FIXTURE[] = { ${MINIMAL_TX_BYTES} };\n") +# Copied only when it changes, so a reconfigure does not rebuild the tests. +configure_file(${CMAKE_CURRENT_BINARY_DIR}/minimal_tx_fixture.hpp.tmp + ${AVM_TEST_INCLUDE_DIR}/barretenberg/avm/minimal_tx_fixture.hpp COPYONLY) + +# The service as a library over the generated FFI entry, which the wasm reactor exports and the +# tests call. The entry is generated at build time, so a clean tree's configure cannot glob it. +barretenberg_module_with_sources( + avm_ffi + SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/avm_ffi_dispatcher.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/avm_simulate_with_hints.cpp + ${AVM_GEN_DIR}/avm_ffi.cpp + HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/avm_simulate_with_hints.hpp + TEST_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/avm_ffi.test.cpp + DEPENDENCIES vm2_sim +) +add_dependencies(avm_ffi_objects avm_ipc_generated) +if(TARGET avm_ffi_test_objects) + add_dependencies(avm_ffi_test_objects avm_ipc_generated) + target_include_directories(avm_ffi_test_objects PRIVATE ${AVM_TEST_INCLUDE_DIR}) +endif() + +if(WASM) + # A WASI reactor exporting the FFI entry: the module and its dependencies plus barretenberg/wasi + # (the env functions over WASI, and _initialize). It asks its host for WASI alone. + add_executable( + bb-avm-sim-debug.wasm + $ + $ + ) + target_link_libraries(bb-avm-sim-debug.wasm PRIVATE vm2_sim) + target_link_options(bb-avm-sim-debug.wasm PRIVATE -nostartfiles -Wl,--no-entry,--export-dynamic) + add_custom_command( + OUTPUT ${CMAKE_BINARY_DIR}/bin/bb-avm-sim.wasm + COMMAND ${CMAKE_STRIP} ${CMAKE_BINARY_DIR}/bin/bb-avm-sim-debug.wasm -o ${CMAKE_BINARY_DIR}/bin/bb-avm-sim.wasm + DEPENDS bb-avm-sim-debug.wasm + COMMENT "Stripping debug info to create bb-avm-sim.wasm" + ) + add_custom_target(bb-avm-sim.wasm ALL DEPENDS ${CMAKE_BINARY_DIR}/bin/bb-avm-sim.wasm) + return() +endif() + add_executable( bb-avm-sim main.cpp @@ -47,6 +109,7 @@ target_link_libraries( bb-avm-sim PRIVATE barretenberg + avm_ffi vm2_sim wsdb_ipc_merkle_db cdb_ipc_client diff --git a/barretenberg/cpp/src/barretenberg/avm/avm_execute.cpp b/barretenberg/cpp/src/barretenberg/avm/avm_execute.cpp index b5c77675232..994fef8f2f5 100644 --- a/barretenberg/cpp/src/barretenberg/avm/avm_execute.cpp +++ b/barretenberg/cpp/src/barretenberg/avm/avm_execute.cpp @@ -1,8 +1,8 @@ #include "barretenberg/avm/avm_execute.hpp" +#include "barretenberg/avm/avm_simulate_with_hints.hpp" #include "barretenberg/common/log.hpp" #include "barretenberg/serialize/msgpack.hpp" #include "barretenberg/serialize/msgpack_impl.hpp" -#include "barretenberg/vm2/avm_sim_api.hpp" #include "barretenberg/vm2/common/avm_io.hpp" #include "barretenberg/vm2/simulation/lib/cancellation_token.hpp" #include "barretenberg/vm2/simulation_helper.hpp" @@ -52,6 +52,10 @@ template static T deserialize_from_msgpack(const std::vector void handle_simulate(AvmRequest& request, wire::AvmSimulate&& command, Responder respond) { + if (request.cdb_client == nullptr || request.wsdb_client == nullptr) { + respond.error("Simulate needs bb-avm-sim started with --wsdb and --cdb"); + return; + } // Reuse the process-lifetime token (cleared of any prior cancellation) instead // of allocating a per-request one the signal handler could outlive. g_sim_cancellation_token->reset(); @@ -64,7 +68,7 @@ void handle_simulate(AvmRequest& request, wire::AvmSimulate&& command, Responder uint64_t fork_id = sim_inputs.ws_revision.forkId; vinfo("Using external WSDB fork ", fork_id, " for AVM simulation"); - request.cdb_client.set_fork_id(fork_id); + request.cdb_client->set_fork_id(fork_id); g_active_cancellation_token.store(cancellation_token.get(), std::memory_order_release); @@ -76,18 +80,18 @@ void handle_simulate(AvmRequest& request, wire::AvmSimulate&& command, Responder .includeUncommitted = true, }; - bb::avm2::simulation::WsdbIpcMerkleDB merkle_db(request.wsdb_client, revision); + bb::avm2::simulation::WsdbIpcMerkleDB merkle_db(*request.wsdb_client, revision); AvmSimulationHelper simulation_helper; auto result = sim_inputs.config.collect_hints - ? simulation_helper.simulate_for_hint_collection_internal(request.cdb_client, + ? simulation_helper.simulate_for_hint_collection_internal(*request.cdb_client, merkle_db, sim_inputs.config, sim_inputs.tx, sim_inputs.global_variables, sim_inputs.protocol_contracts, cancellation_token) - : simulation_helper.simulate_fast_internal(request.cdb_client, + : simulation_helper.simulate_fast_internal(*request.cdb_client, merkle_db, sim_inputs.config, sim_inputs.tx, @@ -112,12 +116,7 @@ void handle_simulate_with_hints(AvmRequest& request, (void)request; try { - auto proving_inputs = deserialize_from_msgpack(command.inputs); - - AvmSimAPI api; - auto result = api.simulate_with_hinted_dbs(proving_inputs); - - respond.ok(wire::AvmSimulateWithHintsResponse{ .result = serialize_to_msgpack(result) }); + respond.ok(wire::AvmSimulateWithHintsResponse{ .result = simulate_with_hints(command.inputs) }); } catch (const std::exception& e) { respond.error(e.what()); } diff --git a/barretenberg/cpp/src/barretenberg/avm/avm_execute.hpp b/barretenberg/cpp/src/barretenberg/avm/avm_execute.hpp index 37f2a85dd5c..c9a5764eb5c 100644 --- a/barretenberg/cpp/src/barretenberg/avm/avm_execute.hpp +++ b/barretenberg/cpp/src/barretenberg/avm/avm_execute.hpp @@ -19,11 +19,12 @@ extern std::atomic g_active_cancellation_t /** * @brief Context passed to each command's execute() method. - * Provides access to WSDB and CDB IPC clients. + * Provides access to WSDB and CDB IPC clients. Both are null when bb-avm-sim runs without them, which leaves + * SimulateWithHints alone working. */ struct AvmRequest { - cdb::CdbIpcContractDB& cdb_client; - wsdb::WsdbIpcClient& wsdb_client; + cdb::CdbIpcContractDB* cdb_client = nullptr; + wsdb::WsdbIpcClient* wsdb_client = nullptr; }; } // namespace bb::avm diff --git a/barretenberg/cpp/src/barretenberg/avm/avm_ffi.test.cpp b/barretenberg/cpp/src/barretenberg/avm/avm_ffi.test.cpp new file mode 100644 index 00000000000..371be43aaca --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/avm_ffi.test.cpp @@ -0,0 +1,112 @@ +#include "barretenberg/avm/generated/avm_ffi.hpp" +#include "barretenberg/avm/minimal_tx_fixture.hpp" +#include "barretenberg/serialize/msgpack_impl.hpp" +#include "barretenberg/vm2/common/avm_io.hpp" +#include "barretenberg/vm2/common/tagged_value.hpp" + +#include + +#include +#include + +using namespace bb; +using namespace bb::avm; + +namespace { + +const std::vector MINIMAL_TX(std::begin(MINIMAL_TX_FIXTURE), std::end(MINIMAL_TX_FIXTURE)); + +/** A decoded response frame: its tag, and the payload (or the service's error message). */ +struct Response { + std::string tag; + std::vector result; + std::string error; +}; + +/** Call the service through the generated in-process FFI entry: the path the wasm transport takes. */ +Response call_ffi(const char* name, const auto& cmd) +{ + msgpack::sbuffer buf; + msgpack::packer pk(buf); + pk.pack_array(1); + pk.pack_array(2); + pk.pack(std::string(name)); + pk.pack(cmd); + + uint8_t* output = nullptr; + size_t output_len = 0; + avm_ipc_ffi_entry(reinterpret_cast(buf.data()), buf.size(), &output, &output_len); + auto unpacked = msgpack::unpack(reinterpret_cast(output), output_len); + avm_ipc_ffi_free(output); + + auto obj = unpacked.get(); + Response response{ .tag = std::string(obj.via.array.ptr[0].via.str.ptr, obj.via.array.ptr[0].via.str.size) }; + if (response.tag == "AvmErrorResponse") { + wire::AvmErrorResponse error; + obj.via.array.ptr[1].convert(error); + response.error = error.message; + } else { + wire::AvmSimulateWithHintsResponse ok; + obj.via.array.ptr[1].convert(ok); + response.result = ok.result; + } + return response; +} + +} // namespace + +// The fixture carries the public inputs the transaction produced, so the simulation's outputs can be checked against +// them. A build where a wider type narrows (size_t is 32-bit on wasm32) gets them wrong. +TEST(AvmFfi, SimulateWithHintsMatchesTheFixturesPublicInputs) +{ + auto response = call_ffi("AvmSimulateWithHints", wire::AvmSimulateWithHints{ .inputs = MINIMAL_TX }); + ASSERT_EQ(response.tag, "AvmSimulateWithHintsResponse") << response.error; + + avm2::TxSimulationResult result; + msgpack::unpack(reinterpret_cast(response.result.data()), response.result.size()) + .get() + .convert(result); + auto expected = avm2::AvmProvingInputs::from(MINIMAL_TX).public_inputs; + EXPECT_EQ(result.revert_code == avm2::RevertCode::OK, !expected.reverted); + EXPECT_EQ(result.gas_used.total_gas, expected.end_gas_used); + EXPECT_EQ(result.public_tx_effect.transaction_fee, expected.transaction_fee); + EXPECT_EQ(result.public_tx_effect.note_hashes.size(), expected.accumulated_data_array_lengths.note_hashes); + EXPECT_EQ(result.public_tx_effect.nullifiers.size(), expected.accumulated_data_array_lengths.nullifiers); + EXPECT_EQ(result.public_tx_effect.public_data_writes.size(), + expected.accumulated_data_array_lengths.public_data_writes); +} + +// The exceptions the simulator signals exceptional halts with are caught where it expects, including in wasm. +TEST(AvmFfi, SimulatorExceptionsAreCaught) +{ + EXPECT_THROW(avm2::TaggedValue::from(1) + avm2::TaggedValue::from(1), + avm2::TagMismatchException); +} + +// Inputs that fail to decode throw inside the simulator, and the entry answers with an error rather than ending the +// module, so the next call still succeeds. +TEST(AvmFfi, MalformedInputsGetAnErrorAndTheEntryKeepsAnswering) +{ + auto bad = call_ffi("AvmSimulateWithHints", wire::AvmSimulateWithHints{ .inputs = { 1, 2, 3 } }); + EXPECT_EQ(bad.tag, "AvmErrorResponse"); + EXPECT_FALSE(bad.error.empty()); + + auto good = call_ffi("AvmSimulateWithHints", wire::AvmSimulateWithHints{ .inputs = MINIMAL_TX }); + EXPECT_EQ(good.tag, "AvmSimulateWithHintsResponse") << good.error; +} + +TEST(AvmFfi, SimulateAnswersWithAnError) +{ + auto response = call_ffi("AvmSimulate", wire::AvmSimulate{ .inputs = MINIMAL_TX }); + EXPECT_EQ(response.tag, "AvmErrorResponse"); + EXPECT_NE(response.error.find("SimulateWithHints"), std::string::npos) << response.error; +} + +// wasm does not find main() in gtest_main (see ecc_tests' secp256r1.test.cpp). +#ifdef __wasm__ +GTEST_API_ int main(int argc, char** argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +#endif diff --git a/barretenberg/cpp/src/barretenberg/avm/avm_ffi_dispatcher.cpp b/barretenberg/cpp/src/barretenberg/avm/avm_ffi_dispatcher.cpp new file mode 100644 index 00000000000..a76d52f9eab --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/avm_ffi_dispatcher.cpp @@ -0,0 +1,46 @@ +/** + * @file avm_ffi_dispatcher.cpp + * @brief Names the context the AVM handlers run over in the in-process FFI entry (avm_ipc_ffi_entry), the entry + * bb-avm-sim.wasm exports. + * + * The in-process build has no route to a world state or contract DB yet, so it serves SimulateWithHints, whose hints + * answer every read, and answers Simulate with an error. + */ +#include "barretenberg/avm/avm_simulate_with_hints.hpp" +#include "barretenberg/avm/generated/avm_ffi.hpp" + +namespace bb::avm { + +namespace { +struct AvmFfiRequest {}; +} // namespace + +template <> +void handle_simulate(AvmFfiRequest& /*request*/, + wire::AvmSimulate&& /*command*/, + Responder respond) +{ + respond.error("Simulate needs a world state and contract DB, which the in-process bb-avm-sim cannot reach yet; " + "SimulateWithHints needs neither"); +} + +template <> +void handle_simulate_with_hints(AvmFfiRequest& /*request*/, + wire::AvmSimulateWithHints&& command, + Responder respond) +{ + respond.ok(wire::AvmSimulateWithHintsResponse{ .result = simulate_with_hints(command.inputs) }); +} + +/** + * @brief The dispatcher behind the generated in-process FFI entry (see avm_ffi.hpp). The context is stateless, so one + * serves every call. + */ +AsyncDispatchHandler& ipc_ffi_dispatcher() +{ + static AvmFfiRequest request; + static AsyncDispatchHandler handler = make_avm_handler(request); + return handler; +} + +} // namespace bb::avm diff --git a/barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.cpp b/barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.cpp index 197840afd7e..a5830f871aa 100644 --- a/barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.cpp +++ b/barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.cpp @@ -60,9 +60,11 @@ int execute_avm_server(const std::string& input_path, const std::string& wsdb_pa constexpr int max_retries = 600; constexpr int retry_delay_ms = 100; - info("Connecting to aztec-wsdb at ", wsdb_path); std::unique_ptr wsdb_client; - for (int attempt = 0; attempt < max_retries; ++attempt) { + if (!wsdb_path.empty()) { + info("Connecting to aztec-wsdb at ", wsdb_path); + } + for (int attempt = 0; !wsdb_path.empty() && attempt < max_retries; ++attempt) { try { wsdb_client = std::make_unique(wsdb_path); break; @@ -75,9 +77,11 @@ int execute_avm_server(const std::string& input_path, const std::string& wsdb_pa } } - info("Connecting to CDB at ", cdb_path); std::unique_ptr cdb_client; - for (int attempt = 0; attempt < max_retries; ++attempt) { + if (!cdb_path.empty()) { + info("Connecting to CDB at ", cdb_path); + } + for (int attempt = 0; !cdb_path.empty() && attempt < max_retries; ++attempt) { try { cdb_client = std::make_unique(cdb_path); break; @@ -90,7 +94,7 @@ int execute_avm_server(const std::string& input_path, const std::string& wsdb_pa } } - AvmRequest request{ .cdb_client = *cdb_client, .wsdb_client = *wsdb_client }; + AvmRequest request{ .cdb_client = cdb_client.get(), .wsdb_client = wsdb_client.get() }; info("bb-avm-sim IPC server ready"); diff --git a/barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.hpp b/barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.hpp index e7164cf3c13..5a1a967611a 100644 --- a/barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.hpp +++ b/barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.hpp @@ -8,11 +8,12 @@ namespace bb::avm { * @brief Start the bb-avm-sim IPC server. * * Connects to WSDB and CDB as IPC clients, then runs the server loop - * dispatching incoming simulation commands. + * dispatching incoming simulation commands. Without either path it serves + * SimulateWithHints alone, which reads neither. * * @param input_path IPC path for TS client connections. - * @param wsdb_path IPC path to the running WSDB server. - * @param cdb_path IPC path to the running CDB server. + * @param wsdb_path IPC path to the running WSDB server, or empty. + * @param cdb_path IPC path to the running CDB server, or empty. * @return 0 on success, non-zero on error. */ int execute_avm_server(const std::string& input_path, const std::string& wsdb_path, const std::string& cdb_path); diff --git a/barretenberg/cpp/src/barretenberg/avm/avm_simulate_with_hints.cpp b/barretenberg/cpp/src/barretenberg/avm/avm_simulate_with_hints.cpp new file mode 100644 index 00000000000..df0e0de371f --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/avm_simulate_with_hints.cpp @@ -0,0 +1,17 @@ +#include "barretenberg/avm/avm_simulate_with_hints.hpp" +#include "barretenberg/serialize/msgpack.hpp" +#include "barretenberg/serialize/msgpack_impl.hpp" +#include "barretenberg/vm2/avm_sim_api.hpp" +#include "barretenberg/vm2/common/avm_io.hpp" + +namespace bb::avm { + +std::vector simulate_with_hints(const std::vector& inputs) +{ + auto result = avm2::AvmSimAPI().simulate_with_hinted_dbs(avm2::AvmProvingInputs::from(inputs)); + msgpack::sbuffer buf; + msgpack::pack(buf, result); + return { buf.data(), buf.data() + buf.size() }; +} + +} // namespace bb::avm diff --git a/barretenberg/cpp/src/barretenberg/avm/avm_simulate_with_hints.hpp b/barretenberg/cpp/src/barretenberg/avm/avm_simulate_with_hints.hpp new file mode 100644 index 00000000000..afe20ffa34d --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/avm_simulate_with_hints.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +namespace bb::avm { + +/** + * @brief Simulate a transaction from its hinted inputs, reading no world state or contract DB. + * + * @param inputs A msgpack AvmProvingInputs, whose hints answer every read the simulation makes. + * @return The msgpack TxSimulationResult. + */ +std::vector simulate_with_hints(const std::vector& inputs); + +} // namespace bb::avm diff --git a/barretenberg/cpp/src/barretenberg/avm/cli.cpp b/barretenberg/cpp/src/barretenberg/avm/cli.cpp index 4b91ccd44f0..d003e65c976 100644 --- a/barretenberg/cpp/src/barretenberg/avm/cli.cpp +++ b/barretenberg/cpp/src/barretenberg/avm/cli.cpp @@ -25,10 +25,10 @@ int parse_and_run_avm(int argc, char* argv[]) msgpack_run_command->add_option("-i,--input", input_path, "IPC socket/shm path (.sock or .shm)")->required(); std::string wsdb_path; - msgpack_run_command->add_option("--wsdb", wsdb_path, "WSDB server IPC path")->required(); + msgpack_run_command->add_option("--wsdb", wsdb_path, "WSDB server IPC path (Simulate needs it)"); std::string cdb_path; - msgpack_run_command->add_option("--cdb", cdb_path, "CDB server IPC path")->required(); + msgpack_run_command->add_option("--cdb", cdb_path, "CDB server IPC path (Simulate needs it)"); // Parse CLI try { diff --git a/barretenberg/cpp/src/barretenberg/bbapi/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/bbapi/CMakeLists.txt index 423d12aea02..a2303f91764 100644 --- a/barretenberg/cpp/src/barretenberg/bbapi/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/bbapi/CMakeLists.txt @@ -25,7 +25,7 @@ if(NOT FUZZING) # --ffi emits the in-process entry (bb_ipc_ffi_entry) bb.js's wasm backend and barretenberg-rs call. add_custom_command( OUTPUT ${BB_GEN_OUTPUTS} - COMMAND node --experimental-strip-types --experimental-transform-types --no-warnings + COMMAND node --experimental-strip-types --no-warnings ${IPC_CODEGEN_DIR}/src/generate.ts --schema ${BB_SCHEMA} --lang cpp diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp index 98b5e0dc69c..0112f0bb328 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp @@ -615,7 +615,7 @@ template class ShplonkVerifier_ { * */ template -static std::vector compute_shplonk_batching_challenge_powers(const Fr& shplonk_batching_challenge, +inline std::vector compute_shplonk_batching_challenge_powers(const Fr& shplonk_batching_challenge, const size_t virtual_log_n, bool has_zk = false, bool committed_sumcheck = false) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/small_subgroup_ipa/small_subgroup_ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/small_subgroup_ipa/small_subgroup_ipa.hpp index 7422a1f9e86..ce188e77b32 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/small_subgroup_ipa/small_subgroup_ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/small_subgroup_ipa/small_subgroup_ipa.hpp @@ -420,7 +420,7 @@ template class SmallSubgroupIPAVerifier { * @return Polynomial */ template -static std::vector compute_challenge_polynomial_coeffs( +inline std::vector compute_challenge_polynomial_coeffs( const std::vector& multivariate_challenge) { diff --git a/barretenberg/cpp/src/barretenberg/common/ref_vector.hpp b/barretenberg/cpp/src/barretenberg/common/ref_vector.hpp index bfc92e13055..d4f1b63d5ec 100644 --- a/barretenberg/cpp/src/barretenberg/common/ref_vector.hpp +++ b/barretenberg/cpp/src/barretenberg/common/ref_vector.hpp @@ -173,7 +173,7 @@ template RefVector concatenate(const RefVector& ref_vector, c } /* @details Ensures a nested vector holds reference objects */ -template static std::vector> to_vector_of_ref_vectors(std::vector>& vec) +template inline std::vector> to_vector_of_ref_vectors(std::vector>& vec) { std::vector> result; for (std::vector& inner : vec) { diff --git a/barretenberg/cpp/src/barretenberg/common/throw_or_abort.hpp b/barretenberg/cpp/src/barretenberg/common/throw_or_abort.hpp index c6856f40c26..eb790bee091 100644 --- a/barretenberg/cpp/src/barretenberg/common/throw_or_abort.hpp +++ b/barretenberg/cpp/src/barretenberg/common/throw_or_abort.hpp @@ -1,9 +1,16 @@ #pragma once #include "barretenberg/env/throw_or_abort_impl.hpp" #include "log.hpp" +#include #include inline void throw_or_abort [[noreturn]] (std::string const& err) { +#if defined(__wasm__) && !defined(BB_NO_EXCEPTIONS) + // Throw inside the module, so catch blocks such as bbapi's error responses see it. throw_or_abort_impl ends the + // call without running them or any destructors. + throw std::runtime_error(err); +#else throw_or_abort_impl(err.c_str()); +#endif } diff --git a/barretenberg/cpp/src/barretenberg/common/utils.cpp b/barretenberg/cpp/src/barretenberg/common/utils.cpp index 312ccb9a377..f55167a5273 100644 --- a/barretenberg/cpp/src/barretenberg/common/utils.cpp +++ b/barretenberg/cpp/src/barretenberg/common/utils.cpp @@ -1,5 +1,7 @@ #include "./utils.hpp" +#include + namespace bb::utils { std::vector hex_to_bytes(const std::string& hex) diff --git a/barretenberg/cpp/src/barretenberg/crypto/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/crypto/CMakeLists.txt index a2911b6b687..63c1926142f 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/crypto/CMakeLists.txt @@ -12,6 +12,4 @@ add_subdirectory(ecdsa) add_subdirectory(aes128) add_subdirectory(poseidon2) -if (NOT WASM) - add_subdirectory(merkle_tree) -endif() +add_subdirectory(merkle_tree) diff --git a/barretenberg/cpp/src/barretenberg/crypto/schnorr/schnorr.tcc b/barretenberg/cpp/src/barretenberg/crypto/schnorr/schnorr.tcc index 16fe624c72d..b8f1d3d2eef 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/schnorr/schnorr.tcc +++ b/barretenberg/cpp/src/barretenberg/crypto/schnorr/schnorr.tcc @@ -51,7 +51,7 @@ template static inline Fq compute_schnorr_challenge_dst() * @return e as a bb::fr element */ template -static typename G1::Fq schnorr_generate_challenge(const typename G1::Fq& message_field, +inline typename G1::Fq schnorr_generate_challenge(const typename G1::Fq& message_field, const typename G1::affine_element& pubkey, const typename G1::affine_element& R) { diff --git a/barretenberg/cpp/src/barretenberg/vm2/CLAUDE.md b/barretenberg/cpp/src/barretenberg/vm2/CLAUDE.md index f6cf8749745..147f92012fe 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/CLAUDE.md +++ b/barretenberg/cpp/src/barretenberg/vm2/CLAUDE.md @@ -60,6 +60,8 @@ Configure once: `cmake --preset clang20-assert`. All commands below are from `ba - Build: `cmake --build --preset clang20-assert --target vm2_tests`. Run from `barretenberg/cpp/build`: `./bin/vm2_tests --gtest_filter="*test_name*"`. 3. **`bb-avm-sim`** — Simulation-only IPC server that TS drives. - Build: `cmake --build --preset clang20-assert --target bb-avm-sim`. For TS: from `barretenberg/cpp/`, run `(cd ../ts && ./bootstrap.sh build_bb_avm_sim)` then bootstrap `yarn-project` (see yarn-project/CLAUDE.md). +4. **`bb-avm-sim.wasm`**: the same service in-process, the `@aztec-foundation/bb-avm-sim` package's wasm backend. It serves `SimulateWithHints` only, since it has no route to world state or contract data yet. + - Build: `cmake --preset wasm && cmake --build --preset wasm` (also builds `avm_ffi_tests`). Test: `SINGLE_THREADED=1 scripts/wasmtime.sh build-wasm/bin/avm_ffi_tests`, and natively `./build/bin/avm_ffi_tests`. ## AVM and TypeScript diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/retrieved_bytecodes_tree_check.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/retrieved_bytecodes_tree_check.cpp index cb96ae66227..7ab61f3718a 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/retrieved_bytecodes_tree_check.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/retrieved_bytecodes_tree_check.cpp @@ -17,8 +17,8 @@ bool RetrievedBytecodesTreeCheck::contains(const FF& class_id) { const auto snapshot = tree.get_snapshot(); auto [exists, low_leaf_index] = tree.get_low_indexed_leaf(class_id); - auto sibling_path = tree.get_sibling_path(low_leaf_index); - auto low_leaf_preimage = tree.get_leaf_preimage(low_leaf_index); + auto sibling_path = tree.get_sibling_path(static_cast(low_leaf_index)); + auto low_leaf_preimage = tree.get_leaf_preimage(static_cast(low_leaf_index)); indexed_tree_check.assert_read(class_id, std::nullopt, // No siloing diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/to_radix.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/to_radix.cpp index 232d7ebac52..7976e78a186 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/to_radix.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/to_radix.cpp @@ -50,7 +50,7 @@ std::pair, /* truncated */ bool> ToRadix::to_le_radix(const bool truncated = num_limbs < limbs.size(); if (truncated) { - limbs.erase(limbs.begin() + num_limbs, limbs.end()); + limbs.erase(limbs.begin() + static_cast(num_limbs), limbs.end()); } return { limbs, truncated }; diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/written_public_data_slots_tree_check.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/written_public_data_slots_tree_check.cpp index e7908f33077..32b0d6e8e12 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/written_public_data_slots_tree_check.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/written_public_data_slots_tree_check.cpp @@ -24,8 +24,8 @@ bool WrittenPublicDataSlotsTreeCheck::contains(const AztecAddress& contract_addr const auto& tree = written_public_data_slots_tree_stack.top(); const auto snapshot = tree.get_snapshot(); auto [exists, low_leaf_index] = tree.get_low_indexed_leaf(leaf_slot); - auto sibling_path = tree.get_sibling_path(low_leaf_index); - auto low_leaf_preimage = tree.get_leaf_preimage(low_leaf_index); + auto sibling_path = tree.get_sibling_path(static_cast(low_leaf_index)); + auto low_leaf_preimage = tree.get_leaf_preimage(static_cast(low_leaf_index)); indexed_tree_check.assert_read(slot, IndexedTreeSiloingParameters{ diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/contract_crypto.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/contract_crypto.cpp index d74895f1def..370aa4bf476 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/contract_crypto.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/contract_crypto.cpp @@ -58,7 +58,7 @@ FF compute_public_bytecode_first_field(size_t bytecode_size) // Fr<0x00000000000000000000000000000000000000000000000000016b480f8411f1> From: max fields in bytes = 3000 * 31 = // 16b48, Dom sep = f8411f1 static_assert(DOM_SEP__PUBLIC_BYTECODE <= UINT32_MAX, "Public bytecode domain separator must fit in 32 bits"); - return FF(uint256_t(DOM_SEP__PUBLIC_BYTECODE) + uint256_t(bytecode_size << 32)); + return FF(uint256_t(DOM_SEP__PUBLIC_BYTECODE) + (uint256_t(bytecode_size) << 32)); } FF compute_public_bytecode_commitment(std::span bytecode) diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/indexed_memory_tree.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/indexed_memory_tree.hpp index 0bd01838ba0..c2575fb2298 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/indexed_memory_tree.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/indexed_memory_tree.hpp @@ -150,20 +150,22 @@ SequentialInsertionResult IndexedMemoryTree:: for (const auto& leaf_to_insert : leaves_to_insert) { FF key = leaf_to_insert.get_key(); GetLowIndexedLeafResponse find_low_leaf_result = get_low_indexed_leaf(key); - IndexedLeaf& low_leaf = leaves.at(find_low_leaf_result.index); + // index_t is 64-bit; size_t is 32-bit on wasm32. An index into this in-memory tree always fits. + const auto low_index = static_cast(find_low_leaf_result.index); + IndexedLeaf& low_leaf = leaves.at(low_index); - result.low_leaf_witness_data.push_back(LeafUpdateWitnessData( - low_leaf, find_low_leaf_result.index, tree.get_sibling_path(find_low_leaf_result.index))); + result.low_leaf_witness_data.push_back( + LeafUpdateWitnessData(low_leaf, find_low_leaf_result.index, tree.get_sibling_path(low_index))); if (!find_low_leaf_result.is_already_present) { // If the leaf is not already present, we point the low leaf to the new leaf and then insert the new leaf. IndexedLeaf new_indexed_leaf(leaf_to_insert, low_leaf.nextIndex, low_leaf.nextKey); - index_t insertion_index = leaves.size(); + size_t insertion_index = leaves.size(); low_leaf.nextIndex = insertion_index; low_leaf.nextKey = key; FF low_leaf_hash = HashingPolicy::hash(low_leaf.get_hash_inputs()); - tree.update_element(find_low_leaf_result.index, low_leaf_hash); + tree.update_element(low_index, low_leaf_hash); append_leaf(new_indexed_leaf); FF new_leaf_hash = HashingPolicy::hash(new_indexed_leaf.get_hash_inputs()); @@ -176,7 +178,7 @@ SequentialInsertionResult IndexedMemoryTree:: // Update the current leaf's value, don't change it's link low_leaf = IndexedLeaf(leaf_to_insert, low_leaf.nextIndex, low_leaf.nextKey); FF low_leaf_hash = HashingPolicy::hash(low_leaf.get_hash_inputs()); - tree.update_element(find_low_leaf_result.index, low_leaf_hash); + tree.update_element(low_index, low_leaf_hash); // Push an empty insertion witness result.insertion_witness_data.push_back( diff --git a/barretenberg/cpp/src/barretenberg/vm2/tooling/stats.cpp b/barretenberg/cpp/src/barretenberg/vm2/tooling/stats.cpp index 7f4113e93c7..548cf658c2f 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/tooling/stats.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/tooling/stats.cpp @@ -1,5 +1,6 @@ #include "barretenberg/vm2/tooling/stats.hpp" +#include #include #include #include diff --git a/barretenberg/cpp/src/barretenberg/wasi/wasm_env.cpp b/barretenberg/cpp/src/barretenberg/wasi/wasm_env.cpp index 8f2e074fe37..e83010746ed 100644 --- a/barretenberg/cpp/src/barretenberg/wasi/wasm_env.cpp +++ b/barretenberg/cpp/src/barretenberg/wasi/wasm_env.cpp @@ -34,8 +34,9 @@ WASM_EXPORT uint32_t env_hardware_concurrency() } /** - * The wasm build compiles without exceptions, so a throw cannot unwind: report and exit. The - * embedder sees proc_exit, which its WASI implementation turns back into an exception. + * Only a build without exceptions (BB_NO_EXCEPTIONS) reaches this, since throw_or_abort throws + * otherwise. A throw cannot unwind there: report and exit. The embedder sees proc_exit, which its + * WASI implementation turns back into an exception. */ WASM_EXPORT void throw_or_abort_impl [[noreturn]] (char const* err) { diff --git a/barretenberg/cpp/src/barretenberg/world_state_reference/memory_merkle_db.hpp b/barretenberg/cpp/src/barretenberg/world_state_reference/memory_merkle_db.hpp index 03dc055724d..5e9046d8328 100644 --- a/barretenberg/cpp/src/barretenberg/world_state_reference/memory_merkle_db.hpp +++ b/barretenberg/cpp/src/barretenberg/world_state_reference/memory_merkle_db.hpp @@ -103,7 +103,7 @@ template class MemoryIndexedTree { leaves_.push_back(Leaf(LeafType::padding(i), /*nextIndex=*/0, /*nextKey=*/0)); } for (size_t i = 0; i < initial_size; ++i) { - index_t next_index = i == (initial_size - 1) ? 0 : i + 1; + size_t next_index = i == (initial_size - 1) ? 0 : i + 1; leaves_[i].nextIndex = next_index; leaves_[i].nextKey = leaves_[next_index].leaf.get_key(); tree_.update_element(i, HashingPolicy::hash(leaves_[i].get_hash_inputs())); @@ -131,7 +131,7 @@ template class MemoryIndexedTree { Leaf get_leaf_preimage(index_t leaf_index) const { BB_ASSERT_LT(leaf_index, leaves_.size(), "Leaf index out of bounds"); - return leaves_[leaf_index]; + return leaves_[static_cast(leaf_index)]; } FF get_leaf_value(index_t leaf_index) const { return tree_.get_node(0, leaf_index); } @@ -149,7 +149,7 @@ template class MemoryIndexedTree { FF key = leaf_to_insert.get_key(); GetLowIndexedLeafResponse find_low_leaf_result = get_low_indexed_leaf(key); - Leaf& low_leaf = leaves_[find_low_leaf_result.index]; + Leaf& low_leaf = leaves_[static_cast(find_low_leaf_result.index)]; result.low_leaf_witness_data.emplace_back( low_leaf, find_low_leaf_result.index, tree_.get_sibling_path(find_low_leaf_result.index)); diff --git a/barretenberg/rust/bootstrap.sh b/barretenberg/rust/bootstrap.sh index b49097e57f4..445dcff0d68 100755 --- a/barretenberg/rust/bootstrap.sh +++ b/barretenberg/rust/bootstrap.sh @@ -11,7 +11,7 @@ hash=$(hash_str $(cache_content_hash .rebuild_patterns)) # generated client never depends on hand-maintained copies of them. function generate { local root=$(git rev-parse --show-toplevel) - (cd barretenberg-rs && node --experimental-strip-types --experimental-transform-types --no-warnings \ + (cd barretenberg-rs && node --experimental-strip-types --no-warnings \ "$root/ipc-codegen/src/generate.ts" \ --schema "$root/barretenberg/cpp/src/barretenberg/bbapi/bb_schema.json" \ --lang rust \ diff --git a/barretenberg/ts/bb.js/src/backends/wasm.test.ts b/barretenberg/ts/bb.js/src/backends/wasm.test.ts index 821eaf106e7..7bf982c2326 100644 --- a/barretenberg/ts/bb.js/src/backends/wasm.test.ts +++ b/barretenberg/ts/bb.js/src/backends/wasm.test.ts @@ -47,8 +47,8 @@ describe('wasm backends', () => { }, 60000); it('surfaces bb errors as exceptions, with the message bb gave', () => { - // Buffers too small for the point count they claim: bb throws inside from_buffer, and under - // BB_NO_EXCEPTIONS (which wasm builds with) that becomes an abort the host turns into a throw. + // Buffers too small for the point count they claim: bb throws inside from_buffer, and the + // dispatcher's catch turns that into an error response. const tooSmall = { numPoints: 100, pointsBuf: new Uint8Array(10), g2Point: new Uint8Array(10) }; return BarretenbergSync.new({ backend: BackendType.Wasm }).then(api => { try { diff --git a/barretenberg/ts/bb.js/src/barretenberg/errors.test.ts b/barretenberg/ts/bb.js/src/barretenberg/errors.test.ts index 5ce81b2c824..74843191df0 100644 --- a/barretenberg/ts/bb.js/src/barretenberg/errors.test.ts +++ b/barretenberg/ts/bb.js/src/barretenberg/errors.test.ts @@ -16,7 +16,7 @@ function reportedFailure(api: Barretenberg) { /** * A failure bb raises with throw_or_abort: a compressed SRS buffer that is not chunk-aligned. - * The handler throws rather than returning, which is the case the two backends diverge on. + * The handler throws rather than returning. */ function thrownFailure(api: Barretenberg) { return api.srsInitSrs({ pointsBuf: new Uint8Array(32), numPoints: 1, g2Point: new Uint8Array(128) }); @@ -44,20 +44,14 @@ describe('command errors', () => { } }); - it('reports a thrown error as BbError on native', async () => { - const err = await thrownFailure(native).catch(e => e); - expect(err).toBeInstanceOf(BbError); - expect(err.message).toMatch(/must be a positive multiple of/); - }); - - // bb's wasm build compiles with BB_NO_EXCEPTIONS, so the dispatcher's catch is compiled away and - // a throw cannot unwind: bb writes the reason to stderr and exits, and ipc-runtime turns that - // into an error carrying the text. The message survives; the type does not. Pinned so the - // divergence cannot change silently — catch Error to handle both. - it('reports a thrown error as a plain Error on wasm', async () => { - const err = await thrownFailure(wasm).catch(e => e); - expect(err).toBeInstanceOf(Error); - expect(err).not.toBeInstanceOf(BbError); - expect(err.message).toMatch(/must be a positive multiple of/); + // The wasm build catches C++ exceptions, so a throw reaches the dispatcher's catch and becomes an + // error response there too, and the module keeps answering afterwards. + it('reports a thrown error as BbError on both backends', async () => { + for (const api of [native, wasm]) { + const err = await thrownFailure(api).catch(e => e); + expect(err).toBeInstanceOf(BbError); + expect(err.message).toMatch(/must be a positive multiple of/); + await expect(reportedFailure(api)).rejects.toThrow(/must be on the curve/); + } }); }); diff --git a/barretenberg/ts/bb.js/src/index.ts b/barretenberg/ts/bb.js/src/index.ts index 2f6494675f1..77aeabb019c 100644 --- a/barretenberg/ts/bb.js/src/index.ts +++ b/barretenberg/ts/bb.js/src/index.ts @@ -21,11 +21,6 @@ export { splitHonkProof, reconstructHonkProof, deflattenFields, type ProofData } * `BbError` is thrown when bb answers a command with an error rather than a result. It comes from * the generated API, so the async and sync classes throw the same one. * - * One failure does not raise it: a handler that reports by throwing (throw_or_abort) rather than by - * returning an error response gives a plain Error on the wasm backends, because bb's wasm build - * compiles with BB_NO_EXCEPTIONS and the throw reaches the host's hook instead of the dispatcher's - * catch. The message is the same either way, so catch Error to handle both. - * * BBApiException is the name the previous API used, kept so existing callers keep compiling. */ export { BbError, BbError as BBApiException } from '@aztec-foundation/bb.js-api'; diff --git a/barretenberg/ts/bootstrap.sh b/barretenberg/ts/bootstrap.sh index 45dc22e314e..e64e4c7ee9f 100755 --- a/barretenberg/ts/bootstrap.sh +++ b/barretenberg/ts/bootstrap.sh @@ -31,8 +31,9 @@ function generate_bb_avm_sim_package { --binary-name "$BB_AVM_SIM_BINARY" \ --prefix Avm \ --strip-method-prefix \ - --package-transports uds \ - --package-ipc-path-args 'msgpack,run,--input,{path}' + --package-transports uds,wasm \ + --package-ipc-path-args 'msgpack,run,--input,{path}' \ + --package-wasm-module bb-avm-sim.wasm } # Server binding package for the AVM CDB protocol: generated wire types + @@ -54,7 +55,7 @@ function generate_bb_js_api_package { local bbapi="$ROOT/barretenberg/cpp/src/barretenberg/bbapi" # bb.js keeps its historical API surface (poseidon2Hash, Poseidon2Hash), so the Bb # service prefix is stripped from identifiers; wire tags keep it. - node --experimental-strip-types --experimental-transform-types --no-warnings \ + node --experimental-strip-types --no-warnings \ "$ROOT/ipc-codegen/src/generate.ts" \ --schema "$bbapi/bb_schema.json" \ --lang ts \ @@ -139,6 +140,13 @@ function build_bb_js_api { prepare_bb_js_api_arch_packages "$(arch)-$(os)=build/$(arch)-$(os)/bb" } +# The single-thread module only: the simulator runs no threads. +function copy_bb_avm_sim_wasm { + rm -rf bb-avm-sim/wasm + mkdir -p bb-avm-sim/wasm + cp "$ROOT/barretenberg/cpp/build-wasm/bin/bb-avm-sim.wasm" bb-avm-sim/wasm/bb-avm-sim.wasm +} + function copy_bb_avm_sim_native { local target_dir="bb-avm-sim/build/$(arch)-$(os)" mkdir -p "$target_dir" @@ -173,6 +181,7 @@ function build_bb_js { function build_bb_avm_sim { echo_header "bb-avm-sim package build" generate_packages + copy_bb_avm_sim_wasm copy_bb_avm_sim_native npm_install_deps "$IPC_RUNTIME_PKG" yarn workspace "$BB_AVM_SIM_PACKAGE" build @@ -230,8 +239,17 @@ function build { build_cdb } +# `test_cmds bb_avm_sim` lists the bb-avm-sim package's tests alone: they need its build +# (bb-avm-sim), which bb.js's tests do not. function test_cmds { - (cd bb.js && ./bootstrap.sh test_cmds) + case "${1:-}" in + bb_avm_sim) + echo "$hash barretenberg/ts/scripts/bb_avm_sim_test.sh" + ;; + *) + (cd bb.js && ./bootstrap.sh test_cmds) + ;; + esac } function bench_cmds { @@ -280,6 +298,7 @@ function get_projects { function release_bb_avm_sim { generate_packages + copy_bb_avm_sim_wasm copy_bb_avm_sim_native copy_bb_avm_sim_cross npm_install_deps "$IPC_RUNTIME_PKG" @@ -336,7 +355,7 @@ function release { release_bb_bin } -export -f generate_bb_avm_sim_package copy_bb_avm_sim_native copy_bb_avm_sim_cross generate_cdb_package generate_packages +export -f generate_bb_avm_sim_package copy_bb_avm_sim_wasm copy_bb_avm_sim_native copy_bb_avm_sim_cross generate_cdb_package generate_packages export -f generate_bb_js_api_package copy_bb_js_api_wasm copy_bb_js_api_native copy_bb_js_api_cross prepare_bb_js_api_arch_packages build_bb_js_api_ts build_bb_js_api export -f build_bb_js build_bb_avm_sim build_cdb build cross_copy_bb_js cross_copy_bb_avm_sim cross_copy_bb_js_api release release_cdb release_bb_js_api diff --git a/barretenberg/ts/scripts/bb_avm_sim.test.mjs b/barretenberg/ts/scripts/bb_avm_sim.test.mjs new file mode 100644 index 00000000000..f63e1faa7de --- /dev/null +++ b/barretenberg/ts/scripts/bb_avm_sim.test.mjs @@ -0,0 +1,94 @@ +// Tests of the generated @aztec-foundation/bb-avm-sim package, from its build: one public transaction through the +// spawned bb-avm-sim binary and through the wasm module (the browser's backend) gives the same result. +import assert from "node:assert/strict"; +import { execFileSync } from "node:child_process"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import { after, before, test } from "node:test"; + +import { + AvmError, + AvmService, + AvmServiceSync, +} from "@aztec-foundation/bb-avm-sim"; +import { unpack } from "msgpackr"; + +const root = execFileSync("git", ["rev-parse", "--show-toplevel"], { + encoding: "utf8", +}).trim(); +// Hinted inputs (AvmProvingInputs), carrying the public inputs the transaction produced. +const inputs = readFileSync( + join( + root, + "barretenberg/cpp/src/barretenberg/vm2/testing/minimal_tx.testdata.bin", + ), +); + +/** The simulation result, less its stats, which include timings. */ +function decodeResult({ result }) { + const { stats, ...rest } = unpack(result); + return rest; +} + +const services = {}; +before(async () => { + // No --wsdb or --cdb: the process then serves SimulateWithHints alone, as the wasm module does. + services.process = await AvmService.create({ backend: "process" }); + services.wasm = await AvmService.create({ backend: "wasm" }); +}); +after(async () => { + await Promise.all( + Object.values(services).map((service) => service.destroy()), + ); +}); + +test("the process and the wasm module give the same result, the one the transaction produced", async () => { + const fromProcess = decodeResult( + await services.process.simulateWithHints({ inputs }), + ); + const fromWasm = decodeResult( + await services.wasm.simulateWithHints({ inputs }), + ); + assert.deepEqual(fromWasm, fromProcess); + + const { publicInputs } = unpack(inputs); + assert.deepEqual(fromWasm.gasUsed.totalGas, publicInputs.endGasUsed); + assert.deepEqual( + Buffer.from(fromWasm.publicTxEffect.transactionFee), + Buffer.from(publicInputs.transactionFee), + ); + assert.equal(fromWasm.revertCode !== 0, publicInputs.reverted); +}); + +test("the synchronous wasm service gives the same result", async () => { + const sync = await AvmServiceSync.create({ backend: "wasm" }); + try { + const fromSync = decodeResult(sync.simulateWithHints({ inputs })); + assert.deepEqual( + fromSync, + decodeResult(await services.process.simulateWithHints({ inputs })), + ); + } finally { + sync.destroy(); + } +}); + +test("a simulation that throws is an AvmError, and the service keeps answering", async () => { + for (const service of Object.values(services)) { + await assert.rejects( + service.simulateWithHints({ inputs: new Uint8Array([1, 2, 3]) }), + AvmError, + ); + decodeResult(await service.simulateWithHints({ inputs })); + } +}); + +test("Simulate, which needs world state and contract data, is an AvmError on both", async () => { + for (const service of Object.values(services)) { + await assert.rejects(service.simulate({ inputs }), (error) => { + assert.ok(error instanceof AvmError); + assert.match(error.message, /Simulate needs/); + return true; + }); + } +}); diff --git a/barretenberg/ts/scripts/bb_avm_sim_test.sh b/barretenberg/ts/scripts/bb_avm_sim_test.sh new file mode 100755 index 00000000000..cdf3e1b9c82 --- /dev/null +++ b/barretenberg/ts/scripts/bb_avm_sim_test.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +# Runs the @aztec-foundation/bb-avm-sim package tests: the spawned bb-avm-sim binary and the wasm module. +# Needs the package built (make bb-avm-sim, or ./bootstrap.sh build_bb_avm_sim here). +set -euo pipefail +cd "$(dirname "$0")/.." +node --test --test-reporter=spec scripts/bb_avm_sim.test.mjs diff --git a/bootstrap.sh b/bootstrap.sh index dde97c8dd0d..faa016dacf0 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -12,10 +12,10 @@ # Expected toolchain versions. export expected_min_clang_version=20.0.0 export expected_min_cmake_version=3.24 -export expected_min_node_version=24.12.0 +export expected_min_node_version=26.12.0 export expected_min_zig_version=0.15.1 export expected_abs_rust_version=1.89.0 -export expected_abs_wasi_version=27.0 +export expected_abs_wasi_version=34.0 export expected_abs_foundry_version=1.8.5 export expected_abs_yarn_version=4.13.0 @@ -66,8 +66,8 @@ function install_node { curl -s -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash . "$HOME/.nvm/nvm.sh" --no-use fi - nvm install --lts - nvm alias default lts/* + nvm install $expected_min_node_version + nvm alias default $expected_min_node_version } function install_node_utils { @@ -204,6 +204,36 @@ function cleanup { } trap cleanup EXIT +### TRANSITIONAL TOOLCHAIN UPGRADE ##################################################################################### +# TODO: Delete this section and its calls (prep, check) once CI runs a build image (and AMI) with the toolchains +# build-images/src/Dockerfile now installs. Until then, upgrade the older image in place on every run: wasi-sdk at +# /opt/wasi-sdk (only builds use it), and node, wasmtime and playwright's chromium under $HOME, which isolated test +# containers mount too, so PATH and PLAYWRIGHT_BROWSERS_PATH are passed through to them. +function transitional_toolchain_upgrade { + [ "$(os)" == "linux" ] && { [ "${CI:-0}" -eq 1 ] || [ "$(whoami)" == "aztec-dev" ]; } || return 0 + local dir=$HOME/.cache/aztec-transitional-toolchains arch=$(uname -m) + local node_version=v$expected_min_node_version wasmtime_version=v46.0.3 playwright_version=1.63.0 + local node_arch=$([ "$arch" == "aarch64" ] && echo arm64 || echo x64) + mkdir -p $dir + install_wasi_sdk + if [ ! -x $dir/node-$node_version/bin/node ]; then + curl -fsSL https://nodejs.org/dist/$node_version/node-$node_version-linux-$node_arch.tar.xz | tar xJ -C $dir + mv $dir/node-$node_version-linux-$node_arch $dir/node-$node_version + fi + if [ ! -x $dir/wasmtime-$wasmtime_version/wasmtime ]; then + curl -fsSL https://github.com/bytecodealliance/wasmtime/releases/download/$wasmtime_version/wasmtime-$wasmtime_version-$arch-linux.tar.xz | tar xJ -C $dir + mv $dir/wasmtime-$wasmtime_version-$arch-linux $dir/wasmtime-$wasmtime_version + fi + export PATH=$dir/node-$node_version/bin:$dir/wasmtime-$wasmtime_version:$PATH + export PLAYWRIGHT_BROWSERS_PATH=$dir/ms-playwright + local out + if ! out=$(npx -y playwright@$playwright_version install chromium --only-shell 2>&1); then + echo "$out" + return 1 + fi + export ENV_VARS_TO_INJECT="${ENV_VARS_TO_INJECT:-} PATH PLAYWRIGHT_BROWSERS_PATH" +} + ### TOOLCHAIN CHECKS ################################################################################################### function check_minimum_version { local min_version=$1 @@ -417,6 +447,7 @@ function stop_txes { } function prep { + transitional_toolchain_upgrade check_toolchains pull_submodules } @@ -853,6 +884,7 @@ case "$cmd" in git clean -ffdx ;; "check") + transitional_toolchain_upgrade check_toolchains echo "Toolchains look good! 🎉" ;; diff --git a/build-images/src/Dockerfile b/build-images/src/Dockerfile index 67dd8ced00c..cd6a6a5e3b2 100644 --- a/build-images/src/Dockerfile +++ b/build-images/src/Dockerfile @@ -61,9 +61,9 @@ RUN curl -L https://foundry.paradigm.xyz | bash \ # We use this image for test isolation. FROM base-build AS build -# APT repo for node (24.15.0) +# APT repo for node (26.12.0) RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg -ENV NODE_MAJOR=24 +ENV NODE_MAJOR=26 RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list RUN apt update && \ @@ -102,7 +102,7 @@ RUN apt update && \ pkg-config \ libssl-dev \ # Node - nodejs=24.15.0-1nodesource1 \ + nodejs=26.12.0-1nodesource1 \ # Python (clang bindings for wasm bindgen.) python3 \ python3-clang \ @@ -121,10 +121,10 @@ RUN wget https://apt.llvm.org/llvm.sh && \ # Install wasi-sdk. RUN arch=$(uname -m) && \ if [ "$arch" = "aarch64" ]; then arch="arm64"; fi && \ - wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-27/wasi-sdk-27.0-${arch}-linux.tar.gz && \ - tar xvf wasi-sdk-27.0-${arch}-linux.tar.gz && \ - mv wasi-sdk-27.0-${arch}-linux /opt/wasi-sdk && \ - rm wasi-sdk-27.0-${arch}-linux.tar.gz + wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-34/wasi-sdk-34.0-${arch}-linux.tar.gz && \ + tar xvf wasi-sdk-34.0-${arch}-linux.tar.gz && \ + mv wasi-sdk-34.0-${arch}-linux /opt/wasi-sdk && \ + rm wasi-sdk-34.0-${arch}-linux.tar.gz # Install foundry. COPY --from=foundry /opt/foundry /opt/foundry @@ -154,9 +154,9 @@ RUN curl -fsSL https://releases.hashicorp.com/terraform/1.7.5/terraform_1.7.5_li && chmod +x /usr/local/bin/terraform \ && rm terraform.zip -# Install wasmtime. -RUN curl -fsSL https://github.com/bytecodealliance/wasmtime/releases/download/v20.0.2/wasmtime-v20.0.2-$(uname -m)-linux.tar.xz | tar xJ \ - && mv wasmtime-v20.0.2-$(uname -m)-linux/wasmtime /usr/local/bin \ +# Install wasmtime. 46 is the last release with wasi-threads (-Sthreads), which the threaded wasm tests use. +RUN curl -fsSL https://github.com/bytecodealliance/wasmtime/releases/download/v46.0.3/wasmtime-v46.0.3-$(uname -m)-linux.tar.xz | tar xJ \ + && mv wasmtime-v46.0.3-$(uname -m)-linux/wasmtime /usr/local/bin \ && rm -rf wasmtime* # Install zig. @@ -171,7 +171,7 @@ RUN curl -sL https://github.com/mikefarah/yq/releases/download/v4.42.1/yq_linux_ -o /usr/local/bin/yq && chmod +x /usr/local/bin/yq # Install playwright for browser testing. Ensure accessible for other users. -RUN npx -y playwright@1.49 install --with-deps && mv /root/.cache/ms-playwright /opt/ms-playwright +RUN npx -y playwright@1.63.0 install --with-deps && mv /root/.cache/ms-playwright /opt/ms-playwright ENV PLAYWRIGHT_BROWSERS_PATH=/opt/ms-playwright @@ -214,7 +214,7 @@ RUN apt update && \ zsh \ inotify-tools \ # Annoyingly unminimize upgrades nodejs. - nodejs=24.15.0-1nodesource1 \ + nodejs=26.12.0-1nodesource1 \ # smt_verification dependencies python3-venv \ python3-pip \ diff --git a/ipc-runtime/ts/scripts/prepare_arch_packages.mjs b/ipc-runtime/ts/scripts/prepare_arch_packages.mjs index 2cf6e38695c..d2cb9e94f89 100755 --- a/ipc-runtime/ts/scripts/prepare_arch_packages.mjs +++ b/ipc-runtime/ts/scripts/prepare_arch_packages.mjs @@ -18,13 +18,14 @@ const PLATFORMS = [ ]; const pkg = JSON.parse(readFileSync('package.json', 'utf8')); -const binaryName = Object.keys(pkg.bin ?? {})[0]; +// The scoped name's last segment, which is what the per-platform directories are named after. +const stem = pkg.name.split('/').pop(); +// A string bin names its command after the package, which is the form yarn rewrites a one-entry map to on install. +const binaryName = typeof pkg.bin === 'string' ? stem : Object.keys(pkg.bin ?? {})[0]; if (!binaryName) { console.error(`prepare_arch_packages: ${pkg.name} declares no bin, so it ships no binary`); process.exit(1); } -// The scoped name's last segment, which is what the per-platform directories are named after. -const stem = pkg.name.split('/').pop(); const overrides = new Map(); for (const arg of process.argv.slice(2)) { diff --git a/labs-patches/0019-test-refresh-contract-expand-snapshots-for-Noir-v1.0.patch b/labs-patches/0019-test-refresh-contract-expand-snapshots-for-Noir-v1.0.patch new file mode 100644 index 00000000000..a6f37ee86cb --- /dev/null +++ b/labs-patches/0019-test-refresh-contract-expand-snapshots-for-Noir-v1.0.patch @@ -0,0 +1,294 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AztecBot +Date: Thu, 15 Oct 2026 15:06:53 +0000 +Subject: [PATCH] test: refresh contract expand snapshots for Noir v1.0.0-rc.5 + + +diff --git a/noir-projects/contract-snapshots/tests/snapshots/expand/amm_contract/snapshots__expanded.snap b/noir-projects/contract-snapshots/tests/snapshots/expand/amm_contract/snapshots__expanded.snap +index 57e67faa6beca7a585d5c7a1701764282d28bab4..457abe20cc0c6f591fa03d56e775777f5c326fb0 100644 +--- a/noir-projects/contract-snapshots/tests/snapshots/expand/amm_contract/snapshots__expanded.snap ++++ b/noir-projects/contract-snapshots/tests/snapshots/expand/amm_contract/snapshots__expanded.snap +@@ -2,6 +2,7 @@ + source: tests/snapshots.rs + expression: stdout + --- ++ + use aztec::macros::aztec; + use aztec::macros::aztec; + +@@ -812,9 +813,9 @@ pub contract AMM { + } + + pub fn offchain_receive(self, messages: BoundedVec) -> aztec::context::calls::UtilityCall<16, 321, ()> { +- let serialized_params: [Field; 321] = as aztec::protocol::traits::Serialize>::serialize(messages); ++ let serialized_params: [Field; (16 * 20) + 1] = as aztec::protocol::traits::Serialize>::serialize(messages); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(1396850735_Field); +- aztec::context::calls::UtilityCall::<16, 321, ()>::new(self.target_contract, selector, "offchain_receive", serialized_params) ++ aztec::context::calls::UtilityCall::<16, (16 * 20) + 1, ()>::new(self.target_contract, selector, "offchain_receive", serialized_params) + } + } + +diff --git a/noir-projects/contract-snapshots/tests/snapshots/expand/avm_gadgets_test_contract/snapshots__expanded.snap b/noir-projects/contract-snapshots/tests/snapshots/expand/avm_gadgets_test_contract/snapshots__expanded.snap +index 47a94ed32414cb0a7d7ae1c8b92ed4339ce135f0..38189194797f7a8c418851b7ccba7229e88d5fe9 100644 +--- a/noir-projects/contract-snapshots/tests/snapshots/expand/avm_gadgets_test_contract/snapshots__expanded.snap ++++ b/noir-projects/contract-snapshots/tests/snapshots/expand/avm_gadgets_test_contract/snapshots__expanded.snap +@@ -161,153 +161,153 @@ contract AvmGadgetsTest { + } + + pub fn keccak_hash(self, data: [u8; 10]) -> aztec::context::calls::PublicCall<11, 10, [u8; 32]> { +- let serialized_params: [Field; 10] = <[u8; 10] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 10 * 1] = <[u8; 10] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(1352369823_Field); +- aztec::context::calls::PublicCall::<11, 10, [u8; 32]>::new(self.target_contract, selector, "keccak_hash", serialized_params) ++ aztec::context::calls::PublicCall::<11, 10 * 1, [u8; 32]>::new(self.target_contract, selector, "keccak_hash", serialized_params) + } + + pub fn keccak_hash_300(self, data: [u8; 300]) -> aztec::context::calls::PublicCall<15, 300, [u8; 32]> { +- let serialized_params: [Field; 300] = <[u8; 300] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 300 * 1] = <[u8; 300] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(3065624740_Field); +- aztec::context::calls::PublicCall::<15, 300, [u8; 32]>::new(self.target_contract, selector, "keccak_hash_300", serialized_params) ++ aztec::context::calls::PublicCall::<15, 300 * 1, [u8; 32]>::new(self.target_contract, selector, "keccak_hash_300", serialized_params) + } + + pub fn keccak_hash_1400(self, data: [u8; 1400]) -> aztec::context::calls::PublicCall<16, 1400, [u8; 32]> { +- let serialized_params: [Field; 1400] = <[u8; 1400] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 1400 * 1] = <[u8; 1400] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(372001522_Field); +- aztec::context::calls::PublicCall::<16, 1400, [u8; 32]>::new(self.target_contract, selector, "keccak_hash_1400", serialized_params) ++ aztec::context::calls::PublicCall::<16, 1400 * 1, [u8; 32]>::new(self.target_contract, selector, "keccak_hash_1400", serialized_params) + } + + pub fn keccak_f1600(self, data: [u64; 25]) -> aztec::context::calls::PublicCall<12, 25, [u64; 25]> { +- let serialized_params: [Field; 25] = <[u64; 25] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 25 * 1] = <[u64; 25] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(1867977279_Field); +- aztec::context::calls::PublicCall::<12, 25, [u64; 25]>::new(self.target_contract, selector, "keccak_f1600", serialized_params) ++ aztec::context::calls::PublicCall::<12, 25 * 1, [u64; 25]>::new(self.target_contract, selector, "keccak_f1600", serialized_params) + } + + pub fn poseidon2_hash(self, data: [Field; 10]) -> aztec::context::calls::PublicCall<14, 10, Field> { +- let serialized_params: [Field; 10] = <[Field; 10] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 10 * 1] = <[Field; 10] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(3724082008_Field); +- aztec::context::calls::PublicCall::<14, 10, Field>::new(self.target_contract, selector, "poseidon2_hash", serialized_params) ++ aztec::context::calls::PublicCall::<14, 10 * 1, Field>::new(self.target_contract, selector, "poseidon2_hash", serialized_params) + } + + pub fn poseidon2_hash_1000fields(self, data: [Field; 1000]) -> aztec::context::calls::PublicCall<25, 1000, Field> { +- let serialized_params: [Field; 1000] = <[Field; 1000] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 1000 * 1] = <[Field; 1000] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(278602445_Field); +- aztec::context::calls::PublicCall::<25, 1000, Field>::new(self.target_contract, selector, "poseidon2_hash_1000fields", serialized_params) ++ aztec::context::calls::PublicCall::<25, 1000 * 1, Field>::new(self.target_contract, selector, "poseidon2_hash_1000fields", serialized_params) + } + + pub fn sha256_hash_10(self, data: [u8; 10]) -> aztec::context::calls::PublicCall<14, 10, [u8; 32]> { +- let serialized_params: [Field; 10] = <[u8; 10] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 10 * 1] = <[u8; 10] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(3545543018_Field); +- aztec::context::calls::PublicCall::<14, 10, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_10", serialized_params) ++ aztec::context::calls::PublicCall::<14, 10 * 1, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_10", serialized_params) + } + + pub fn sha256_hash_20(self, data: [u8; 20]) -> aztec::context::calls::PublicCall<14, 20, [u8; 32]> { +- let serialized_params: [Field; 20] = <[u8; 20] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 20 * 1] = <[u8; 20] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(2926274626_Field); +- aztec::context::calls::PublicCall::<14, 20, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_20", serialized_params) ++ aztec::context::calls::PublicCall::<14, 20 * 1, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_20", serialized_params) + } + + pub fn sha256_hash_30(self, data: [u8; 30]) -> aztec::context::calls::PublicCall<14, 30, [u8; 32]> { +- let serialized_params: [Field; 30] = <[u8; 30] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 30 * 1] = <[u8; 30] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(3609677267_Field); +- aztec::context::calls::PublicCall::<14, 30, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_30", serialized_params) ++ aztec::context::calls::PublicCall::<14, 30 * 1, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_30", serialized_params) + } + + pub fn sha256_hash_40(self, data: [u8; 40]) -> aztec::context::calls::PublicCall<14, 40, [u8; 32]> { +- let serialized_params: [Field; 40] = <[u8; 40] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 40 * 1] = <[u8; 40] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(900925045_Field); +- aztec::context::calls::PublicCall::<14, 40, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_40", serialized_params) ++ aztec::context::calls::PublicCall::<14, 40 * 1, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_40", serialized_params) + } + + pub fn sha256_hash_50(self, data: [u8; 50]) -> aztec::context::calls::PublicCall<14, 50, [u8; 32]> { +- let serialized_params: [Field; 50] = <[u8; 50] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 50 * 1] = <[u8; 50] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(256881939_Field); +- aztec::context::calls::PublicCall::<14, 50, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_50", serialized_params) ++ aztec::context::calls::PublicCall::<14, 50 * 1, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_50", serialized_params) + } + + pub fn sha256_hash_60(self, data: [u8; 60]) -> aztec::context::calls::PublicCall<14, 60, [u8; 32]> { +- let serialized_params: [Field; 60] = <[u8; 60] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 60 * 1] = <[u8; 60] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(2495458303_Field); +- aztec::context::calls::PublicCall::<14, 60, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_60", serialized_params) ++ aztec::context::calls::PublicCall::<14, 60 * 1, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_60", serialized_params) + } + + pub fn sha256_hash_70(self, data: [u8; 70]) -> aztec::context::calls::PublicCall<14, 70, [u8; 32]> { +- let serialized_params: [Field; 70] = <[u8; 70] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 70 * 1] = <[u8; 70] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(2200074453_Field); +- aztec::context::calls::PublicCall::<14, 70, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_70", serialized_params) ++ aztec::context::calls::PublicCall::<14, 70 * 1, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_70", serialized_params) + } + + pub fn sha256_hash_80(self, data: [u8; 80]) -> aztec::context::calls::PublicCall<14, 80, [u8; 32]> { +- let serialized_params: [Field; 80] = <[u8; 80] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 80 * 1] = <[u8; 80] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(4268069756_Field); +- aztec::context::calls::PublicCall::<14, 80, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_80", serialized_params) ++ aztec::context::calls::PublicCall::<14, 80 * 1, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_80", serialized_params) + } + + pub fn sha256_hash_90(self, data: [u8; 90]) -> aztec::context::calls::PublicCall<14, 90, [u8; 32]> { +- let serialized_params: [Field; 90] = <[u8; 90] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 90 * 1] = <[u8; 90] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(799349905_Field); +- aztec::context::calls::PublicCall::<14, 90, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_90", serialized_params) ++ aztec::context::calls::PublicCall::<14, 90 * 1, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_90", serialized_params) + } + + pub fn sha256_hash_100(self, data: [u8; 100]) -> aztec::context::calls::PublicCall<15, 100, [u8; 32]> { +- let serialized_params: [Field; 100] = <[u8; 100] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 100 * 1] = <[u8; 100] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(2074027672_Field); +- aztec::context::calls::PublicCall::<15, 100, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_100", serialized_params) ++ aztec::context::calls::PublicCall::<15, 100 * 1, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_100", serialized_params) + } + + pub fn sha256_hash_255(self, data: [u8; 255]) -> aztec::context::calls::PublicCall<15, 255, [u8; 32]> { +- let serialized_params: [Field; 255] = <[u8; 255] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 255 * 1] = <[u8; 255] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(3548859530_Field); +- aztec::context::calls::PublicCall::<15, 255, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_255", serialized_params) ++ aztec::context::calls::PublicCall::<15, 255 * 1, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_255", serialized_params) + } + + pub fn sha256_hash_256(self, data: [u8; 256]) -> aztec::context::calls::PublicCall<15, 256, [u8; 32]> { +- let serialized_params: [Field; 256] = <[u8; 256] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 256 * 1] = <[u8; 256] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(3409224101_Field); +- aztec::context::calls::PublicCall::<15, 256, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_256", serialized_params) ++ aztec::context::calls::PublicCall::<15, 256 * 1, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_256", serialized_params) + } + + pub fn sha256_hash_511(self, data: [u8; 511]) -> aztec::context::calls::PublicCall<15, 511, [u8; 32]> { +- let serialized_params: [Field; 511] = <[u8; 511] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 511 * 1] = <[u8; 511] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(797967809_Field); +- aztec::context::calls::PublicCall::<15, 511, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_511", serialized_params) ++ aztec::context::calls::PublicCall::<15, 511 * 1, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_511", serialized_params) + } + + pub fn sha256_hash_512(self, data: [u8; 512]) -> aztec::context::calls::PublicCall<15, 512, [u8; 32]> { +- let serialized_params: [Field; 512] = <[u8; 512] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 512 * 1] = <[u8; 512] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(2560938307_Field); +- aztec::context::calls::PublicCall::<15, 512, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_512", serialized_params) ++ aztec::context::calls::PublicCall::<15, 512 * 1, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_512", serialized_params) + } + + pub fn sha256_hash_1024(self, data: [u8; 1024]) -> aztec::context::calls::PublicCall<16, 1024, [u8; 32]> { +- let serialized_params: [Field; 1024] = <[u8; 1024] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 1024 * 1] = <[u8; 1024] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(2235915052_Field); +- aztec::context::calls::PublicCall::<16, 1024, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_1024", serialized_params) ++ aztec::context::calls::PublicCall::<16, 1024 * 1, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_1024", serialized_params) + } + + pub fn sha256_hash_1536(self, data: [u8; 1536]) -> aztec::context::calls::PublicCall<16, 1536, [u8; 32]> { +- let serialized_params: [Field; 1536] = <[u8; 1536] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 1536 * 1] = <[u8; 1536] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(373006257_Field); +- aztec::context::calls::PublicCall::<16, 1536, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_1536", serialized_params) ++ aztec::context::calls::PublicCall::<16, 1536 * 1, [u8; 32]>::new(self.target_contract, selector, "sha256_hash_1536", serialized_params) + } + + pub fn pedersen_hash(self, data: [Field; 10]) -> aztec::context::calls::PublicCall<13, 10, Field> { +- let serialized_params: [Field; 10] = <[Field; 10] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 10 * 1] = <[Field; 10] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(2669406607_Field); +- aztec::context::calls::PublicCall::<13, 10, Field>::new(self.target_contract, selector, "pedersen_hash", serialized_params) ++ aztec::context::calls::PublicCall::<13, 10 * 1, Field>::new(self.target_contract, selector, "pedersen_hash", serialized_params) + } + + pub fn pedersen_hash_with_index(self, data: [Field; 10]) -> aztec::context::calls::PublicCall<24, 10, Field> { +- let serialized_params: [Field; 10] = <[Field; 10] as aztec::protocol::traits::Serialize>::serialize(data); ++ let serialized_params: [Field; 10 * 1] = <[Field; 10] as aztec::protocol::traits::Serialize>::serialize(data); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(3265405934_Field); +- aztec::context::calls::PublicCall::<24, 10, Field>::new(self.target_contract, selector, "pedersen_hash_with_index", serialized_params) ++ aztec::context::calls::PublicCall::<24, 10 * 1, Field>::new(self.target_contract, selector, "pedersen_hash_with_index", serialized_params) + } + + pub fn offchain_receive(self, messages: BoundedVec) -> aztec::context::calls::UtilityCall<16, 321, ()> { +- let serialized_params: [Field; 321] = as aztec::protocol::traits::Serialize>::serialize(messages); ++ let serialized_params: [Field; (16 * 20) + 1] = as aztec::protocol::traits::Serialize>::serialize(messages); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(1396850735_Field); +- aztec::context::calls::UtilityCall::<16, 321, ()>::new(self.target_contract, selector, "offchain_receive", serialized_params) ++ aztec::context::calls::UtilityCall::<16, (16 * 20) + 1, ()>::new(self.target_contract, selector, "offchain_receive", serialized_params) + } + } + +diff --git a/noir-projects/contract-snapshots/tests/snapshots/expand/public_fns_with_emit_repro_contract/snapshots__expanded.snap b/noir-projects/contract-snapshots/tests/snapshots/expand/public_fns_with_emit_repro_contract/snapshots__expanded.snap +index d50c3aa40099acf2f3ed8da3d78e5621320475ec..f625a7c59302f305d9bab183757bbbce7d3e5cf6 100644 +--- a/noir-projects/contract-snapshots/tests/snapshots/expand/public_fns_with_emit_repro_contract/snapshots__expanded.snap ++++ b/noir-projects/contract-snapshots/tests/snapshots/expand/public_fns_with_emit_repro_contract/snapshots__expanded.snap +@@ -276,9 +276,9 @@ pub contract PublicFnsWithEmitRepro { + } + + pub fn offchain_receive(self, messages: BoundedVec) -> aztec::context::calls::UtilityCall<16, 321, ()> { +- let serialized_params: [Field; 321] = as aztec::protocol::traits::Serialize>::serialize(messages); ++ let serialized_params: [Field; (16 * 20) + 1] = as aztec::protocol::traits::Serialize>::serialize(messages); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(1396850735_Field); +- aztec::context::calls::UtilityCall::<16, 321, ()>::new(self.target_contract, selector, "offchain_receive", serialized_params) ++ aztec::context::calls::UtilityCall::<16, (16 * 20) + 1, ()>::new(self.target_contract, selector, "offchain_receive", serialized_params) + } + } + +diff --git a/noir-projects/contract-snapshots/tests/snapshots/expand/storage_proof_test_contract/snapshots__expanded.snap b/noir-projects/contract-snapshots/tests/snapshots/expand/storage_proof_test_contract/snapshots__expanded.snap +index 18bf5a7ed2a6d6678e9ed255e46b05e828677466..d261a437c82b0e7fb86544b26479a80ed096c967 100644 +--- a/noir-projects/contract-snapshots/tests/snapshots/expand/storage_proof_test_contract/snapshots__expanded.snap ++++ b/noir-projects/contract-snapshots/tests/snapshots/expand/storage_proof_test_contract/snapshots__expanded.snap +@@ -232,9 +232,9 @@ contract StorageProofTest { + } + + pub fn offchain_receive(self, messages: BoundedVec) -> aztec::context::calls::UtilityCall<16, 321, ()> { +- let serialized_params: [Field; 321] = messages.serialize(); ++ let serialized_params: [Field; (16 * 20) + 1] = messages.serialize(); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(1396850735_Field); +- aztec::context::calls::UtilityCall::<16, 321, ()>::new(self.target_contract, selector, "offchain_receive", serialized_params) ++ aztec::context::calls::UtilityCall::<16, (16 * 20) + 1, ()>::new(self.target_contract, selector, "offchain_receive", serialized_params) + } + } + +diff --git a/noir-projects/contract-snapshots/tests/snapshots/expand/token_contract/snapshots__expanded.snap b/noir-projects/contract-snapshots/tests/snapshots/expand/token_contract/snapshots__expanded.snap +index 01f2e2f818b549e65dbf14e4704995d8c02d1834..ec4bd524c842c50b0629c47b717fe86a29b34ca3 100644 +--- a/noir-projects/contract-snapshots/tests/snapshots/expand/token_contract/snapshots__expanded.snap ++++ b/noir-projects/contract-snapshots/tests/snapshots/expand/token_contract/snapshots__expanded.snap +@@ -2,6 +2,7 @@ + source: tests/snapshots.rs + expression: stdout + --- ++ + use aztec::macros::aztec; + use aztec::macros::aztec; + +@@ -1016,9 +1017,9 @@ pub contract Token { + } + + pub fn offchain_receive(self, messages: BoundedVec) -> aztec::context::calls::UtilityCall<16, 321, ()> { +- let serialized_params: [Field; 321] = as aztec::protocol::traits::Serialize>::serialize(messages); ++ let serialized_params: [Field; (16 * 20) + 1] = as aztec::protocol::traits::Serialize>::serialize(messages); + let selector: aztec::protocol::abis::function_selector::FunctionSelector = ::from_field(1396850735_Field); +- aztec::context::calls::UtilityCall::<16, 321, ()>::new(self.target_contract, selector, "offchain_receive", serialized_params) ++ aztec::context::calls::UtilityCall::<16, (16 * 20) + 1, ()>::new(self.target_contract, selector, "offchain_receive", serialized_params) + } + } + diff --git a/noir/noir-repo b/noir/noir-repo index 5a7ee9bf5ed..0f759ffe917 160000 --- a/noir/noir-repo +++ b/noir/noir-repo @@ -1 +1 @@ -Subproject commit 5a7ee9bf5ed8973076df7bb0d2b723024db09ae7 +Subproject commit 0f759ffe9176ffbbe7f67b159ac2c3202e0748a0 diff --git a/scripts/setup-container.sh b/scripts/setup-container.sh index a55da9c5ca6..02e6fece714 100644 --- a/scripts/setup-container.sh +++ b/scripts/setup-container.sh @@ -94,14 +94,14 @@ apt-get -y autoremove apt-get clean # ============================================================================= -# SECTION 2: Node.js 24 +# SECTION 2: Node.js 26 # ============================================================================= -log_info "Installing Node.js 24..." +log_info "Installing Node.js 26..." curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg -echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_24.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list +echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_26.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list apt-get update -apt-get install -y nodejs=24.15.0-1nodesource1 +apt-get install -y nodejs=26.12.0-1nodesource1 # ============================================================================= # SECTION 3: Clang 18/20 @@ -141,14 +141,14 @@ chmod +x /etc/profile.d/rust.sh # ============================================================================= # SECTION 5: wasi-sdk # ============================================================================= -log_info "Installing wasi-sdk 27..." +log_info "Installing wasi-sdk 34..." arch=$(uname -m) if [ "$arch" = "aarch64" ]; then arch="arm64"; fi -wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-27/wasi-sdk-27.0-${arch}-linux.tar.gz -tar xvf wasi-sdk-27.0-${arch}-linux.tar.gz -mv wasi-sdk-27.0-${arch}-linux /opt/wasi-sdk -rm wasi-sdk-27.0-${arch}-linux.tar.gz +wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-34/wasi-sdk-34.0-${arch}-linux.tar.gz +tar xvf wasi-sdk-34.0-${arch}-linux.tar.gz +mv wasi-sdk-34.0-${arch}-linux /opt/wasi-sdk +rm wasi-sdk-34.0-${arch}-linux.tar.gz # ============================================================================= # SECTION 6: Foundry @@ -209,8 +209,8 @@ curl -sL https://github.com/ProcursusTeam/ldid/releases/download/v2.1.5-procursu # ============================================================================= log_info "Installing wasmtime..." -curl -fsSL https://github.com/bytecodealliance/wasmtime/releases/download/v20.0.2/wasmtime-v20.0.2-$(uname -m)-linux.tar.xz | tar xJ -mv wasmtime-v20.0.2-$(uname -m)-linux/wasmtime /usr/local/bin +curl -fsSL https://github.com/bytecodealliance/wasmtime/releases/download/v46.0.3/wasmtime-v46.0.3-$(uname -m)-linux.tar.xz | tar xJ +mv wasmtime-v46.0.3-$(uname -m)-linux/wasmtime /usr/local/bin rm -rf wasmtime* # =============================================================================