diff --git a/README.md b/README.md index 9e478a22ea..5a1b4d0ec8 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ The structured reference string contains monomials up to x^{2^20}. This SRS was RUN git clone -b release/10.x --depth 1 https://github.com/llvm/llvm-project.git \ && cd llvm-project && mkdir build-openmp && cd build-openmp \ && cmake ../openmp -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DLIBOMP_ENABLE_SHARED=OFF \ - && make -j$(nproc) \ - && make install \ + && cmake --build . --parallel \ + && cmake --build . --parallel --target install \ && cd ../.. && rm -rf llvm-project ``` @@ -27,12 +27,13 @@ RUN git clone -b release/10.x --depth 1 https://github.com/llvm/llvm-project.git Run the bootstrap script. (The bootstrap script will build both the native and wasm versions of barretenberg) ``` +cd cpp ./bootstrap.sh ``` ### Parallelise the build -Make sure your MAKEFLAGS environment variable is set to run jobs equal to number of cores. e.g. `MAKEFLAGS=-j$(nproc)`. +Use the `--parallel` option to `cmake --build ` to parallelize builds. This is roughly equivalent to `make -j$(nproc)` but is more portable. ### Formatting @@ -44,20 +45,20 @@ If you've installed the C++ Vscode extension you should configure it to format o Each module has its own tests. e.g. To build and run `ecc` tests: ``` -make ecc_tests +cmake --build . --parallel --target ecc_tests ./bin/ecc_tests ``` A shorthand for the above is: ``` -make run_ecc_tests +cmake --build . --parallel --target run_ecc_tests ``` Running the entire suite of tests using `ctest`: ``` -make test +cmake --build . --parallel --target test ``` You can run specific tests, e.g. @@ -71,14 +72,14 @@ You can run specific tests, e.g. Some modules have benchmarks. The build targets are named `_bench`. To build and run, for example `ecc` benchmarks. ``` -make ecc_bench +cmake --build . --parallel --target ecc_bench ./src/aztec/ecc/ecc_bench ``` A shorthand for the above is: ``` -make run_ecc_bench +cmake --build . --parallel --target run_ecc_bench ``` ### CMake Build Options @@ -101,10 +102,10 @@ To build: ``` mkdir build-wasm && cd build-wasm cmake -DTOOLCHAIN=wasm-linux-clang .. -make barretenberg.wasm +cmake --build . --parallel --target barretenberg.wasm ``` -The resulting wasm binary will be at `./src/aztec/barretenberg.wasm`. +The resulting wasm binary will be at `./build-wasm/bin/barretenberg.wasm`. To run the tests, you'll need to install `wasmtime`. @@ -115,7 +116,7 @@ curl https://wasmtime.dev/install.sh -sSf | bash Tests can be built and run like: ``` -make ecc_tests +cmake --build . --parallel --target ecc_tests wasmtime --dir=.. ./bin/ecc_tests ``` @@ -125,11 +126,11 @@ To build: ``` mkdir build-fuzzing && cd build-fuzzing cmake -DTOOLCHAIN=x86_64-linux-clang -DFUZZING=ON .. -make +cmake --build . --parallel ``` Fuzzing build turns off building tests and benchmarks, since they are incompatible with libfuzzer interface. To turn on address sanitizer add `-DADDRESS_SANITIZER=ON`. Note that address sanitizer can be used to explore crashes. Sometimes you might have to specify the address of llvm-symbolizer. You have to do it with `export ASAN_SYMBOLIZER_PATH=`. For undefined behaviour sanitizer `-DUNDEFINED_BEHAVIOUR_SANITIZER=ON`. -Note that the fuzzer can be orders of magnitude slower with ASan (2-3x slower) or UBSan on, so it is best to run a non-sanitized build first, minimize the testcase and then run it for a bit of time with sanitizers. +Note that the fuzzer can be orders of magnitude slower with ASan (2-3x slower) or UBSan on, so it is best to run a non-sanitized build first, minimize the testcase and then run it for a bit of time with sanitizers. \ No newline at end of file diff --git a/cpp/.gitignore b/cpp/.gitignore index e2d5bec647..be056d1bc0 100644 --- a/cpp/.gitignore +++ b/cpp/.gitignore @@ -1,4 +1,4 @@ -./build* +build*/ src/wasi-sdk-* src/aztec/plonk/proof_system/proving_key/fixtures src/aztec/rollup/proofs/*/fixtures diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 5805f2db92..83f003f258 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -40,11 +40,12 @@ if(FUZZING) if(UNDEFINED_BEHAVIOUR_SANITIZER) set(SANITIZER_OPTIONS ${SANITIZER_OPTIONS} -fsanitize=undefined -fno-sanitize=alignment) endif() - + add_compile_options(-fsanitize=fuzzer-no-link ${SANITIZER_OPTIONS}) set(WASM OFF) set(BENCHMARKS OFF) + set(MULTITHREADING OFF) set(TESTING OFF) endif() @@ -68,4 +69,4 @@ include(cmake/gtest.cmake) include(cmake/benchmark.cmake) include(cmake/module.cmake) -add_subdirectory(src/aztec) \ No newline at end of file +add_subdirectory(src/aztec) diff --git a/cpp/bootstrap.sh b/cpp/bootstrap.sh index 2d28fe3e56..ec8159f669 100755 --- a/cpp/bootstrap.sh +++ b/cpp/bootstrap.sh @@ -1,56 +1,59 @@ #!/bin/bash -set -e +set -eu # Clean. rm -rf ./build rm -rf ./build-wasm +rm -rf ./src/wasi-sdk-* # Install formatting git hook. -echo "cd ./barretenberg && ./format.sh staged" > ../.git/hooks/pre-commit -chmod +x ../.git/hooks/pre-commit +HOOKS_DIR=$(git rev-parse --git-path hooks) +# The pre-commit script will live in a barretenberg-specific hooks directory +# That may be just in the top level of this repository, +# or may be in a .git/modules/barretenberg subdirectory when this is actually a submodule +# Either way, running `git rev-parse --show-toplevel` from the hooks directory gives the path to barretenberg +echo "cd \$(git rev-parse --show-toplevel)/cpp && ./format.sh staged" > $HOOKS_DIR/pre-commit +chmod +x $HOOKS_DIR/pre-commit # Determine system. if [[ "$OSTYPE" == "darwin"* ]]; then - OS=macos + OS=macos elif [[ "$OSTYPE" == "linux-gnu" ]]; then - OS=linux + OS=linux else - echo "Unknown OS: $OSTYPE" - exit 1 + echo "Unknown OS: $OSTYPE" + exit 1 fi # Download ignition transcripts. -cd ./srs_db -./download_ignition.sh 3 -cd .. +(cd ./srs_db && ./download_ignition.sh 3) # Pick native toolchain file. if [ "$OS" == "macos" ]; then - export BREW_PREFIX=$(brew --prefix) - # Ensure we have toolchain. - if [ ! "$?" -eq 0 ] || [ ! -f "$BREW_PREFIX/opt/llvm/bin/clang++" ]; then - echo "Default clang not sufficient. Install homebrew, and then: brew install llvm libomp clang-format" - exit 1 - fi - ARCH=$(uname -m) - if [ "$ARCH" = "arm64" ]; then - TOOLCHAIN=arm-apple-clang - else - TOOLCHAIN=x86_64-apple-clang - fi + export BREW_PREFIX=$(brew --prefix) + # Ensure we have toolchain. + if [ ! "$?" -eq 0 ] || [ ! -f "$BREW_PREFIX/opt/llvm/bin/clang++" ]; then + echo "Default clang not sufficient. Install homebrew, and then: brew install llvm libomp clang-format" + exit 1 + fi + ARCH=$(uname -m) + if [ "$ARCH" = "arm64" ]; then + TOOLCHAIN=arm-apple-clang + else + TOOLCHAIN=x86_64-apple-clang + fi else - TOOLCHAIN=x86_64-linux-clang + TOOLCHAIN=x86_64-linux-clang fi # Build native. mkdir -p build && cd build cmake -DCMAKE_BUILD_TYPE=RelWithAssert -DTOOLCHAIN=$TOOLCHAIN .. -make -j$(getconf _NPROCESSORS_ONLN) $@ +cmake --build . --parallel ${@/#/--target } cd .. # Install the webassembly toolchain. WASI_VERSION=12 -rm -rf ./src/wasi-sdk-* cd ./src curl -s -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$WASI_VERSION/wasi-sdk-$WASI_VERSION.0-$OS.tar.gz | tar zxfv - cd .. @@ -59,4 +62,4 @@ cd .. mkdir -p build-wasm && cd build-wasm cmake -DTOOLCHAIN=wasm-linux-clang .. cmake --build . --parallel --target barretenberg.wasm -cd .. +cd .. \ No newline at end of file diff --git a/cpp/cmake/arch.cmake b/cpp/cmake/arch.cmake index 084c0b67df..8850cbc346 100644 --- a/cpp/cmake/arch.cmake +++ b/cpp/cmake/arch.cmake @@ -6,5 +6,5 @@ if(WASM) endif() if(NOT WASM AND NOT APPLE) - add_compile_options(-march=skylake-avx512) + add_compile_options(-march=skylake) endif() diff --git a/cpp/cmake/toolchains/wasm-linux-clang.cmake b/cpp/cmake/toolchains/wasm-linux-clang.cmake index f8e7796ff5..d410e8833f 100644 --- a/cpp/cmake/toolchains/wasm-linux-clang.cmake +++ b/cpp/cmake/toolchains/wasm-linux-clang.cmake @@ -9,7 +9,10 @@ set(CMAKE_SYSTEM_VERSION 1) set(CMAKE_SYSTEM_PROCESSOR wasm32) set(triple wasm32-wasi) -set(WASI_SDK_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/src/wasi-sdk-12.0") +if (NOT WASI_SDK_PREFIX) + # can be set by a dependent project + set(WASI_SDK_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/src/wasi-sdk-12.0") +endif() set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang) set(CMAKE_CXX_COMPILER ${WASI_SDK_PREFIX}/bin/clang++) set(CMAKE_AR ${WASI_SDK_PREFIX}/bin/llvm-ar CACHE STRING "wasi-sdk build") diff --git a/cpp/dockerfiles/Dockerfile.arm64-linux-gcc b/cpp/dockerfiles/Dockerfile.arm64-linux-gcc index c4b5623a73..ccfbe287ef 100644 --- a/cpp/dockerfiles/Dockerfile.arm64-linux-gcc +++ b/cpp/dockerfiles/Dockerfile.arm64-linux-gcc @@ -1,6 +1,6 @@ FROM aztecprotocol/crosstool-ng-arm64:latest -WORKDIR /usr/src/barretenberg +WORKDIR /usr/src/barretenberg/cpp COPY . . -RUN mkdir build && cd build && cmake -DTOOLCHAIN=arm64-linux-gcc .. && make -j$(nproc) +RUN mkdir build && cd build && cmake -DTOOLCHAIN=arm64-linux-gcc .. && cmake --build . --parallel RUN cd build && qemu-aarch64 ./test/barretenberg_tests ENTRYPOINT /bin/bash \ No newline at end of file diff --git a/cpp/dockerfiles/Dockerfile.wasm-linux-clang b/cpp/dockerfiles/Dockerfile.wasm-linux-clang index 17dc4b8795..924083bbab 100644 --- a/cpp/dockerfiles/Dockerfile.wasm-linux-clang +++ b/cpp/dockerfiles/Dockerfile.wasm-linux-clang @@ -1,11 +1,11 @@ FROM ubuntu:focal AS builder RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential wget git libssl-dev cmake curl binaryen RUN curl https://wasmtime.dev/install.sh -sSf | bash /dev/stdin --version v0.25.0 -WORKDIR /usr/src/barretenberg/src +WORKDIR /usr/src/barretenberg/cpp/src RUN curl -s -L https://github.com/CraneStation/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-linux.tar.gz | tar zxfv - -WORKDIR /usr/src/barretenberg +WORKDIR /usr/src/barretenberg/cpp COPY . . -RUN mkdir build && cd build && cmake -DTOOLCHAIN=wasm-linux-clang .. && make -j$(nproc) barretenberg.wasm +RUN mkdir build && cd build && cmake -DTOOLCHAIN=wasm-linux-clang .. && cmake --build . --parallel --target barretenberg.wasm FROM alpine:3.13 -COPY --from=builder /usr/src/barretenberg/build/bin/barretenberg.wasm /usr/src/barretenberg/build/bin/barretenberg.wasm +COPY --from=builder /usr/src/barretenberg/cpp/build/bin/barretenberg.wasm /usr/src/barretenberg/cpp/build/bin/barretenberg.wasm \ No newline at end of file diff --git a/cpp/dockerfiles/Dockerfile.x86_64-linux-clang b/cpp/dockerfiles/Dockerfile.x86_64-linux-clang index 9554f11d35..19c4018ac0 100644 --- a/cpp/dockerfiles/Dockerfile.x86_64-linux-clang +++ b/cpp/dockerfiles/Dockerfile.x86_64-linux-clang @@ -12,18 +12,18 @@ RUN apk update \ RUN git clone -b release/10.x --depth 1 https://github.com/llvm/llvm-project.git \ && cd llvm-project && mkdir build-openmp && cd build-openmp \ && cmake ../openmp -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DLIBOMP_ENABLE_SHARED=OFF \ - && make -j$(nproc) \ - && make install \ + && cmake --build . --parallel \ + && cmake --build . --parallel --target install \ && cd ../.. && rm -rf llvm-project -WORKDIR /usr/src/barretenberg +WORKDIR /usr/src/barretenberg/cpp COPY . . # Only build binaries that are needed upstream. -RUN mkdir build && cd build && cmake -DOpenMP_omp_LIBRARY=/usr/local/lib/libomp.a .. && make -j$(nproc) db_cli rollup_cli tx_factory keygen +RUN mkdir build && cd build && cmake -DOpenMP_omp_LIBRARY=/usr/local/lib/libomp.a .. && cmake --build . --parallel --target db_cli --target rollup_cli --target tx_factory --target keygen FROM alpine:3.13 RUN apk update && apk add llvm10-libs -COPY --from=builder /usr/src/barretenberg/srs_db /usr/src/barretenberg/srs_db -COPY --from=builder /usr/src/barretenberg/build/bin/db_cli /usr/src/barretenberg/build/bin/db_cli -COPY --from=builder /usr/src/barretenberg/build/bin/rollup_cli /usr/src/barretenberg/build/bin/rollup_cli -COPY --from=builder /usr/src/barretenberg/build/bin/tx_factory /usr/src/barretenberg/build/bin/tx_factory -COPY --from=builder /usr/src/barretenberg/build/bin/keygen /usr/src/barretenberg/build/bin/keygen \ No newline at end of file +COPY --from=builder /usr/src/barretenberg/cpp/srs_db /usr/src/barretenberg/cpp/srs_db +COPY --from=builder /usr/src/barretenberg/cpp/build/bin/db_cli /usr/src/barretenberg/cpp/build/bin/db_cli +COPY --from=builder /usr/src/barretenberg/cpp/build/bin/rollup_cli /usr/src/barretenberg/cpp/build/bin/rollup_cli +COPY --from=builder /usr/src/barretenberg/cpp/build/bin/tx_factory /usr/src/barretenberg/cpp/build/bin/tx_factory +COPY --from=builder /usr/src/barretenberg/cpp/build/bin/keygen /usr/src/barretenberg/cpp/build/bin/keygen \ No newline at end of file diff --git a/cpp/dockerfiles/Dockerfile.x86_64-linux-clang-assert b/cpp/dockerfiles/Dockerfile.x86_64-linux-clang-assert index cdd9c8c48f..dec88851a3 100644 --- a/cpp/dockerfiles/Dockerfile.x86_64-linux-clang-assert +++ b/cpp/dockerfiles/Dockerfile.x86_64-linux-clang-assert @@ -12,15 +12,15 @@ RUN apk update \ RUN git clone -b release/10.x --depth 1 https://github.com/llvm/llvm-project.git \ && cd llvm-project && mkdir build-openmp && cd build-openmp \ && cmake ../openmp -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DLIBOMP_ENABLE_SHARED=OFF \ - && make -j$(nproc) \ - && make install \ + && cmake --build . --parallel \ + && cmake --build . --parallel --target install \ && cd ../.. && rm -rf llvm-project -WORKDIR /usr/src/barretenberg +WORKDIR /usr/src/barretenberg/cpp COPY . . # Build everything to ensure everything builds. All tests will be run from the result of this build. -RUN mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=RelWithAssert -DOpenMP_omp_LIBRARY=/usr/local/lib/libomp.a -DCI=ON .. && make -j$(nproc) +RUN mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=RelWithAssert -DOpenMP_omp_LIBRARY=/usr/local/lib/libomp.a -DCI=ON .. && cmake --build . --parallel FROM alpine:3.13 RUN apk update && apk add llvm10-libs curl -COPY --from=builder /usr/src/barretenberg/srs_db /usr/src/barretenberg/srs_db -COPY --from=builder /usr/src/barretenberg/build/bin/*_tests /usr/src/barretenberg/build/bin/ \ No newline at end of file +COPY --from=builder /usr/src/barretenberg/cpp/srs_db /usr/src/barretenberg/cpp/srs_db +COPY --from=builder /usr/src/barretenberg/cpp/build/bin/*_tests /usr/src/barretenberg/cpp/build/bin/ \ No newline at end of file diff --git a/cpp/dockerfiles/Dockerfile.x86_64-linux-gcc b/cpp/dockerfiles/Dockerfile.x86_64-linux-gcc index 30687a98f5..b71e6f8b3c 100644 --- a/cpp/dockerfiles/Dockerfile.x86_64-linux-gcc +++ b/cpp/dockerfiles/Dockerfile.x86_64-linux-gcc @@ -6,12 +6,12 @@ RUN apk update \ cmake \ git \ curl -WORKDIR /usr/src/barretenberg +WORKDIR /usr/src/barretenberg/cpp COPY . . # Build the entire project (not just rollup_cli and db_cli), as we want to check everything builds under gcc. -RUN mkdir build && cd build && cmake -DTOOLCHAIN=x86_64-linux-gcc -DCI=ON .. && make -j$(nproc) +RUN mkdir build && cd build && cmake -DTOOLCHAIN=x86_64-linux-gcc -DCI=ON .. && cmake --build . --parallel FROM alpine:3.13 RUN apk update && apk add libstdc++ libgomp -COPY --from=builder /usr/src/barretenberg/build/bin/db_cli /usr/src/barretenberg/build/bin/db_cli -COPY --from=builder /usr/src/barretenberg/build/bin/rollup_cli /usr/src/barretenberg/build/bin/rollup_cli \ No newline at end of file +COPY --from=builder /usr/src/barretenberg/cpp/build/bin/db_cli /usr/src/barretenberg/cpp/build/bin/db_cli +COPY --from=builder /usr/src/barretenberg/cpp/build/bin/rollup_cli /usr/src/barretenberg/cpp/build/bin/rollup_cli \ No newline at end of file diff --git a/cpp/docs/Fuzzing.md b/cpp/docs/Fuzzing.md index 629e7526ea..d1a4025a92 100644 --- a/cpp/docs/Fuzzing.md +++ b/cpp/docs/Fuzzing.md @@ -82,7 +82,7 @@ Build with coverage instrumentation: mkdir build-coverage/ cd build-coverage/ cmake -DFUZZING=ON -DCMAKE_CXX_FLAGS="-fprofile-instr-generate -fcoverage-mapping" .. -make -j$(nproc) +cmake --build . --parallel ``` Then run the fuzzer on the corpus and generate the HTML coverage reports: diff --git a/cpp/scripts/run_tests b/cpp/scripts/run_tests index 392134d1cc..e6c8c8d28d 100755 --- a/cpp/scripts/run_tests +++ b/cpp/scripts/run_tests @@ -16,7 +16,7 @@ fi docker run --rm -t $IMAGE_URI /bin/sh -c "\ set -e; \ - cd /usr/src/barretenberg/srs_db; \ + cd /usr/src/barretenberg/cpp/srs_db; \ ./download_ignition.sh 1; \ - cd /usr/src/barretenberg/build; \ + cd /usr/src/barretenberg/cpp/build; \ for BIN in $TESTS; do ./bin/\$BIN $@; done" \ No newline at end of file diff --git a/cpp/src/aztec/CMakeLists.txt b/cpp/src/aztec/CMakeLists.txt index c588d7bfb7..eac67df28c 100644 --- a/cpp/src/aztec/CMakeLists.txt +++ b/cpp/src/aztec/CMakeLists.txt @@ -1,4 +1,5 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) add_compile_options(-Werror -Wall -Wextra -Wconversion -Wsign-conversion -Wno-deprecated -Wno-tautological-compare -Wfatal-errors) @@ -62,6 +63,7 @@ if(WASM) $ $ $ + # TODO: remove all except those needed for testing (join_split) $ $ $ @@ -91,5 +93,55 @@ if(WASM) barretenberg.wasm DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/barretenberg.wasm ) + # For use when compiling dependent cpp projects for WASM + message(STATUS "Compiling all-in-one barretenberg WASM archive") + add_library( + barretenberg + STATIC + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + ) -endif() +else() + # For use when compiling dependent cpp projects + message(STATUS "Compiling all-in-one barretenberg archive") + add_library( + barretenberg + STATIC + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + ) +endif() \ No newline at end of file diff --git a/cpp/src/aztec/ecc/curves/grumpkin/grumpkin.hpp b/cpp/src/aztec/ecc/curves/grumpkin/grumpkin.hpp index d6c2a05d91..918969d6ba 100644 --- a/cpp/src/aztec/ecc/curves/grumpkin/grumpkin.hpp +++ b/cpp/src/aztec/ecc/curves/grumpkin/grumpkin.hpp @@ -5,6 +5,9 @@ #include "../bn254/fr.hpp" namespace grumpkin { + +constexpr size_t MAX_NO_WRAP_INTEGER_BIT_LENGTH = 252; + typedef barretenberg::fr fq; typedef barretenberg::fq fr; diff --git a/cpp/src/aztec/rollup/constants.hpp b/cpp/src/aztec/rollup/constants.hpp index 9275e9c82a..1311e47b16 100644 --- a/cpp/src/aztec/rollup/constants.hpp +++ b/cpp/src/aztec/rollup/constants.hpp @@ -2,6 +2,8 @@ #include #include #include +#include + namespace rollup { constexpr size_t DATA_TREE_DEPTH = 32; @@ -9,7 +11,7 @@ constexpr size_t NULL_TREE_DEPTH = 256; constexpr size_t ROOT_TREE_DEPTH = 28; constexpr size_t DEFI_TREE_DEPTH = 30; -constexpr size_t MAX_NO_WRAP_INTEGER_BIT_LENGTH = 252; +constexpr size_t MAX_NO_WRAP_INTEGER_BIT_LENGTH = grumpkin::MAX_NO_WRAP_INTEGER_BIT_LENGTH; constexpr size_t MAX_TXS_BIT_LENGTH = 10; constexpr size_t TX_FEE_BIT_LENGTH = MAX_NO_WRAP_INTEGER_BIT_LENGTH - MAX_TXS_BIT_LENGTH; @@ -34,8 +36,8 @@ is_circuit_change_expected to zero and change the modified circuit gate counts a constexpr bool is_circuit_change_expected = 0; /* The below constants are only used for regression testing; to identify accidental changes to circuit constraints. They need to be changed when there is a circuit change. */ -constexpr uint32_t ACCOUNT = 23958; -constexpr uint32_t JOIN_SPLIT = 64000; +constexpr uint32_t ACCOUNT = 23967; +constexpr uint32_t JOIN_SPLIT = 64047; constexpr uint32_t CLAIM = 22684; constexpr uint32_t ROLLUP = 1173221; constexpr uint32_t ROOT_ROLLUP = 5481327; @@ -57,13 +59,14 @@ namespace circuit_vk_hash { /* These below constants are only used for regression testing; to identify accidental changes to circuit constraints. They need to be changed when there is a circuit change. Note that they are written in the reverse order to comply with the from_buffer<>() method. */ -constexpr auto ACCOUNT = uint256_t(0x78ebf096ab73e440, 0xaa1dc7c26a125f6e, 0x488a97e465b96964, 0xf9d3e501b89bf466); -constexpr auto JOIN_SPLIT = uint256_t(0x5e67a4a4503ebf25, 0xb3c070c061e76d1a, 0xb18c6c6a5bcad5fb, 0xe0d5f46cafb33ecf); +constexpr auto ACCOUNT = uint256_t(0xcd6d70c733eaf823, 0x6505d3402817ad3d, 0xbf9e2b6a262589cf, 0xafcc546b55cc45e3); +constexpr auto JOIN_SPLIT = uint256_t(0xb23c7772f47bc823, 0x5493625d4f08603c, 0x21ac50a5929576f9, 0xb7b3113c131460e5); constexpr auto CLAIM = uint256_t(0x878301ebba40ab60, 0x931466762c62d661, 0x40aad71ec3496905, 0x9f47aaa109759d0a); -constexpr auto ROLLUP = uint256_t(0x160731cc44173fdc, 0x6a6d55e46bf198bd, 0x9ce1d4608ae26fb0, 0x865ced5c16cb6152); -constexpr auto ROOT_ROLLUP = uint256_t(0xd77e82eae9e6efc7, 0x2b5ddf767012a4cf, 0x8b5982bb3d64616f, 0x20b515f5a9c78048); +constexpr auto ROLLUP = uint256_t(0x8712bcbeb11180c5, 0x598412e4f700c484, 0xfe50ad453c8e4288, 0xa7340fac5feb663f); +constexpr auto ROOT_ROLLUP = uint256_t(0xcf2fee21f089b32f, 0x90c6187354cf70d4, 0x3a5a90b8c86d8c64, 0xd55af088ddc86db7); +; constexpr auto ROOT_VERIFIER = - uint256_t(0x8e8313d6015ca626, 0x62ccf70b81c4e099, 0x33bee0072a20f36a, 0x44bd24daa009cd59); + uint256_t(0xe91df73df393fb5f, 0x99a9fa13abfbb206, 0x2ffe8c891cbde8c2, 0xdcb051e8ca06df5e); }; // namespace circuit_vk_hash namespace ProofIds { diff --git a/cpp/src/aztec/rollup/proofs/account/account.cpp b/cpp/src/aztec/rollup/proofs/account/account.cpp index 03173f5c27..1270e85556 100644 --- a/cpp/src/aztec/rollup/proofs/account/account.cpp +++ b/cpp/src/aztec/rollup/proofs/account/account.cpp @@ -105,7 +105,8 @@ void account_circuit(Composer& composer, account_tx const& tx) nullifier_1, nullifier_2 }; const byte_array_ct message = pedersen::compress(to_compress); - stdlib::schnorr::verify_signature(message, signer, signature); + const bool_ct verified = stdlib::schnorr::verify_signature(message, signer, signature); + verified.assert_equal(true, "verify signature failed"); if (composer.failed && !composerAlreadyFailed) { // only assign this error if an error hasn't already been assigned. composer.err = "verify signature failed"; diff --git a/cpp/src/aztec/rollup/proofs/join_split/join_split.test.cpp b/cpp/src/aztec/rollup/proofs/join_split/join_split.test.cpp index fe11637dde..b921c6bfdc 100644 --- a/cpp/src/aztec/rollup/proofs/join_split/join_split.test.cpp +++ b/cpp/src/aztec/rollup/proofs/join_split/join_split.test.cpp @@ -40,13 +40,14 @@ class join_split_tests : public ::testing::Test { { store = std::make_unique(); tree = std::make_unique>(*store, 32); - user = rollup::fixtures::create_user_context(); + input_user = rollup::fixtures::create_user_context(); + output_user = rollup::fixtures::create_user_context(); default_value_note = { .value = 100, .asset_id = asset_id, .account_required = false, - .owner = user.owner.public_key, - .secret = user.note_secret, + .owner = input_user.owner.public_key, + .secret = input_user.note_secret, .creator_pubkey = 0, .input_nullifier = fr::random_element() }; @@ -56,7 +57,7 @@ class join_split_tests : public ::testing::Test { value_note.input_nullifier = fr::random_element(); // to ensure uniqueness } - value_notes[0].creator_pubkey = user.owner.public_key.x; + value_notes[0].creator_pubkey = input_user.owner.public_key.x; value_notes[1].value = 50; value_notes[1].creator_pubkey = rollup::fixtures::create_key_pair(nullptr).public_key.x; @@ -76,16 +77,16 @@ class join_split_tests : public ::testing::Test { value_notes[6].value = 90; value_notes[6].asset_id = asset_id + 1; - value_notes[6].creator_pubkey = user.owner.public_key.x; + value_notes[6].creator_pubkey = input_user.owner.public_key.x; // Value chosen to cause tests to fail. value_notes[7].value = 110; value_notes[7].asset_id = asset_id + 1; - value_notes[7].creator_pubkey = user.owner.public_key.x; + value_notes[7].creator_pubkey = input_user.owner.public_key.x; // Similar to value_notes[0], but a different value of 90, to match defi_deposit_value in tests. value_notes[8].value = 90; - value_notes[8].creator_pubkey = user.owner.public_key.x; + value_notes[8].creator_pubkey = input_user.owner.public_key.x; // Similar previous virtual notes, but a different value of 90, to match defi_deposit_value in tests. value_notes[9].value = 90; @@ -113,12 +114,14 @@ class join_split_tests : public ::testing::Test { */ void preload_account_notes() { - tree->update_element( - tree->size(), - create_account_leaf_data(user.alias_hash, user.owner.public_key, user.signing_keys[0].public_key)); - tree->update_element( - tree->size(), - create_account_leaf_data(user.alias_hash, user.owner.public_key, user.signing_keys[1].public_key)); + tree->update_element(tree->size(), + create_account_leaf_data(input_user.alias_hash, + input_user.owner.public_key, + input_user.signing_keys[0].public_key)); + tree->update_element(tree->size(), + create_account_leaf_data(input_user.alias_hash, + input_user.owner.public_key, + input_user.signing_keys[1].public_key)); } /** @@ -143,24 +146,25 @@ class join_split_tests : public ::testing::Test { */ join_split_tx create_join_split_tx(std::array const& input_indices, std::array const& input_notes, + rollup::fixtures::user_context& output_user, uint32_t account_note_index = 0, bool account_required = false) { uint32_t tx_asset_id = input_notes[0].asset_id; - auto input_nullifier1 = compute_nullifier(input_notes[0].commit(), user.owner.private_key, true); - auto input_nullifier2 = compute_nullifier(input_notes[1].commit(), user.owner.private_key, true); + auto input_nullifier1 = compute_nullifier(input_notes[0].commit(), input_user.owner.private_key, true); + auto input_nullifier2 = compute_nullifier(input_notes[1].commit(), input_user.owner.private_key, true); value::value_note output_note1 = { .value = input_notes[0].value + input_notes[1].value, .asset_id = tx_asset_id, .account_required = account_required, - .owner = user.owner.public_key, - .secret = user.note_secret, + .owner = output_user.owner.public_key, + .secret = output_user.note_secret, .creator_pubkey = 0, .input_nullifier = input_nullifier1 }; value::value_note output_note2 = { .value = 0, .asset_id = tx_asset_id, .account_required = account_required, - .owner = user.owner.public_key, - .secret = user.note_secret, + .owner = output_user.owner.public_key, + .secret = output_user.note_secret, .creator_pubkey = 0, .input_nullifier = input_nullifier2 }; @@ -176,11 +180,11 @@ class join_split_tests : public ::testing::Test { tx.public_owner = fr(0); tx.account_note_index = account_note_index; tx.account_note_path = tree->get_hash_path(account_note_index); - tx.signing_pub_key = user.signing_keys[0].public_key; + tx.signing_pub_key = input_user.signing_keys[0].public_key; tx.asset_id = tx_asset_id; - tx.account_private_key = user.owner.private_key; + tx.account_private_key = input_user.owner.private_key; tx.partial_claim_note.input_nullifier = 0; - tx.alias_hash = !account_required ? rollup::fixtures::generate_alias_hash("penguin") : user.alias_hash; + tx.alias_hash = !account_required ? rollup::fixtures::generate_alias_hash("penguin") : input_user.alias_hash; tx.account_required = account_required; // default to no chaining: tx.backward_link = 0; @@ -194,13 +198,15 @@ class join_split_tests : public ::testing::Test { */ join_split_tx simple_setup(std::array const& input_indices = { 0, 1 }, uint32_t account_note_index = 0, - bool account_required = false) + bool account_required = false, + rollup::fixtures::user_context* tx_output_user = nullptr) { // The tree, user and notes are initialised in SetUp(). preload_value_notes(); preload_account_notes(); // indices: [ACCOUNT_INDEX, ACCOUNT_INDEX + 1] return create_join_split_tx(input_indices, { value_notes[input_indices[0]], value_notes[input_indices[1]] }, + tx_output_user ? *tx_output_user : output_user, account_note_index, account_required); @@ -214,17 +220,32 @@ class join_split_tests : public ::testing::Test { */ } + join_split_tx same_owner_setup(std::array const& input_indices = { 0, 1 }, + uint32_t account_note_index = 0, + bool account_required = false) + { + return simple_setup(input_indices, account_note_index, account_required, &input_user); + } + /** * Return a join split tx with 0-valued input notes. */ join_split_tx zero_input_setup() { - value::value_note input_note1 = { 0, 0, 0, user.owner.public_key, user.note_secret, 0, fr::random_element() }; - value::value_note input_note2 = { 0, 0, 0, user.owner.public_key, user.note_secret, 0, fr::random_element() }; - auto input_nullifier1 = compute_nullifier(input_note1.commit(), user.owner.private_key, false); - auto input_nullifier2 = compute_nullifier(input_note2.commit(), user.owner.private_key, false); - value::value_note output_note1 = { 0, 0, 0, user.owner.public_key, user.note_secret, 0, input_nullifier1 }; - value::value_note output_note2 = { 0, 0, 0, user.owner.public_key, user.note_secret, 0, input_nullifier2 }; + value::value_note input_note1 = { + 0, 0, 0, input_user.owner.public_key, input_user.note_secret, 0, fr::random_element() + }; + value::value_note input_note2 = { + 0, 0, 0, input_user.owner.public_key, input_user.note_secret, 0, fr::random_element() + }; + auto input_nullifier1 = compute_nullifier(input_note1.commit(), input_user.owner.private_key, false); + auto input_nullifier2 = compute_nullifier(input_note2.commit(), input_user.owner.private_key, false); + value::value_note output_note1 = { + 0, 0, 0, input_user.owner.public_key, input_user.note_secret, 0, input_nullifier1 + }; + value::value_note output_note2 = { + 0, 0, 0, input_user.owner.public_key, input_user.note_secret, 0, input_nullifier2 + }; join_split_tx tx; tx.proof_id = ProofIds::SEND; @@ -238,12 +259,12 @@ class join_split_tests : public ::testing::Test { tx.input_note = { input_note1, input_note2 }; tx.output_note = { output_note1, output_note2 }; tx.partial_claim_note.input_nullifier = 0; - tx.account_private_key = user.owner.private_key; + tx.account_private_key = input_user.owner.private_key; tx.alias_hash = rollup::fixtures::generate_alias_hash("penguin"); tx.account_required = false; tx.account_note_index = 0; tx.account_note_path = tree->get_hash_path(0); - tx.signing_pub_key = user.signing_keys[0].public_key; + tx.signing_pub_key = input_user.signing_keys[0].public_key; tx.backward_link = 0; tx.allow_chain = 0; return tx; @@ -285,7 +306,8 @@ class join_split_tests : public ::testing::Test { return verify_logic(tx); } - rollup::fixtures::user_context user; + rollup::fixtures::user_context input_user; + rollup::fixtures::user_context output_user; std::unique_ptr store; std::unique_ptr> tree; bridge_call_data empty_bridge_call_data = { .bridge_address_id = 0, @@ -433,9 +455,10 @@ TEST_F(join_split_tests, test_invalid_num_input_notes_fails) tx.num_input_notes = 100; // <-- testing this fails. tx.input_note[1].value = 0; tx.output_note[0].value = tx.input_note[0].value; - tx.output_note[1].input_nullifier = compute_nullifier(tx.input_note[1].commit(), user.owner.private_key, false); + tx.output_note[1].input_nullifier = + compute_nullifier(tx.input_note[1].commit(), input_user.owner.private_key, false); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "invalid num_input_notes"); } @@ -447,7 +470,7 @@ TEST_F(join_split_tests, test_deposit_public_value_invalid_fails) tx.public_value = 0; // <-- invalid, should be nonzero tx.public_owner = fr::random_element(); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "public value invalid"); } @@ -458,7 +481,7 @@ TEST_F(join_split_tests, test_send_public_value_invalid_fails) tx.public_value = 10; // <-- invalid - should be 0 tx.public_owner = fr::random_element(); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "public value invalid"); } @@ -470,7 +493,7 @@ TEST_F(join_split_tests, test_withdraw_public_value_invalid_fails) tx.public_value = 0; // <-- invalid - should be nonzero tx.public_owner = fr::random_element(); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "public value invalid"); } @@ -489,7 +512,7 @@ TEST_F(join_split_tests, test_defi_deposit_public_value_invalid_fails) bridge_call_data.input_asset_id_a = tx.input_note[0].asset_id; tx.partial_claim_note.bridge_call_data = bridge_call_data.to_uint256_t(); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "public value invalid"); } @@ -501,7 +524,7 @@ TEST_F(join_split_tests, test_deposit_public_owner_invalid_fails) tx.public_value = 10; tx.public_owner = 0; // <-- invalid - should be nonzero - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "public owner invalid"); } @@ -512,7 +535,7 @@ TEST_F(join_split_tests, test_send_public_owner_invalid_fails) tx.public_value = 0; tx.public_owner = fr::random_element(); // <-- invalid - should be 0 - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "public owner invalid"); } @@ -524,7 +547,7 @@ TEST_F(join_split_tests, test_withdraw_public_owner_invalid_fails) tx.public_value = 10; tx.public_owner = 0; // <-- invalid - should be nonzero - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "public owner invalid"); } @@ -543,7 +566,7 @@ TEST_F(join_split_tests, test_defi_deposit_public_owner_invalid_fails) bridge_call_data.input_asset_id_a = tx.input_note[0].asset_id; tx.partial_claim_note.bridge_call_data = bridge_call_data.to_uint256_t(); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "public owner invalid"); } @@ -553,7 +576,7 @@ TEST_F(join_split_tests, test_wrong_proof_id) join_split_tx tx = zero_input_setup(); tx.proof_id = ProofIds::DEFI_CLAIM; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "invalid proof id"); } @@ -561,7 +584,7 @@ TEST_F(join_split_tests, test_wrong_proof_id) TEST_F(join_split_tests, test_joining_same_note_fails) { join_split_tx tx = simple_setup({ 1, 1 }); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "joining same note"); } @@ -570,7 +593,7 @@ TEST_F(join_split_tests, test_send_with_0_input_notes_fails) { join_split_tx tx = zero_input_setup(); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "can only deposit"); } @@ -582,7 +605,7 @@ TEST_F(join_split_tests, test_withdraw_with_0_input_notes_fails) tx.public_value = 10; tx.public_owner = fr::random_element(); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "can only deposit"); } @@ -592,7 +615,7 @@ TEST_F(join_split_tests, test_defi_deposit_with_0_input_notes_fails) join_split_tx tx = zero_input_setup(); tx.proof_id = ProofIds::DEFI_DEPOSIT; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "can only deposit"); } @@ -602,7 +625,7 @@ TEST_F(join_split_tests, test_wrong_asset_id_fails) join_split_tx tx = simple_setup(); tx.asset_id = 3; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "asset ids don't match"); } @@ -612,7 +635,7 @@ TEST_F(join_split_tests, test_different_input_note_1_asset_id_fails) join_split_tx tx = simple_setup(); tx.input_note[0].asset_id = 3; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "asset ids don't match"); } @@ -622,7 +645,7 @@ TEST_F(join_split_tests, test_different_output_note_1_asset_id_fails) join_split_tx tx = simple_setup(); tx.output_note[0].asset_id = 3; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "asset ids don't match"); } @@ -632,7 +655,7 @@ TEST_F(join_split_tests, test_different_output_note_2_asset_id_fails) join_split_tx tx = simple_setup(); tx.output_note[1].asset_id = 3; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "asset ids don't match"); } @@ -645,7 +668,7 @@ TEST_F(join_split_tests, test_deposit_but_different_input_note_2_asset_id_fails) tx.public_owner = fr::random_element(); tx.input_note[1].asset_id = 3; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "input asset ids must match unless defi-depositing"); } @@ -655,7 +678,7 @@ TEST_F(join_split_tests, test_send_but_different_input_note_2_asset_id_fails) join_split_tx tx = simple_setup(); tx.input_note[1].asset_id = 3; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "input asset ids must match unless defi-depositing"); } @@ -668,7 +691,7 @@ TEST_F(join_split_tests, test_withdraw_but_different_input_note_2_asset_id_fails tx.public_owner = fr::random_element(); tx.input_note[1].asset_id = 3; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "input asset ids must match unless defi-depositing"); } @@ -685,7 +708,7 @@ TEST_F(join_split_tests, test_0_input_notes_and_detect_circuit_change) tx.public_owner = fr::random_element(); tx.output_note[0].value = 30; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_TRUE(result.valid); // The below part detects any changes in the join-split circuit @@ -724,7 +747,7 @@ TEST_F(join_split_tests, test_0_input_notes_create_dupicate_output_notes_fails) tx.output_note[1] = tx.output_note[0]; // <-- attempt to maliciously create duplicate output notes. tx.input_note[1] = tx.input_note[0]; // <-- for output notes to be equal, input_nullifiers must be equal. - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "joining same note"); } @@ -744,7 +767,7 @@ TEST_F(join_split_tests, test_0_input_notes_create_dupicate_output_notes_fails_2 1; // <-- to avoid 'joining same note', modify input_note[1], but then hit the error that requirement for // different input_nullifiers will force the output_notes to be different. - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "output note 2 has incorrect input nullifier"); } @@ -759,7 +782,7 @@ TEST_F(join_split_tests, test_dummy_input_note_1_non_0_value_fails) tx.input_note[0].value = 10; tx.output_note[0].value = 20; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "padding note non zero"); } @@ -774,7 +797,7 @@ TEST_F(join_split_tests, test_dummy_input_note_2_non_0_value_fails) tx.input_note[1].value = 10; tx.output_note[0].value = 20; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "padding note non zero"); } @@ -785,9 +808,10 @@ TEST_F(join_split_tests, test_1_input_note) tx.num_input_notes = 1; // <-- testing this tx.input_note[1].value = 0; tx.output_note[0].value = tx.input_note[0].value; - tx.output_note[1].input_nullifier = compute_nullifier(tx.input_note[1].commit(), user.owner.private_key, false); + tx.output_note[1].input_nullifier = + compute_nullifier(tx.input_note[1].commit(), input_user.owner.private_key, false); - EXPECT_TRUE(sign_and_verify_logic(tx, user.owner).valid); + EXPECT_TRUE(sign_and_verify_logic(tx, input_user.owner).valid); } // Bespoke test seeking bug. @@ -799,10 +823,12 @@ TEST_F(join_split_tests, test_1_input_note_with_num_input_notes_as_0) tx.input_note[1].value = 0; tx.output_note[0].value = tx.input_note[0].value; // create a cheeky nullifier for tx.input_note[0] where is_real = false - tx.output_note[0].input_nullifier = compute_nullifier(tx.input_note[0].commit(), user.owner.private_key, false); - tx.output_note[1].input_nullifier = compute_nullifier(tx.input_note[1].commit(), user.owner.private_key, false); + tx.output_note[0].input_nullifier = + compute_nullifier(tx.input_note[0].commit(), input_user.owner.private_key, false); + tx.output_note[1].input_nullifier = + compute_nullifier(tx.input_note[1].commit(), input_user.owner.private_key, false); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "can only deposit"); } @@ -810,7 +836,7 @@ TEST_F(join_split_tests, test_1_input_note_with_num_input_notes_as_0) TEST_F(join_split_tests, test_2_input_notes) { join_split_tx tx = simple_setup(); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_TRUE(result.valid); EXPECT_EQ(result.public_inputs.size(), InnerProofFields::NUM_FIELDS); } @@ -824,7 +850,7 @@ TEST_F(join_split_tests, test_0_output_notes) tx.public_value = tx.input_note[0].value + tx.input_note[1].value; tx.public_owner = fr::random_element(); - EXPECT_TRUE(sign_and_verify_logic(tx, user.owner).valid); + EXPECT_TRUE(sign_and_verify_logic(tx, input_user.owner).valid); } // ************************************************************************************************************* @@ -834,10 +860,10 @@ TEST_F(join_split_tests, test_0_output_notes) class test_valid_allow_chain_permutations : public join_split_tests, public ::testing::WithParamInterface {}; TEST_P(test_valid_allow_chain_permutations, ) { - join_split_tx tx = simple_setup(); + join_split_tx tx = same_owner_setup(); // sending to self is implied here, by the fixture's default values tx.allow_chain = GetParam(); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_TRUE(result.valid); EXPECT_EQ(result.public_inputs[InnerProofFields::ALLOW_CHAIN], GetParam()); } @@ -847,7 +873,7 @@ TEST_F(join_split_tests, test_allow_chain_out_of_range_fails) { join_split_tx tx = simple_setup(); tx.backward_link = fr::random_element(); // choose a value unrelated to the inputs being spent - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "backward_link unrelated to inputs"); } @@ -856,7 +882,7 @@ TEST_F(join_split_tests, test_unrelated_backward_link_fails) { join_split_tx tx = simple_setup(); tx.allow_chain = 4; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "allow_chain out of range"); } @@ -867,7 +893,7 @@ TEST_P(test_allow_chain_to_other_users_fail, ) join_split_tx tx = simple_setup(); tx.allow_chain = GetParam(); tx.output_note[tx.allow_chain - 1].owner = grumpkin::g1::element::random_element(); // i.e. not owned by self. - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "inter-user chaining disallowed"); } @@ -899,7 +925,7 @@ TEST_P(test_propagated_notes_skip_membership_check, ) assign_backward_link(tx, indicator); tx.input_path[indicator - 1] = tree->get_hash_path(99); // select a clearly incorrect path for the input note being propagated. - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_TRUE(result.valid); } INSTANTIATE_TEST_SUITE_P(join_split_tests, test_propagated_notes_skip_membership_check, ::testing::Values(1, 2)); @@ -923,7 +949,7 @@ TEST_F(join_split_tests, test_propagated_input_note1_no_double_spend) tx.output_note[0].input_nullifier }; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "joining same note"); @@ -941,7 +967,7 @@ TEST_F(join_split_tests, test_max_public_input) tx.output_note[0].value = max_value; tx.public_owner = fr::random_element(); - EXPECT_TRUE(sign_and_verify_logic(tx, user.owner).valid); + EXPECT_TRUE(sign_and_verify_logic(tx, input_user.owner).valid); } TEST_F(join_split_tests, test_overflow_public_value_fails) @@ -951,7 +977,7 @@ TEST_F(join_split_tests, test_overflow_public_value_fails) tx.public_value = max_value + 1; tx.public_owner = fr::random_element(); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "safe_uint_t range constraint failure: public_value"); } @@ -967,7 +993,7 @@ TEST_F(join_split_tests, test_non_zero_tx_fee) tx.public_value += 10; tx.public_owner = fr::random_element(); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_TRUE(result.valid); EXPECT_EQ(result.public_inputs[InnerProofFields::TX_FEE], 10); } @@ -977,7 +1003,7 @@ TEST_F(join_split_tests, test_non_zero_tx_fee_zero_public_values) join_split_tx tx = simple_setup(); tx.output_note[0].value -= 10; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_TRUE(result.valid); EXPECT_EQ(result.public_inputs[InnerProofFields::TX_FEE], 10); } @@ -990,7 +1016,7 @@ TEST_F(join_split_tests, test_max_tx_fee) tx.public_value += tx_fee; tx.public_owner = fr::random_element(); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_TRUE(result.valid); EXPECT_EQ(result.public_inputs[InnerProofFields::TX_FEE], fr(tx_fee)); } @@ -1003,7 +1029,7 @@ TEST_F(join_split_tests, test_overflow_tx_fee_fails) tx.public_value += tx_fee; tx.public_owner = fr::random_element(); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "safe_uint_t range constraint failure: subtract: total_in_value < total_out_value"); } @@ -1013,7 +1039,7 @@ TEST_F(join_split_tests, test_total_output_value_larger_than_total_input_value_f join_split_tx tx = simple_setup(); tx.output_note[0].value += 1; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "safe_uint_t range constraint failure: subtract: total_in_value < total_out_value"); } @@ -1027,7 +1053,7 @@ TEST_F(join_split_tests, test_different_input_note_owners_fails) join_split_tx tx = simple_setup({ 1, 2 }); tx.input_note[0].owner = grumpkin::g1::affine_element::hash_to_curve(1).second; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "input note owners don't match"); } @@ -1036,7 +1062,7 @@ TEST_F(join_split_tests, test_different_input_note_account_requireds_fails) { join_split_tx tx = simple_setup({ 1, 2 }); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "input note account_required don't match"); } @@ -1056,13 +1082,13 @@ TEST_F(join_split_tests, test_different_input_note_account_requireds_fails) TEST_F(join_split_tests, test_spend_notes_with_registered_account) { join_split_tx tx = simple_setup({ 2, 3 }, ACCOUNT_INDEX, 1); - EXPECT_TRUE(sign_and_verify_logic(tx, user.signing_keys[0]).valid); + EXPECT_TRUE(sign_and_verify_logic(tx, input_user.signing_keys[0]).valid); } TEST_F(join_split_tests, test_different_note_account_required_vs_account_required_fails) { join_split_tx tx = simple_setup({ 2, 3 }, ACCOUNT_INDEX, 0); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "account_required incorrect"); } @@ -1073,7 +1099,7 @@ TEST_F(join_split_tests, test_wrong_input_note_owner_fails) tx.input_note[0].owner = grumpkin::g1::element::random_element(); tx.input_note[1].owner = tx.input_note[0].owner; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "account_private_key incorrect"); } @@ -1084,7 +1110,7 @@ TEST_F(join_split_tests, test_random_output_note_owners) tx.output_note[0].owner = grumpkin::g1::element::random_element(); tx.output_note[1].owner = grumpkin::g1::element::random_element(); - EXPECT_TRUE(sign_and_verify_logic(tx, user.owner).valid); + EXPECT_TRUE(sign_and_verify_logic(tx, input_user.owner).valid); } TEST_F(join_split_tests, test_tainted_output_owner_fails) @@ -1092,12 +1118,12 @@ TEST_F(join_split_tests, test_tainted_output_owner_fails) join_split_tx tx = simple_setup(); tx.proof_id = ProofIds::DEPOSIT; tx.public_value = 1; - tx.signing_pub_key = user.owner.public_key; + tx.signing_pub_key = input_user.owner.public_key; uint8_t public_owner[32] = { 0x01, 0xaa, 0x42, 0xd4, 0x72, 0x88, 0x8e, 0xae, 0xa5, 0x56, 0x39, 0x46, 0xeb, 0x5c, 0xf5, 0x6c, 0x81, 0x6, 0x4d, 0x80, 0xc6, 0xf5, 0xa5, 0x38, 0xcc, 0x87, 0xae, 0x54, 0xae, 0xdb, 0x75, 0xd9 }; tx.public_owner = from_buffer(public_owner); - tx.signature = sign_join_split_tx(tx, user.owner); + tx.signature = sign_join_split_tx(tx, input_user.owner); auto prover = new_join_split_prover(tx, false); auto proof = prover.construct_proof(); @@ -1117,7 +1143,7 @@ TEST_F(join_split_tests, test_wrong_account_private_key_fails) join_split_tx tx = simple_setup(); tx.account_private_key = grumpkin::fr::random_element(); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "account_private_key incorrect"); } @@ -1130,7 +1156,7 @@ TEST_F(join_split_tests, test_wrong_public_owner_sig_fail) tx.public_owner = fr::random_element(); // sign over a different public owner - tx.signature = sign_join_split_tx(tx, user.owner); + tx.signature = sign_join_split_tx(tx, input_user.owner); tx.public_owner = fr::random_element(); @@ -1139,10 +1165,56 @@ TEST_F(join_split_tests, test_wrong_public_owner_sig_fail) EXPECT_EQ(result.err, "verify signature failed"); } +TEST_F(join_split_tests, test_merge_with_different_owners_sig_fail) +{ + // Negative case of "Simple merge transactions do not need to be signed" + // Here since our recipient does not equal our sender, this fails + // See https://hackmd.io/@aztec-network/H10r1JqOo + join_split_tx tx = simple_setup(); + tx.proof_id = ProofIds::SEND; + // sign with the wrong signer + // since this is a merge with *different* owners, this will fail + tx.signature = sign_join_split_tx(tx, output_user.owner); + + auto result = verify_logic(tx); + EXPECT_FALSE(result.valid); + EXPECT_EQ(result.err, "verify signature failed"); +} + +TEST_F(join_split_tests, test_merge_with_diff_amounts_sig_fail) +{ + // Negative case of "Simple merge transactions do not need to be signed" + // Here since our input and output amounts don't match, this fails + // See https://hackmd.io/@aztec-network/H10r1JqOo + join_split_tx tx = simple_setup(); + tx.output_note[0].value = 0; + tx.output_note[1].value = 0; + tx.proof_id = ProofIds::SEND; + // sign with the wrong signer + // since this is a merge with same owners and amount, this will still pass + tx.signature = sign_join_split_tx(tx, output_user.owner); + auto result = verify_logic(tx); + EXPECT_FALSE(result.valid); + EXPECT_EQ(result.err, "verify signature failed"); +} + +TEST_F(join_split_tests, test_merge_with_same_owners_same_amounts_sig_bypass) +{ + // Simple merge transactions do not need to be signed + // Since our recipient equals our sender, amounts are the same, and it is a send, this passes + // See https://hackmd.io/@aztec-network/H10r1JqOo + join_split_tx tx = same_owner_setup(); + tx.proof_id = ProofIds::SEND; + // sign with the wrong signer + // since this is a merge with same owners and amount, this will still pass + tx.signature = sign_join_split_tx(tx, output_user.owner); + EXPECT_TRUE(verify_logic(tx).valid); +} + TEST_F(join_split_tests, test_spend_notes_with_signing_key_when_account_required_is_false_fails) { join_split_tx tx = simple_setup(); - auto result = sign_and_verify_logic(tx, user.signing_keys[0]); + auto result = sign_and_verify_logic(tx, input_user.signing_keys[0]); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "verify signature failed"); } @@ -1150,7 +1222,7 @@ TEST_F(join_split_tests, test_spend_notes_with_signing_key_when_account_required TEST_F(join_split_tests, test_spend_registered_notes_with_owner_key_fails) { auto tx = simple_setup({ 2, 3 }, ACCOUNT_INDEX, 1); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "verify signature failed"); } @@ -1164,7 +1236,7 @@ TEST_F(join_split_tests, test_wrong_alias_hash_fails) join_split_tx tx = simple_setup({ 2, 3 }, ACCOUNT_INDEX, 1); tx.alias_hash = rollup::fixtures::generate_alias_hash("chicken"); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "account check_membership failed"); } @@ -1175,7 +1247,7 @@ TEST_F(join_split_tests, test_nonregistered_signing_key_fails) auto keys = rollup::fixtures::create_key_pair(nullptr); tx.signing_pub_key = keys.public_key; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "account check_membership failed"); } @@ -1189,7 +1261,7 @@ TEST_F(join_split_tests, test_wrong_merkle_root_fails) join_split_tx tx = simple_setup(); tx.old_data_root = fr::random_element(); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "input note not a member"); } @@ -1200,7 +1272,7 @@ TEST_F(join_split_tests, test_wrong_note_hash_path_fails) auto gibberish_path = fr_hash_path(32, std::make_pair(fr::random_element(), fr::random_element())); tx.input_path[0] = gibberish_path; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "input note not a member"); } @@ -1210,7 +1282,7 @@ TEST_F(join_split_tests, test_wrong_leaf_index_fails) join_split_tx tx = simple_setup(); tx.input_index[0] = 99; - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "input note not a member"); } @@ -1224,7 +1296,7 @@ TEST_F(join_split_tests, test_incorrect_input_nullifier_in_output_note_1_fails) join_split_tx tx = simple_setup(); tx.output_note[0].input_nullifier = 1; // incorrect nullifier - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "output note 1 has incorrect input nullifier"); } @@ -1234,7 +1306,7 @@ TEST_F(join_split_tests, test_incorrect_input_nullifier_in_output_note_2_fails) join_split_tx tx = simple_setup(); tx.output_note[1].input_nullifier = 1; // incorrect nullifier - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "output note 2 has incorrect input nullifier"); } @@ -1248,8 +1320,8 @@ TEST_F(join_split_tests, test_defi_deposit_one_virtual_input) join_split_tx tx = simple_setup({ 4, 13 }); tx.num_input_notes = 1; - tx.output_note[1].input_nullifier = - compute_nullifier(tx.input_note[1].commit(), user.owner.private_key, false); // input note 2 is a dummy note + tx.output_note[1].input_nullifier = compute_nullifier( + tx.input_note[1].commit(), input_user.owner.private_key, false); // input note 2 is a dummy note tx.proof_id = ProofIds::DEFI_DEPOSIT; tx.partial_claim_note.deposit_value = 90; @@ -1267,7 +1339,7 @@ TEST_F(join_split_tests, test_defi_deposit_one_virtual_input) * - 10 paid as fee */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_TRUE(result.valid); } @@ -1294,7 +1366,7 @@ TEST_F(join_split_tests, test_defi_deposit_one_real_one_virtual_inputs) * - 10 paid as fee (in1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_TRUE(result.valid); } @@ -1320,7 +1392,7 @@ TEST_F(join_split_tests, test_defi_deposit_one_virtual_one_real_inputs) * - 110 deposited via bridge input 2 (virtual) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_TRUE(result.valid); } @@ -1347,7 +1419,7 @@ TEST_F(join_split_tests, test_defi_deposit_one_real_one_virtual_inputs_same_asse * - 10 paid as fee (in1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_TRUE(result.valid); } @@ -1374,7 +1446,7 @@ TEST_F(join_split_tests, test_defi_deposit_two_real_inputs_different_asset_ids) * - 10 paid as fee (in1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_TRUE(result.valid); } @@ -1401,7 +1473,7 @@ TEST_F(join_split_tests, test_defi_deposit_two_virtual_inputs_different_asset_id * - 10 paid as fee (in1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_TRUE(result.valid); } @@ -1430,7 +1502,7 @@ TEST_F(join_split_tests, * - 10 paid as fee (in1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "all of input note 2 must be defi-deposited"); } @@ -1459,7 +1531,7 @@ TEST_F(join_split_tests, test_defi_deposit_two_real_inputs_different_asset_ids_a * - 10 paid as fee (in1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "all of input note 2 must be defi-deposited"); } @@ -1488,7 +1560,7 @@ TEST_F(join_split_tests, test_defi_deposit_two_real_inputs_different_asset_ids_a * - 5 paid as fee (in1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "all of input note 2 must be defi-deposited"); } @@ -1518,7 +1590,7 @@ TEST_F(join_split_tests, test_defi_invalid_tx_fee_asset_id_fails) * - 10 paid as fee (incorrect asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "asset ids don't match"); } @@ -1536,7 +1608,7 @@ TEST_F(join_split_tests, test_defi_deposit_of_zero_fails) bridge_call_data.input_asset_id_a = tx.input_note[0].asset_id; tx.partial_claim_note.bridge_call_data = bridge_call_data.to_uint256_t(); - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "Expected a nonzero defi_deposit_value for a defi-deposit"); } @@ -1564,12 +1636,12 @@ TEST_F(join_split_tests, test_defi_non_zero_output_note_1_ignored) * - 0 paid as fee */ - EXPECT_TRUE(sign_and_verify_logic(tx, user.owner).valid); + EXPECT_TRUE(sign_and_verify_logic(tx, input_user.owner).valid); } TEST_F(join_split_tests, test_defi_allow_chain_1_fails) { - join_split_tx tx = simple_setup(); + join_split_tx tx = same_owner_setup(); tx.proof_id = ProofIds::DEFI_DEPOSIT; tx.output_note[1].value = 100; tx.partial_claim_note.deposit_value = 50; @@ -1591,7 +1663,7 @@ TEST_F(join_split_tests, test_defi_allow_chain_1_fails) * - trying to chain off output_note_1, which is not allowed for defi deposits. */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "cannot chain from a partial claim note"); } @@ -1601,8 +1673,8 @@ TEST_F(join_split_tests, test_defi_deposit_incorrect_input_nullifier_in_partial_ join_split_tx tx = simple_setup({ 4, 13 }); tx.num_input_notes = 1; - tx.output_note[1].input_nullifier = - compute_nullifier(tx.input_note[1].commit(), user.owner.private_key, false); // input note 2 is a dummy note + tx.output_note[1].input_nullifier = compute_nullifier( + tx.input_note[1].commit(), input_user.owner.private_key, false); // input note 2 is a dummy note tx.proof_id = ProofIds::DEFI_DEPOSIT; tx.partial_claim_note.deposit_value = 90; @@ -1620,7 +1692,7 @@ TEST_F(join_split_tests, test_defi_deposit_incorrect_input_nullifier_in_partial_ * - 10 paid as fee */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "partial claim note has incorrect input nullifier"); } @@ -1652,7 +1724,7 @@ TEST_F(join_split_tests, test_defi_deposit_bridge_call_data_second_bridge_input_ * - 10 paid as fee (in1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "Expected second_input_in_use, given input_asset_id_b != 0"); } @@ -1662,8 +1734,8 @@ TEST_F(join_split_tests, test_defi_deposit_bridge_call_data_second_bridge_output join_split_tx tx = simple_setup({ 4, 13 }); tx.num_input_notes = 1; - tx.output_note[1].input_nullifier = - compute_nullifier(tx.input_note[1].commit(), user.owner.private_key, false); // input note 2 is a dummy note + tx.output_note[1].input_nullifier = compute_nullifier( + tx.input_note[1].commit(), input_user.owner.private_key, false); // input note 2 is a dummy note tx.proof_id = ProofIds::DEFI_DEPOSIT; tx.partial_claim_note.deposit_value = 90; @@ -1684,7 +1756,7 @@ TEST_F(join_split_tests, test_defi_deposit_bridge_call_data_second_bridge_output * - 10 paid as fee (in1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "Expected second_output_in_use, given output_asset_id_b != 0"); } @@ -1715,7 +1787,7 @@ TEST_F(join_split_tests, test_defi_deposit_second_bridge_input_in_use_but_same_b * - 10 paid as fee (in1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "input asset ids must be different for the second bridge input to be in-use"); } @@ -1725,8 +1797,8 @@ TEST_F(join_split_tests, test_defi_deposit_second_bridge_output_in_use_and_same_ join_split_tx tx = simple_setup({ 4, 13 }); tx.num_input_notes = 1; - tx.output_note[1].input_nullifier = - compute_nullifier(tx.input_note[1].commit(), user.owner.private_key, false); // input note 2 is a dummy note + tx.output_note[1].input_nullifier = compute_nullifier( + tx.input_note[1].commit(), input_user.owner.private_key, false); // input note 2 is a dummy note tx.proof_id = ProofIds::DEFI_DEPOSIT; tx.partial_claim_note.deposit_value = 90; @@ -1749,7 +1821,7 @@ TEST_F(join_split_tests, test_defi_deposit_second_bridge_output_in_use_and_same_ * - 10 paid as fee (in1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_TRUE(result.valid); } @@ -1758,8 +1830,8 @@ TEST_F(join_split_tests, test_defi_deposit_second_bridge_output_in_use_but_same_ join_split_tx tx = simple_setup({ 4, 13 }); tx.num_input_notes = 1; - tx.output_note[1].input_nullifier = - compute_nullifier(tx.input_note[1].commit(), user.owner.private_key, false); // input note 2 is a dummy note + tx.output_note[1].input_nullifier = compute_nullifier( + tx.input_note[1].commit(), input_user.owner.private_key, false); // input note 2 is a dummy note tx.proof_id = ProofIds::DEFI_DEPOSIT; tx.partial_claim_note.deposit_value = 90; @@ -1782,7 +1854,7 @@ TEST_F(join_split_tests, test_defi_deposit_second_bridge_output_in_use_but_same_ * - 10 paid as fee (in1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "real output asset ids must be different for the second bridge output to be in-use"); } @@ -1792,8 +1864,8 @@ TEST_F(join_split_tests, test_defi_deposit_first_bridge_output_asset_id_virtual_ join_split_tx tx = simple_setup({ 4, 13 }); tx.num_input_notes = 1; - tx.output_note[1].input_nullifier = - compute_nullifier(tx.input_note[1].commit(), user.owner.private_key, false); // input note 2 is a dummy note + tx.output_note[1].input_nullifier = compute_nullifier( + tx.input_note[1].commit(), input_user.owner.private_key, false); // input note 2 is a dummy note tx.proof_id = ProofIds::DEFI_DEPOSIT; tx.partial_claim_note.deposit_value = 90; @@ -1813,7 +1885,7 @@ TEST_F(join_split_tests, test_defi_deposit_first_bridge_output_asset_id_virtual_ * - 10 paid as fee (in1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "output_asset_id_a detected as virtual, but has incorrect placeholder value"); } @@ -1823,8 +1895,8 @@ TEST_F(join_split_tests, test_defi_deposit_second_bridge_output_asset_id_virtual join_split_tx tx = simple_setup({ 4, 13 }); tx.num_input_notes = 1; - tx.output_note[1].input_nullifier = - compute_nullifier(tx.input_note[1].commit(), user.owner.private_key, false); // input note 2 is a dummy note + tx.output_note[1].input_nullifier = compute_nullifier( + tx.input_note[1].commit(), input_user.owner.private_key, false); // input note 2 is a dummy note tx.proof_id = ProofIds::DEFI_DEPOSIT; tx.partial_claim_note.deposit_value = 90; @@ -1846,7 +1918,7 @@ TEST_F(join_split_tests, test_defi_deposit_second_bridge_output_asset_id_virtual * - 10 paid as fee (in1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "output_asset_id_b detected as virtual, but has incorrect placeholder value"); } @@ -1860,8 +1932,8 @@ TEST_F(join_split_tests, test_defi_wrong_first_asset_id_in_bridge_call_data_fail join_split_tx tx = simple_setup({ 4, 13 }); tx.num_input_notes = 1; - tx.output_note[1].input_nullifier = - compute_nullifier(tx.input_note[1].commit(), user.owner.private_key, false); // input note 2 is a dummy note + tx.output_note[1].input_nullifier = compute_nullifier( + tx.input_note[1].commit(), input_user.owner.private_key, false); // input note 2 is a dummy note tx.proof_id = ProofIds::DEFI_DEPOSIT; tx.partial_claim_note.deposit_value = 90; @@ -1879,7 +1951,7 @@ TEST_F(join_split_tests, test_defi_wrong_first_asset_id_in_bridge_call_data_fail * - 10 paid as fee */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "Expected bridge_call_data_local.input_asset_id_a == input_note_1.asset_id for a defi-deposit"); @@ -1890,8 +1962,8 @@ TEST_F(join_split_tests, test_defi_bridge_call_data_config_second_input_in_use_b join_split_tx tx = simple_setup({ 4, 13 }); tx.num_input_notes = 1; - tx.output_note[1].input_nullifier = - compute_nullifier(tx.input_note[1].commit(), user.owner.private_key, false); // input note 2 is a dummy note + tx.output_note[1].input_nullifier = compute_nullifier( + tx.input_note[1].commit(), input_user.owner.private_key, false); // input note 2 is a dummy note tx.proof_id = ProofIds::DEFI_DEPOSIT; tx.partial_claim_note.deposit_value = 90; @@ -1911,7 +1983,7 @@ TEST_F(join_split_tests, test_defi_bridge_call_data_config_second_input_in_use_b * - 10 paid as fee */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "Expected input_note_2_in_use, given bridge_call_data_local.config.second_input_in_use"); } @@ -1937,7 +2009,7 @@ TEST_F(join_split_tests, test_defi_missing_second_asset_in_bridge_call_data_fail * - 10 paid as fee (in1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "Expected bridge_call_data_local.config.second_input_in_use, given input_note_2_in_use && " @@ -1967,7 +2039,7 @@ TEST_F(join_split_tests, test_defi_wrong_second_asset_id_in_bridge_call_data_fai * - 10 paid as fee (in1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "Expected bridge_call_data_local.input_asset_id_b == input_note_2.asset_id, given " @@ -2002,7 +2074,7 @@ TEST_F(join_split_tests, test_repayment_logic) * - 10 out2 repayment back to depositor (in1's asset_id) */ - EXPECT_TRUE(sign_and_verify_logic(tx, user.owner).valid); + EXPECT_TRUE(sign_and_verify_logic(tx, input_user.owner).valid); } TEST_F(join_split_tests, test_virtual_note_repay_different_asset_id_fail) @@ -2029,7 +2101,7 @@ TEST_F(join_split_tests, test_virtual_note_repay_different_asset_id_fail) * - 10 out2 repayment back to depositor (INVALID asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "asset ids don't match"); } @@ -2056,7 +2128,7 @@ TEST_F(join_split_tests, test_real_input_value_lt_virtual_input_value_fails) * - 10 out2 repayment back to depositor (INVALID asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); // In this context, failure of this subtraction (due to underflow) implies input note 1's value was less than input // note 2's value (and hence the defi_deposit_value) (which is not allowed). @@ -2079,14 +2151,15 @@ TEST_F(join_split_tests, test_send_two_virtual_notes) * - 0 out2 */ - EXPECT_TRUE(sign_and_verify_logic(tx, user.owner).valid); + EXPECT_TRUE(sign_and_verify_logic(tx, input_user.owner).valid); } TEST_F(join_split_tests, test_send_one_virtual_note) { join_split_tx tx = simple_setup({ 4, 13 }); tx.num_input_notes = 1; - tx.output_note[1].input_nullifier = compute_nullifier(tx.input_note[1].commit(), user.owner.private_key, false); + tx.output_note[1].input_nullifier = + compute_nullifier(tx.input_note[1].commit(), input_user.owner.private_key, false); /** * SEND tx represents: @@ -2096,7 +2169,7 @@ TEST_F(join_split_tests, test_send_one_virtual_note) * - 0 out2 */ - EXPECT_TRUE(sign_and_verify_logic(tx, user.owner).valid); + EXPECT_TRUE(sign_and_verify_logic(tx, input_user.owner).valid); } TEST_F(join_split_tests, test_send_two_virtual_notes_nonzero_public_value_fails) @@ -2113,7 +2186,7 @@ TEST_F(join_split_tests, test_send_two_virtual_notes_nonzero_public_value_fails) * - public_value = 10 (not allowed for a send tx) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "public value invalid"); } @@ -2130,7 +2203,7 @@ TEST_F(join_split_tests, test_send_two_virtual_inputs_different_asset_ids_fails) * - 0 out2 */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "input asset ids must match unless defi-depositing"); } @@ -2149,7 +2222,7 @@ TEST_F(join_split_tests, test_send_two_virtual_inputs_different_fee_asset_id_fai * - public_asset_id = 0 (should be same as input_note_1's asset_id) */ - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "asset ids don't match"); } @@ -2162,19 +2235,19 @@ TEST_F(join_split_tests, test_non_zero_output_note_creator_pubkey_x) { { join_split_tx tx = simple_setup(); - tx.output_note[0].creator_pubkey = user.owner.public_key.x; - tx.output_note[1].creator_pubkey = user.owner.public_key.x; - EXPECT_TRUE(sign_and_verify_logic(tx, user.owner).valid); + tx.output_note[0].creator_pubkey = input_user.owner.public_key.x; + tx.output_note[1].creator_pubkey = input_user.owner.public_key.x; + EXPECT_TRUE(sign_and_verify_logic(tx, input_user.owner).valid); } { join_split_tx tx = simple_setup(); - tx.output_note[0].creator_pubkey = user.owner.public_key.x; - EXPECT_TRUE(sign_and_verify_logic(tx, user.owner).valid); + tx.output_note[0].creator_pubkey = input_user.owner.public_key.x; + EXPECT_TRUE(sign_and_verify_logic(tx, input_user.owner).valid); } { join_split_tx tx = simple_setup(); - tx.output_note[1].creator_pubkey = user.owner.public_key.x; - EXPECT_TRUE(sign_and_verify_logic(tx, user.owner).valid); + tx.output_note[1].creator_pubkey = input_user.owner.public_key.x; + EXPECT_TRUE(sign_and_verify_logic(tx, input_user.owner).valid); } } @@ -2185,7 +2258,7 @@ TEST_F(join_split_tests, test_incorrect_output_note_creator_pubkey_x) tx.output_note[0].creator_pubkey = rollup::fixtures::create_key_pair(nullptr) .public_key.x; // setting creator to be different from sender (the owner of the input notes). - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "output note 1 creator_pubkey mismatch"); } @@ -2194,7 +2267,7 @@ TEST_F(join_split_tests, test_incorrect_output_note_creator_pubkey_x) tx.output_note[1].creator_pubkey = rollup::fixtures::create_key_pair(nullptr) .public_key.x; // setting creator to be different from sender (the owner of the input notes). - auto result = sign_and_verify_logic(tx, user.owner); + auto result = sign_and_verify_logic(tx, input_user.owner); EXPECT_FALSE(result.valid); EXPECT_EQ(result.err, "output note 2 creator_pubkey mismatch"); } @@ -2219,13 +2292,13 @@ TEST_F(join_split_tests, test_deposit_full_proof) * - fee = 3 */ - auto proof = sign_and_create_proof(tx, user.owner); + auto proof = sign_and_create_proof(tx, input_user.owner); auto proof_data = inner_proof_data(proof.proof_data); auto input_note1_commitment = tx.input_note[0].commit(); auto input_note2_commitment = tx.input_note[1].commit(); - uint256_t nullifier1 = compute_nullifier(input_note1_commitment, user.owner.private_key, false); - uint256_t nullifier2 = compute_nullifier(input_note2_commitment, user.owner.private_key, false); + uint256_t nullifier1 = compute_nullifier(input_note1_commitment, input_user.owner.private_key, false); + uint256_t nullifier2 = compute_nullifier(input_note2_commitment, input_user.owner.private_key, false); auto output_note1_commitment = tx.output_note[0].commit(); auto output_note2_commitment = tx.output_note[1].commit(); @@ -2264,13 +2337,13 @@ TEST_F(join_split_tests, test_withdraw_full_proof) * - 3 paid as fee (in1's asset_id) */ - auto proof = sign_and_create_proof(tx, user.owner); + auto proof = sign_and_create_proof(tx, input_user.owner); auto proof_data = inner_proof_data(proof.proof_data); auto input_note1_commitment = tx.input_note[0].commit(); auto input_note2_commitment = tx.input_note[1].commit(); - uint256_t nullifier1 = compute_nullifier(input_note1_commitment, user.owner.private_key, true); - uint256_t nullifier2 = compute_nullifier(input_note2_commitment, user.owner.private_key, true); + uint256_t nullifier1 = compute_nullifier(input_note1_commitment, input_user.owner.private_key, true); + uint256_t nullifier2 = compute_nullifier(input_note2_commitment, input_user.owner.private_key, true); auto output_note1_commitment = tx.output_note[0].commit(); auto output_note2_commitment = tx.output_note[1].commit(); @@ -2305,15 +2378,15 @@ TEST_F(join_split_tests, test_private_send_full_proof) * - 3 paid as fee (in1's asset_id) */ - auto proof = sign_and_create_proof(tx, user.owner); + auto proof = sign_and_create_proof(tx, input_user.owner); auto proof_data = inner_proof_data(proof.proof_data); auto input_note1_commitment = tx.input_note[0].commit(); auto input_note2_commitment = tx.input_note[1].commit(); auto output_note1_commitment = tx.output_note[0].commit(); auto output_note2_commitment = tx.output_note[1].commit(); - uint256_t nullifier1 = compute_nullifier(input_note1_commitment, user.owner.private_key, true); - uint256_t nullifier2 = compute_nullifier(input_note2_commitment, user.owner.private_key, true); + uint256_t nullifier1 = compute_nullifier(input_note1_commitment, input_user.owner.private_key, true); + uint256_t nullifier2 = compute_nullifier(input_note2_commitment, input_user.owner.private_key, true); EXPECT_EQ(proof_data.proof_id, ProofIds::SEND); EXPECT_EQ(proof_data.note_commitment1, output_note1_commitment); @@ -2360,7 +2433,7 @@ TEST_F(join_split_tests, test_defi_deposit_full_proof) * - 10 paid as fee (in1's asset_id) */ - auto proof = sign_and_create_proof(tx, user.owner); + auto proof = sign_and_create_proof(tx, input_user.owner); EXPECT_TRUE(verify_proof(proof)); auto proof_data = inner_proof_data(proof.proof_data); @@ -2376,8 +2449,8 @@ TEST_F(join_split_tests, test_defi_deposit_full_proof) auto input_note2_commitment = tx.input_note[1].commit(); auto output_note1_commitment = claim_note.partial_commit(); auto output_note2_commitment = tx.output_note[1].commit(); - uint256_t nullifier1 = compute_nullifier(input_note1_commitment, user.owner.private_key, true); - uint256_t nullifier2 = compute_nullifier(input_note2_commitment, user.owner.private_key, true); + uint256_t nullifier1 = compute_nullifier(input_note1_commitment, input_user.owner.private_key, true); + uint256_t nullifier2 = compute_nullifier(input_note2_commitment, input_user.owner.private_key, true); EXPECT_EQ(proof_data.proof_id, ProofIds::DEFI_DEPOSIT); EXPECT_EQ(proof_data.note_commitment1, output_note1_commitment); @@ -2424,7 +2497,7 @@ TEST_F(join_split_tests, test_repayment_full_proof) tx.partial_claim_note.bridge_call_data = bridge_call_data.to_uint256_t(); tx.partial_claim_note.input_nullifier = tx.output_note[0].input_nullifier; - auto proof = sign_and_create_proof(tx, user.owner); + auto proof = sign_and_create_proof(tx, input_user.owner); auto proof_data = inner_proof_data(proof.proof_data); auto partial_commitment = value::create_partial_commitment( @@ -2438,8 +2511,8 @@ TEST_F(join_split_tests, test_repayment_full_proof) auto input_note2_commitment = tx.input_note[1].commit(); auto output_note1_commitment = claim_note.partial_commit(); auto output_note2_commitment = tx.output_note[1].commit(); - uint256_t nullifier1 = compute_nullifier(input_note1_commitment, user.owner.private_key, true); - uint256_t nullifier2 = compute_nullifier(input_note2_commitment, user.owner.private_key, true); + uint256_t nullifier1 = compute_nullifier(input_note1_commitment, input_user.owner.private_key, true); + uint256_t nullifier2 = compute_nullifier(input_note2_commitment, input_user.owner.private_key, true); EXPECT_EQ(proof_data.proof_id, ProofIds::DEFI_DEPOSIT); EXPECT_EQ(proof_data.note_commitment1, output_note1_commitment); @@ -2471,7 +2544,7 @@ TEST_F(join_split_tests, test_send_two_virtual_notes_full_proof) * - 0 out2 */ - auto proof = sign_and_create_proof(tx, user.owner); + auto proof = sign_and_create_proof(tx, input_user.owner); auto proof_data = inner_proof_data(proof.proof_data); @@ -2479,8 +2552,8 @@ TEST_F(join_split_tests, test_send_two_virtual_notes_full_proof) auto input_note2_commitment = tx.input_note[1].commit(); auto output_note1_commitment = tx.output_note[0].commit(); auto output_note2_commitment = tx.output_note[1].commit(); - uint256_t nullifier1 = compute_nullifier(input_note1_commitment, user.owner.private_key, true); - uint256_t nullifier2 = compute_nullifier(input_note2_commitment, user.owner.private_key, true); + uint256_t nullifier1 = compute_nullifier(input_note1_commitment, input_user.owner.private_key, true); + uint256_t nullifier2 = compute_nullifier(input_note2_commitment, input_user.owner.private_key, true); EXPECT_EQ(proof_data.proof_id, ProofIds::SEND); EXPECT_EQ(proof_data.note_commitment1, output_note1_commitment); diff --git a/cpp/src/aztec/rollup/proofs/join_split/join_split_circuit.cpp b/cpp/src/aztec/rollup/proofs/join_split/join_split_circuit.cpp index 468a43e698..ab3a0a756a 100644 --- a/cpp/src/aztec/rollup/proofs/join_split/join_split_circuit.cpp +++ b/cpp/src/aztec/rollup/proofs/join_split/join_split_circuit.cpp @@ -254,17 +254,33 @@ join_split_outputs join_split_circuit_component(join_split_inputs const& inputs) // A: By passing a signature to the circuit, the 'signing private key' doesn't need to be passed to the proof // construction software. This is useful for multisigs, offline signing, etc., so that the proof construction // software (or machine) doesn't have access to the signing private key. - verify_signature(inputs.public_value.value, - inputs.public_owner, - public_asset_id.value, - output_note_1_commitment, - output_note_2.commitment, - nullifier1, - nullifier2, - signer, - inputs.backward_link, - inputs.allow_chain, - inputs.signature); + const bool_ct verified = verify_signature(inputs.public_value.value, + inputs.public_owner, + public_asset_id.value, + output_note_1_commitment, + output_note_2.commitment, + nullifier1, + nullifier2, + signer, + inputs.backward_link, + inputs.allow_chain, + inputs.signature); + // is_same_owner: we rely on input_note_1.owner == input_note_2.owner being checked already + const bool_ct is_same_owner = + input_note_1.owner == output_note_1.owner && input_note_2.owner == output_note_2.owner; + const bool_ct is_same_amount = total_in_value == total_out_value; + // is_same_account_flag: + // we rely on input_note_1.account_required == input_note_2.account_required being checked already + const bool_ct is_same_account_flag = input_note_1.account_required == output_note_1.account_required && + input_note_2.account_required == output_note_2.account_required; + // is_merge_send: + // if true, we can elide our signature as this is a same-owner, same-amount send + // where one of the output notes has value 0. In addition, we shouldn't have output notes + // that do not need an account if input notes needed an account, and vice versa + // Caveat: A signature of all 0's will still fail basic checks + const bool_ct is_merge_send = is_send && (output_note_1_value == 0 || output_note_2_value == 0) && is_same_owner && + is_same_amount && is_same_account_flag; + (verified || is_merge_send).assert_equal(true, "verify signature failed"); return { nullifier1, nullifier2, output_note_1_commitment, output_note_2.commitment, public_asset_id, tx_fee, bridge_call_data, defi_deposit_value }; diff --git a/cpp/src/aztec/rollup/proofs/join_split/verify_signature.hpp b/cpp/src/aztec/rollup/proofs/join_split/verify_signature.hpp index 5c5161715e..7862e122af 100644 --- a/cpp/src/aztec/rollup/proofs/join_split/verify_signature.hpp +++ b/cpp/src/aztec/rollup/proofs/join_split/verify_signature.hpp @@ -7,24 +7,24 @@ namespace join_split { using namespace notes; -inline void verify_signature(field_ct const& public_value, - field_ct const& public_owner, - field_ct const& public_asset_id, - field_ct const& output_note1_commitment, - field_ct const& output_note2_commitment, - field_ct const& nullifier1, - field_ct const& nullifier2, - point_ct const& owner_pub_key, - field_ct const& backward_link, - field_ct const& allow_chain, - schnorr::signature_bits const& signature) +inline bool_ct verify_signature(field_ct const& public_value, + field_ct const& public_owner, + field_ct const& public_asset_id, + field_ct const& output_note1_commitment, + field_ct const& output_note2_commitment, + field_ct const& nullifier1, + field_ct const& nullifier2, + point_ct const& owner_pub_key, + field_ct const& backward_link, + field_ct const& allow_chain, + schnorr::signature_bits const& signature) { std::vector to_compress = { public_value, public_owner, public_asset_id, output_note1_commitment, output_note2_commitment, nullifier1, nullifier2, backward_link, allow_chain, }; byte_array_ct message = pedersen::compress(to_compress); - verify_signature(message, owner_pub_key, signature); + return verify_signature(message, owner_pub_key, signature); } } // namespace join_split diff --git a/cpp/src/aztec/stdlib/encryption/schnorr/schnorr.cpp b/cpp/src/aztec/stdlib/encryption/schnorr/schnorr.cpp index de0f4992c1..5b1d255b93 100644 --- a/cpp/src/aztec/stdlib/encryption/schnorr/schnorr.cpp +++ b/cpp/src/aztec/stdlib/encryption/schnorr/schnorr.cpp @@ -271,7 +271,7 @@ point variable_base_mul(const point& pub_key, const point& current_accu * @details TurboPlonk: ~10850 gates (~4k for variable_base_mul, ~6k for blake2s) for a string of length < 32. */ template -void verify_signature(const byte_array& message, const point& pub_key, const signature_bits& sig) +bool_t verify_signature(const byte_array& message, const point& pub_key, const signature_bits& sig) { // Compute [s]g, where s = (s_lo, s_hi) and g = G1::one. point R_1 = group::fixed_base_scalar_mul(sig.s_lo, sig.s_hi); @@ -292,11 +292,10 @@ void verify_signature(const byte_array& message, const point& pub_key, con // compute e' = hash(([s]g + [e]pub).x | message) byte_array output = blake2s(hash_input); - // verify that e' == e field_t output_hi(output.slice(0, 16)); field_t output_lo(output.slice(16, 16)); - output_lo.assert_equal(sig.e_lo, "verify signature failed"); - output_hi.assert_equal(sig.e_hi, "verify signature failed"); + // check that e' == e + return output_lo == sig.e_lo && output_hi == sig.e_hi; } template wnaf_record convert_field_into_wnaf( @@ -311,9 +310,10 @@ template point variable_base_mul( const point&, const wnaf_record&); -template void verify_signature(const byte_array&, - const point&, - const signature_bits&); +template bool_t verify_signature( + const byte_array&, + const point&, + const signature_bits&); template signature_bits convert_signature( waffle::TurboComposer*, const crypto::schnorr::signature&); @@ -321,4 +321,4 @@ template signature_bits convert_signature variable_base_mul(const point& pub_key, const field_t& low_bits, template signature_bits convert_signature(C* context, const crypto::schnorr::signature& sig); template -void verify_signature(const byte_array& message, const point& pub_key, const signature_bits& sig); +bool_t verify_signature(const byte_array& message, const point& pub_key, const signature_bits& sig); extern template point variable_base_mul( const point&, @@ -47,12 +47,14 @@ extern template point variable_base_mul(const point convert_field_into_wnaf( waffle::TurboComposer* context, const field_t& limb); -extern template void verify_signature(const byte_array&, - const point&, - const signature_bits&); -extern template void verify_signature(const byte_array&, - const point&, - const signature_bits&); +extern template bool_t verify_signature( + const byte_array&, + const point&, + const signature_bits&); +extern template bool_t verify_signature( + const byte_array&, + const point&, + const signature_bits&); extern template signature_bits convert_signature( waffle::TurboComposer*, const crypto::schnorr::signature&); diff --git a/cpp/src/aztec/stdlib/encryption/schnorr/schnorr.test.cpp b/cpp/src/aztec/stdlib/encryption/schnorr/schnorr.test.cpp index 1d2d641597..3c65ffb8b3 100644 --- a/cpp/src/aztec/stdlib/encryption/schnorr/schnorr.test.cpp +++ b/cpp/src/aztec/stdlib/encryption/schnorr/schnorr.test.cpp @@ -224,7 +224,8 @@ TEST(stdlib_schnorr, verify_signature) point_ct pub_key{ witness_ct(&composer, account.public_key.x), witness_ct(&composer, account.public_key.y) }; stdlib::schnorr::signature_bits sig = stdlib::schnorr::convert_signature(&composer, signature); byte_array_ct message(&composer, message_string); - stdlib::schnorr::verify_signature(message, pub_key, sig); + bool_ct result_ct = stdlib::schnorr::verify_signature(message, pub_key, sig); + result_ct.assert_equal(true, "verify signature failed"); Prover prover = composer.create_prover(); printf("composer gates = %zu\n", composer.get_num_gates()); @@ -268,7 +269,8 @@ TEST(stdlib_schnorr, verify_signature_failure) point_ct pub_key2_ct{ witness_ct(&composer, account2.public_key.x), witness_ct(&composer, account2.public_key.y) }; stdlib::schnorr::signature_bits sig = stdlib::schnorr::convert_signature(&composer, signature); byte_array_ct message(&composer, message_string); - stdlib::schnorr::verify_signature(message, pub_key2_ct, sig); + bool_ct result_ct = stdlib::schnorr::verify_signature(message, pub_key2_ct, sig); + result_ct.assert_equal(true, "verify signature failed"); Prover prover = composer.create_prover(); diff --git a/cpp/src/aztec/stdlib/primitives/bigfield/bigfield.fuzzer.hpp b/cpp/src/aztec/stdlib/primitives/bigfield/bigfield.fuzzer.hpp index cd85b09aa1..54a1658303 100644 --- a/cpp/src/aztec/stdlib/primitives/bigfield/bigfield.fuzzer.hpp +++ b/cpp/src/aztec/stdlib/primitives/bigfield/bigfield.fuzzer.hpp @@ -1,7 +1,6 @@ #include #include #include -#include "../../../rollup/constants.hpp" #include // This is a global variable, so that the execution handling class could alter it and signal to the input tester diff --git a/cpp/src/aztec/stdlib/primitives/bigfield/bigfield_impl.hpp b/cpp/src/aztec/stdlib/primitives/bigfield/bigfield_impl.hpp index e488445e36..d794149b93 100644 --- a/cpp/src/aztec/stdlib/primitives/bigfield/bigfield_impl.hpp +++ b/cpp/src/aztec/stdlib/primitives/bigfield/bigfield_impl.hpp @@ -1711,8 +1711,9 @@ void bigfield::unsafe_evaluate_multiply_add(const bigfield& input_left, linear_terms += to_add[to_add.size() - 1].prime_basis_limb; } if (remainders.size() >= 2) { - for (size_t i = 0; i < remainders.size(); i += 2) { - linear_terms = linear_terms.add_two(-remainders[i].prime_basis_limb, -remainders[i + 1].prime_basis_limb); + for (size_t i = 0; i < (remainders.size() >> 1); i += 1) { + linear_terms = + linear_terms.add_two(-remainders[2 * i].prime_basis_limb, -remainders[2 * i + 1].prime_basis_limb); } } if ((remainders.size() & 1UL) == 1UL) { diff --git a/cpp/src/aztec/stdlib/primitives/bit_array/bit_array.fuzzer.hpp b/cpp/src/aztec/stdlib/primitives/bit_array/bit_array.fuzzer.hpp index 32e3520f57..93d613ab30 100644 --- a/cpp/src/aztec/stdlib/primitives/bit_array/bit_array.fuzzer.hpp +++ b/cpp/src/aztec/stdlib/primitives/bit_array/bit_array.fuzzer.hpp @@ -1,6 +1,5 @@ #include #include -#include "../../../rollup/constants.hpp" #define MAX_ARRAY_SIZE 128 diff --git a/cpp/src/aztec/stdlib/primitives/bool/bool.fuzzer.hpp b/cpp/src/aztec/stdlib/primitives/bool/bool.fuzzer.hpp index 8ae2cc1a7e..e6870e4f67 100644 --- a/cpp/src/aztec/stdlib/primitives/bool/bool.fuzzer.hpp +++ b/cpp/src/aztec/stdlib/primitives/bool/bool.fuzzer.hpp @@ -1,6 +1,5 @@ #include #include -#include "../../../rollup/constants.hpp" // This is a global variable, so that the execution handling class could alter it and signal to the input tester that // the input should fail diff --git a/cpp/src/aztec/stdlib/primitives/byte_array/byte_array.fuzzer.hpp b/cpp/src/aztec/stdlib/primitives/byte_array/byte_array.fuzzer.hpp index 937a3e7ff8..37fab5c0c4 100644 --- a/cpp/src/aztec/stdlib/primitives/byte_array/byte_array.fuzzer.hpp +++ b/cpp/src/aztec/stdlib/primitives/byte_array/byte_array.fuzzer.hpp @@ -1,7 +1,6 @@ #include #include #include -#include "../../../rollup/constants.hpp" #define MAX_ARRAY_SIZE 128 diff --git a/cpp/src/aztec/stdlib/primitives/field/field.cpp b/cpp/src/aztec/stdlib/primitives/field/field.cpp index 1a526e3a49..52472a7976 100644 --- a/cpp/src/aztec/stdlib/primitives/field/field.cpp +++ b/cpp/src/aztec/stdlib/primitives/field/field.cpp @@ -2,9 +2,8 @@ #include #include "../bool/bool.hpp" #include "../composers/composers.hpp" -#include "../../../rollup/constants.hpp" #include "pow.hpp" -#include "../../../rollup/constants.hpp" +#include // #pragma GCC diagnostic ignored "-Wunused-variable" // #pragma GCC diagnostic ignored "-Wunused-parameter" @@ -824,7 +823,7 @@ std::array, 3> field_t::slice(const ui const field_t lo_wit = field_t(witness_t(ctx, lo)); const field_t slice_wit = field_t(witness_t(ctx, slice)); - hi_wit.create_range_constraint(rollup::MAX_NO_WRAP_INTEGER_BIT_LENGTH - uint32_t(msb), + hi_wit.create_range_constraint(grumpkin::MAX_NO_WRAP_INTEGER_BIT_LENGTH - uint32_t(msb), "slice: hi value too large."); lo_wit.create_range_constraint(lsb, "slice: lo value too large."); slice_wit.create_range_constraint(msb_plus_one - lsb, "slice: sliced value too large."); diff --git a/cpp/src/aztec/stdlib/primitives/field/field.fuzzer.hpp b/cpp/src/aztec/stdlib/primitives/field/field.fuzzer.hpp index 13465d2d2c..79b1f3b3da 100644 --- a/cpp/src/aztec/stdlib/primitives/field/field.fuzzer.hpp +++ b/cpp/src/aztec/stdlib/primitives/field/field.fuzzer.hpp @@ -1,8 +1,8 @@ #include #include #include -#include "../../../rollup/constants.hpp" #include +#include #include // This is a global variable, so that the execution handling class could alter it and signal to the input tester @@ -1575,7 +1575,7 @@ template class FieldBase { // Check assert conditions if ((lsb > msb) || (msb > 252) || (static_cast(stack[first_index].f().get_value()) >= - (static_cast(1) << rollup::MAX_NO_WRAP_INTEGER_BIT_LENGTH))) { + (static_cast(1) << grumpkin::MAX_NO_WRAP_INTEGER_BIT_LENGTH))) { return 0; } PRINT_SLICE(first_index, lsb, msb, stack) diff --git a/cpp/src/aztec/stdlib/primitives/safe_uint/safe_uint.cpp b/cpp/src/aztec/stdlib/primitives/safe_uint/safe_uint.cpp index 9962b03c0d..71cf84d874 100644 --- a/cpp/src/aztec/stdlib/primitives/safe_uint/safe_uint.cpp +++ b/cpp/src/aztec/stdlib/primitives/safe_uint/safe_uint.cpp @@ -1,7 +1,7 @@ #include "safe_uint.hpp" #include "../bool/bool.hpp" #include "../composers/composers.hpp" -#include "../../../rollup/constants.hpp" +#include namespace plonk { namespace stdlib { @@ -64,14 +64,14 @@ std::array, 3> safe_uint_t::slice( const uint8_t lsb) const { ASSERT(msb >= lsb); - ASSERT(static_cast(msb) <= rollup::MAX_NO_WRAP_INTEGER_BIT_LENGTH); + ASSERT(static_cast(msb) <= grumpkin::MAX_NO_WRAP_INTEGER_BIT_LENGTH); const safe_uint_t lhs = *this; ComposerContext* ctx = lhs.get_context(); const uint256_t value = uint256_t(get_value()); // This should be caught by the proof itself, but the circuit creator will have now way of knowing where the issue // is - ASSERT(value < (static_cast(1) << rollup::MAX_NO_WRAP_INTEGER_BIT_LENGTH)); + ASSERT(value < (static_cast(1) << grumpkin::MAX_NO_WRAP_INTEGER_BIT_LENGTH)); const auto msb_plus_one = uint32_t(msb) + 1; const auto hi_mask = ((uint256_t(1) << (256 - uint32_t(msb))) - 1); const auto hi = (value >> msb_plus_one) & hi_mask; @@ -88,7 +88,7 @@ std::array, 3> safe_uint_t::slice( slice_wit = safe_uint_t(slice); } else { - hi_wit = safe_uint_t(witness_t(ctx, hi), rollup::MAX_NO_WRAP_INTEGER_BIT_LENGTH - uint32_t(msb), "hi_wit"); + hi_wit = safe_uint_t(witness_t(ctx, hi), grumpkin::MAX_NO_WRAP_INTEGER_BIT_LENGTH - uint32_t(msb), "hi_wit"); lo_wit = safe_uint_t(witness_t(ctx, lo), lsb, "lo_wit"); slice_wit = safe_uint_t(witness_t(ctx, slice), msb_plus_one - lsb, "slice_wit"); } diff --git a/cpp/src/aztec/stdlib/primitives/safe_uint/safe_uint.fuzzer.hpp b/cpp/src/aztec/stdlib/primitives/safe_uint/safe_uint.fuzzer.hpp index 8ebe454d0b..bcf9c49581 100644 --- a/cpp/src/aztec/stdlib/primitives/safe_uint/safe_uint.fuzzer.hpp +++ b/cpp/src/aztec/stdlib/primitives/safe_uint/safe_uint.fuzzer.hpp @@ -1,7 +1,7 @@ #include #include #include -#include "../../../rollup/constants.hpp" +#include // This is a global variable, so that the execution handling class could alter it and signal to the input tester that // the input should fail @@ -1247,7 +1247,7 @@ template class SafeUintFuzzBase { // Check assert conditions if ((lsb > msb) || (msb > 252) || (static_cast(stack[first_index].suint.get_value()) >= - (static_cast(1) << rollup::MAX_NO_WRAP_INTEGER_BIT_LENGTH))) { + (static_cast(1) << grumpkin::MAX_NO_WRAP_INTEGER_BIT_LENGTH))) { return 0; } PRINT_SLICE(first_index, lsb, msb, stack) diff --git a/cpp/src/aztec/stdlib/primitives/uint/uint.fuzzer.hpp b/cpp/src/aztec/stdlib/primitives/uint/uint.fuzzer.hpp index 1fb8f4baf5..2be79622aa 100644 --- a/cpp/src/aztec/stdlib/primitives/uint/uint.fuzzer.hpp +++ b/cpp/src/aztec/stdlib/primitives/uint/uint.fuzzer.hpp @@ -3,7 +3,6 @@ #include #include #include -#include "../../../rollup/constants.hpp" // This is a global variable, so that the execution handling class could alter it and signal to the input tester that // the input should fail bool circuit_should_fail = false;