diff --git a/.github/workflows/bb-msan.yml b/.github/workflows/bb-msan.yml deleted file mode 100644 index fe30b2193a4..00000000000 --- a/.github/workflows/bb-msan.yml +++ /dev/null @@ -1,25 +0,0 @@ -# Checks bb prover with memory sanitizer -name: BB MSAN -on: - pull_request: - types: [opened, synchronize, reopened, labeled] - paths: - - 'barretenberg/**' - workflow_dispatch: - inputs: {} -# unlike most jobs, we have no concurrency limits - note that we should have spare capacity (of total 1000 concurrency) -# to run a good number of these, but we should still be mindful that we don't queue, say, dozens of these. -jobs: - bb-msan-check: - if: github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'bb-msan-check') - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - - uses: earthly/actions-setup@v1 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - - name: "Barretenberg Prover MSAN Check" - timeout-minutes: 720 # 12 hour timeout. Yes, it's slow, and we just want to use the github runner. - run: earthly ./barretenberg/cpp/+preset-msan-check diff --git a/.github/workflows/bb-sanitizers.yml b/.github/workflows/bb-sanitizers.yml new file mode 100644 index 00000000000..1c85c3cb961 --- /dev/null +++ b/.github/workflows/bb-sanitizers.yml @@ -0,0 +1,98 @@ +# Checks bb (barretenberg prover library) prover with sanitizers +# Unlike most jobs uses free 4 core github runners of which we have lots of capacity (of total 1000 concurrency). +name: BB MSAN +on: + push: + branches: + - master + - "*/bb-sanitizers*" + pull_request: + types: [opened, synchronize, reopened, labeled] + paths: + - 'barretenberg/**' + workflow_dispatch: + inputs: {} + +concurrency: + # force parallelism in master + group: ci-${{ github.ref_name == 'master' && github.run_id || github.ref_name }} + cancel-in-progress: true +jobs: + # acts as prover performance baseline + bb-baseline: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'bb-msan-check') + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + - uses: earthly/actions-setup@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: "Barretenberg Baseline Performance Check" + run: earthly --no-cache ./barretenberg/cpp/+preset-check --preset=clang16 + + # memory sanitzer for prover + bb-msan-check: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'bb-msan-check') + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + - uses: earthly/actions-setup@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: "Barretenberg Prover MSAN Check" + + run: earthly --no-cache ./barretenberg/cpp/+preset-msan-check || true + + # address sanitzer for prover + bb-asan-check: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'bb-asan-check') + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + - uses: earthly/actions-setup@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: "Barretenberg Prover ASAN Check" + timeout-minutes: 720 # 12 hour timeout (proving) + run: earthly --no-cache ./barretenberg/cpp/+preset-check --preset=asan + + # address sanitzer for prover + bb-tsan-check: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'bb-tsan-check') + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + - uses: earthly/actions-setup@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: "Barretenberg Prover TSAN Check" + timeout-minutes: 720 # 12 hour timeout (proving) + run: earthly --no-cache ./barretenberg/cpp/+preset-check --preset=tsan || true + + # undefined behavior sanitzer for prover + bb-ubsan-check: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'bb-ubsan-check') + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + - uses: earthly/actions-setup@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: "Barretenberg Prover TSAN Check" + timeout-minutes: 720 # 12 hour timeout (proving) + run: earthly --no-cache ./barretenberg/cpp/+preset-check --preset=ubsan diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 13c81faeb90..86e416e0560 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -258,7 +258,8 @@ jobs: export EARTHLY_BUILD_ARGS="${{ env.EARTHLY_BUILD_ARGS }}" ./scripts/e2e_test.sh ${{ matrix.test }} - earthly-ci +UPLOAD_LOGS --PULL_REQUEST=${{ github.event.pull_request.number }} --BRANCH=${{ github.ref_name }} --COMMIT_HASH=${{ env.GIT_COMMIT }} + # TODO(#9692): disabled after refactoring - trying to call function as target + # ../../scripts/earthly-ci +UPLOAD_LOGS --PULL_REQUEST=${{ github.event.pull_request.number }} --BRANCH=${{ github.ref_name }} --COMMIT_HASH=${{ env.GIT_COMMIT }} acir-bench: runs-on: ubuntu-20.04 @@ -664,7 +665,8 @@ jobs: strategy: fail-fast: false matrix: - test: [smoke.test.ts, transfer.test.ts] # TODO reinstate: 4epochs.test.ts + # TODO(#9640): add transfer.test.ts and reorg.test.ts + test: [smoke.test.ts] steps: - uses: actions/checkout@v4 with: { ref: "${{ env.GIT_COMMIT }}" } @@ -674,7 +676,7 @@ jobs: - name: Setup and KIND Network Test timeout-minutes: 60 uses: ./.github/ensure-tester-with-images - if: matrix.test == 'smoke.test.ts' || github.ref_name == 'master' || contains(github.event.pull_request.labels.*.name, 'network-all') + if: contains(github.event.pull_request.labels.*.name, 'kind-network-all') env: USERNAME: ${{ needs.configure.outputs.username }} with: @@ -687,11 +689,12 @@ jobs: # command to produce the images in case they don't exist builder_command: scripts/earthly-ci ./yarn-project+export-e2e-test-images tester_ttl: 60 + # TODO(#9640): remove `|| true` and use 16-validators.yaml run: | cd yarn-project/end-to-end echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u aztecprotocolci --password-stdin test=${{ matrix.test }} - NAMESPACE="${test%.test.ts}" FRESH_INSTALL=true VALUES_FILE=${values_file:-default.yaml} ./scripts/network_test.sh ./src/spartan/$test + NAMESPACE="${test%.test.ts}" FRESH_INSTALL=true VALUES_FILE=default.yaml ./scripts/network_test.sh ./src/spartan/$test || true l1-contracts-test: needs: [build, configure] diff --git a/.github/workflows/nightly-kind-test.yml b/.github/workflows/nightly-kind-test.yml index 668dc341ca6..3ed6b5f6367 100644 --- a/.github/workflows/nightly-kind-test.yml +++ b/.github/workflows/nightly-kind-test.yml @@ -53,14 +53,14 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - values_file: ["default.yaml", "3-validators.yaml"] + test: ["transfer", "reorg"] steps: - uses: actions/checkout@v4 with: { ref: "${{ env.GIT_COMMIT }}" } - uses: ./.github/ci-setup-action - name: Setup and Test uses: ./.github/ensure-tester-with-images - timeout-minutes: 45 + timeout-minutes: 90 with: runner_type: ${{ contains(matrix.test, 'prover') && '64core-tester-x86' || '16core-tester-x86' }} builder_type: builder-x86 @@ -69,12 +69,12 @@ jobs: builder_images_to_copy: aztecprotocol/aztec:${{ env.GIT_COMMIT }} aztecprotocol/end-to-end:${{ env.GIT_COMMIT }} # command to produce the images in case they don't exist builder_command: scripts/earthly-ci ./yarn-project+export-e2e-test-images - tester_ttl: 40 + tester_ttl: 90 run: | set -eux ./spartan/scripts/setup_local_k8s.sh export FORCE_COLOR=1 - NAMESPACE=transfer FRESH_INSTALL=true VALUES_FILE=${{ matrix.values_file }} ./scripts/network_test.sh ./src/spartan/transfer.test.ts || true + NAMESPACE=${{ matrix.test }} FRESH_INSTALL=true VALUES_FILE="16-validators.yaml" ./scripts/network_test.sh ./src/spartan/${{ matrix.test }}.test.ts || true success-check: runs-on: ubuntu-20.04 diff --git a/.github/workflows/publish-aztec-packages.yml b/.github/workflows/publish-aztec-packages.yml index 9bc5f3e88fd..1fb170f4f99 100644 --- a/.github/workflows/publish-aztec-packages.yml +++ b/.github/workflows/publish-aztec-packages.yml @@ -5,7 +5,6 @@ on: branches: - master - "*/release-master*" - - ludamad-patch-2 workflow_dispatch: inputs: tag: diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c120b28af53..48c8b7ff848 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,7 +1,7 @@ { - ".": "0.61.0", + ".": "0.62.0", "yarn-project/cli": "0.35.1", - "yarn-project/aztec": "0.61.0", - "barretenberg": "0.61.0", - "barretenberg/ts": "0.61.0" + "yarn-project/aztec": "0.62.0", + "barretenberg": "0.62.0", + "barretenberg/ts": "0.62.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index a1f6c7980a7..5760c68f2a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,67 @@ # Changelog +## [0.62.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-packages-v0.61.0...aztec-packages-v0.62.0) (2024-11-01) + + +### ⚠ BREAKING CHANGES + +* **avm:** use 32 bit locations ([#9596](https://github.com/AztecProtocol/aztec-packages/issues/9596)) +* Unique L1 to L2 messages ([#9492](https://github.com/AztecProtocol/aztec-packages/issues/9492)) + +### Features + +* Add increment secret oracles ([#9573](https://github.com/AztecProtocol/aztec-packages/issues/9573)) ([97a4c0c](https://github.com/AztecProtocol/aztec-packages/commit/97a4c0c4452f31e5c0dc776812242d2444348406)) +* **avm:** Use 32 bit locations ([#9596](https://github.com/AztecProtocol/aztec-packages/issues/9596)) ([5f38696](https://github.com/AztecProtocol/aztec-packages/commit/5f386963b06752087c2600949cbb4bb2910b25ef)) +* Barebones addressbook for tagging ([#9572](https://github.com/AztecProtocol/aztec-packages/issues/9572)) ([6526069](https://github.com/AztecProtocol/aztec-packages/commit/6526069b4faabf1a3b6834da9c290e077715a496)) +* Biggroup_goblin handles points at infinity + 1.8x reduction in ECCVM size ([#9366](https://github.com/AztecProtocol/aztec-packages/issues/9366)) ([9211d8a](https://github.com/AztecProtocol/aztec-packages/commit/9211d8afbd0fe31043ea593675ce5a72c1dc7e4e)) +* Faster square roots ([#2694](https://github.com/AztecProtocol/aztec-packages/issues/2694)) ([722ec5c](https://github.com/AztecProtocol/aztec-packages/commit/722ec5c3dfdc2a5e467528ed94a25677f8800087)) +* Fixed private log size ([#9585](https://github.com/AztecProtocol/aztec-packages/issues/9585)) ([755c70a](https://github.com/AztecProtocol/aztec-packages/commit/755c70ab55c768681349179e777bb0391c381420)) +* Removing register recipient in e2e tests as it is unnecessary now ! ([#9499](https://github.com/AztecProtocol/aztec-packages/issues/9499)) ([9f52cbb](https://github.com/AztecProtocol/aztec-packages/commit/9f52cbb5df8821f46d88116eedbe10a74f32e75e)) +* Reorg test ([#9607](https://github.com/AztecProtocol/aztec-packages/issues/9607)) ([54488b3](https://github.com/AztecProtocol/aztec-packages/commit/54488b33ea6ae0cb517639c60dbe7e7aeaf9b5dd)) +* Simulate validateEpochProofQuoteHeader in the future ([#9641](https://github.com/AztecProtocol/aztec-packages/issues/9641)) ([284c8f8](https://github.com/AztecProtocol/aztec-packages/commit/284c8f8e4504ff8e8d633dc291c20111a0406273)) +* Spartan proving ([#9584](https://github.com/AztecProtocol/aztec-packages/issues/9584)) ([392114a](https://github.com/AztecProtocol/aztec-packages/commit/392114a0a66bd580175ff7a07b0c6d899d69be8f)) +* Sync tagged logs ([#9595](https://github.com/AztecProtocol/aztec-packages/issues/9595)) ([0cc4a48](https://github.com/AztecProtocol/aztec-packages/commit/0cc4a4881ea3d61d4ab0bad7594da4f610746f4f)) +* Token private mint optimization ([#9606](https://github.com/AztecProtocol/aztec-packages/issues/9606)) ([e8fadc7](https://github.com/AztecProtocol/aztec-packages/commit/e8fadc799d015046016b16eeadbb55be929d20c2)) +* Unique L1 to L2 messages ([#9492](https://github.com/AztecProtocol/aztec-packages/issues/9492)) ([4e5ae95](https://github.com/AztecProtocol/aztec-packages/commit/4e5ae9538ebba834b3c4407cf0597c3a432a2d4e)), closes [#9450](https://github.com/AztecProtocol/aztec-packages/issues/9450) +* Use address book in recipient tag calculation ([#9618](https://github.com/AztecProtocol/aztec-packages/issues/9618)) ([5e33ed8](https://github.com/AztecProtocol/aztec-packages/commit/5e33ed8cecb058db654fbcf448749d0fee7cbd5d)) + + +### Bug Fixes + +* E2e event logs test ([#9621](https://github.com/AztecProtocol/aztec-packages/issues/9621)) ([737c573](https://github.com/AztecProtocol/aztec-packages/commit/737c5732165b9fc339ab6a15838029481dcfdbf2)) +* E2e labels ([#9609](https://github.com/AztecProtocol/aztec-packages/issues/9609)) ([ed1deb9](https://github.com/AztecProtocol/aztec-packages/commit/ed1deb9afbc7746fe1668fe35978cb159a02dedf)) +* Ensuring translator range constraint polynomials are zeroes outside of minicircuit ([#9251](https://github.com/AztecProtocol/aztec-packages/issues/9251)) ([04dd2c4](https://github.com/AztecProtocol/aztec-packages/commit/04dd2c4c959d5d80e527be9c71504c051e3c5929)) +* EventMetadata class implementation for serialisation ([#9574](https://github.com/AztecProtocol/aztec-packages/issues/9574)) ([bdff73a](https://github.com/AztecProtocol/aztec-packages/commit/bdff73af3f8043f82ebc3bf7ed1f764a941091c4)) +* Force bb-sanitizers true ([#9614](https://github.com/AztecProtocol/aztec-packages/issues/9614)) ([39cda86](https://github.com/AztecProtocol/aztec-packages/commit/39cda86c3576c5cb94a7beb123b875a2ba37c26b)) +* **k8s:** Boot node long sync ([#9610](https://github.com/AztecProtocol/aztec-packages/issues/9610)) ([1b85840](https://github.com/AztecProtocol/aztec-packages/commit/1b85840cf52442e920f4c25bf67e6bd2066606bc)) +* Multi-node metrics working ([#9486](https://github.com/AztecProtocol/aztec-packages/issues/9486)) ([fd974e1](https://github.com/AztecProtocol/aztec-packages/commit/fd974e1ba91e01910751ed87da6dbeb068faba4f)) +* Remove all register recipient functionality in ts ([#9548](https://github.com/AztecProtocol/aztec-packages/issues/9548)) ([2f7127b](https://github.com/AztecProtocol/aztec-packages/commit/2f7127be39f97873d3b3bc55d1a20d6de82f583f)) +* Remove unnecessary ivpk references in ts ([#9463](https://github.com/AztecProtocol/aztec-packages/issues/9463)) ([0c5121f](https://github.com/AztecProtocol/aztec-packages/commit/0c5121ffc0f7b5073a57d04d38f304ef1b33fe7b)) +* Resolution of bugs from bigfield audits ([#9547](https://github.com/AztecProtocol/aztec-packages/issues/9547)) ([feace70](https://github.com/AztecProtocol/aztec-packages/commit/feace70727f9e5a971809955030a8ea88ce84f4a)) +* Stop bot in case of tx errors ([#9421](https://github.com/AztecProtocol/aztec-packages/issues/9421)) ([6650641](https://github.com/AztecProtocol/aztec-packages/commit/6650641e5711ed9746ccc846a0efc0c68aeafdc3)) +* Typing of artifacts ([#9581](https://github.com/AztecProtocol/aztec-packages/issues/9581)) ([c71645f](https://github.com/AztecProtocol/aztec-packages/commit/c71645f4cc9754d99eb3ac77ff8063495caa264d)) + + +### Miscellaneous + +* Add guides to get_e2e_jobs.sh ([#9624](https://github.com/AztecProtocol/aztec-packages/issues/9624)) ([8891ead](https://github.com/AztecProtocol/aztec-packages/commit/8891ead6c20da220316c6a6fea1e8f2f0bf954b5)) +* Add migration notes to recent address changes and npk_m comment fixes ([#9645](https://github.com/AztecProtocol/aztec-packages/issues/9645)) ([3499410](https://github.com/AztecProtocol/aztec-packages/commit/3499410f90327914d2d0be3afc40e676ba6a3fd6)) +* Add sender to encode and encrypt ([#9562](https://github.com/AztecProtocol/aztec-packages/issues/9562)) ([8ce6834](https://github.com/AztecProtocol/aztec-packages/commit/8ce6834ddcfe48aa672632012c48d5d679c8687c)) +* Add signed int deserialization to decoder ([#9557](https://github.com/AztecProtocol/aztec-packages/issues/9557)) ([0435d00](https://github.com/AztecProtocol/aztec-packages/commit/0435d00671d7f6b6960a685e3e5b76db574c56da)) +* Bb sanitizers on master ([#9564](https://github.com/AztecProtocol/aztec-packages/issues/9564)) ([747bff1](https://github.com/AztecProtocol/aztec-packages/commit/747bff1acbca3d263a765db82baa6ef9ca58c372)) +* Cleaning up token test utils ([#9633](https://github.com/AztecProtocol/aztec-packages/issues/9633)) ([325bdb0](https://github.com/AztecProtocol/aztec-packages/commit/325bdb021f05c82a3abfa1fa0acd8d24635cbd10)) +* Disable breaking e2e_event_logs test ([#9602](https://github.com/AztecProtocol/aztec-packages/issues/9602)) ([cf2ca2e](https://github.com/AztecProtocol/aztec-packages/commit/cf2ca2ed452a398e0bbfd7a4f079c35484f52884)) +* Dont generate vks for simulated circuits ([#9625](https://github.com/AztecProtocol/aztec-packages/issues/9625)) ([366eff3](https://github.com/AztecProtocol/aztec-packages/commit/366eff3bfa237fbe0e06bed39eeeefd36f8f95d6)) +* Fixing broken sample-dapp tests ([#9597](https://github.com/AztecProtocol/aztec-packages/issues/9597)) ([5e52900](https://github.com/AztecProtocol/aztec-packages/commit/5e52900b245a167a9e5697c7b6b25e1d8df42be9)) +* Migration notes for unique l1-to-l2 msgs ([#9649](https://github.com/AztecProtocol/aztec-packages/issues/9649)) ([cedb88e](https://github.com/AztecProtocol/aztec-packages/commit/cedb88e2bd05dc0edbf1b0bb4fcc969dbbd9e378)) +* Nuking `Token::privately_mint_private_note(...)` ([#9616](https://github.com/AztecProtocol/aztec-packages/issues/9616)) ([bf53f5e](https://github.com/AztecProtocol/aztec-packages/commit/bf53f5e0b792e00a45013a16adb72a952e527cb9)) +* Pass on docker_fast.sh ([#9615](https://github.com/AztecProtocol/aztec-packages/issues/9615)) ([1c53459](https://github.com/AztecProtocol/aztec-packages/commit/1c53459ff31b50ba808e204c532885c9b0f69e39)) +* Remove outgoing tagging field in logs ([#9502](https://github.com/AztecProtocol/aztec-packages/issues/9502)) ([c473380](https://github.com/AztecProtocol/aztec-packages/commit/c473380026e2a8eafff91c4f57e9c2ec8f2718f0)) +* Replace relative paths to noir-protocol-circuits ([332bb60](https://github.com/AztecProtocol/aztec-packages/commit/332bb609ee7f8f090a2b7350db9d0c61c8a3477c)) +* Replace relative paths to noir-protocol-circuits ([288099b](https://github.com/AztecProtocol/aztec-packages/commit/288099b89dea0911adb04dd48af531c7a56daf21)) +* Replacing unshield naming with transfer_to_public ([#9608](https://github.com/AztecProtocol/aztec-packages/issues/9608)) ([247e9eb](https://github.com/AztecProtocol/aztec-packages/commit/247e9eb28e931874e98781addebe9a343ba7afe1)) +* Token partial notes refactor pt. 1 ([#9490](https://github.com/AztecProtocol/aztec-packages/issues/9490)) ([3d631f5](https://github.com/AztecProtocol/aztec-packages/commit/3d631f5e98439554443483520011c1c21d18f993)) + ## [0.61.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-packages-v0.60.0...aztec-packages-v0.61.0) (2024-10-30) diff --git a/Dockerfile.end-to-end.fast b/Dockerfile.end-to-end.fast new file mode 100644 index 00000000000..b6ca6c9e99f --- /dev/null +++ b/Dockerfile.end-to-end.fast @@ -0,0 +1,25 @@ +# Use an ARG to define the architecture, defaulting to amd64 +ARG ARCH=amd64 +# aztec must be built from Dockerfile.fast +FROM aztecprotocol/aztec AS aztec +FROM aztecprotocol/build:1.0-${ARCH} + +# Install additional dependencies +RUN apt-get update && apt-get install -y software-properties-common \ + && add-apt-repository ppa:xtradeb/apps -y && apt-get update \ + && apt-get install -y wget gnupg \ + && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ + && echo "deb [arch=$(dpkg --print-architecture)] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list \ + && apt update && apt install -y curl chromium netcat-openbsd \ + && rm -rf /var/lib/apt/lists/* + +ENV CHROME_BIN="/usr/bin/chromium" +ENV PATH=/opt/foundry/bin:$PATH +ENV HARDWARE_CONCURRENCY="" +ENV FAKE_PROOFS="" +ENV PROVER_AGENT_CONCURRENCY=8 + +COPY --from=aztec /usr/src/ /usr/src/ +WORKDIR /usr/src/yarn-project/end-to-end + +ENTRYPOINT ["yarn", "test"] diff --git a/Dockerfile.fast b/Dockerfile.fast index dee0f5ff6f8..c91529a1043 100644 --- a/Dockerfile.fast +++ b/Dockerfile.fast @@ -20,99 +20,83 @@ RUN git init -b master \ && git config user.email 'tech@aztecprotocol.com' # ---------- EXTRACT BUILD-SYSTEM ---------- -COPY build-system.tar.gz . -RUN tar -xzf build-system.tar.gz \ - && rm build-system.tar.gz && git add . \ +COPY build-system build-system +RUN git add . \ && git commit -m "Update git metadata" >/dev/null # ---------- BUILD BARRETENBERG ---------- -COPY barretenberg.tar.gz . -RUN tar -xzf barretenberg.tar.gz \ - && rm barretenberg.tar.gz && git add . \ +COPY barretenberg barretenberg +RUN git add . \ && git commit -m "Update git metadata" >/dev/null # Bootstrap cache for barretenberg/cpp RUN --mount=type=secret,id=aws_access_key_id \ --mount=type=secret,id=aws_secret_access_key \ - cd barretenberg/cpp \ + bash -c 'cd barretenberg \ && AWS_ACCESS_KEY_ID=$(cat /run/secrets/aws_access_key_id) \ AWS_SECRET_ACCESS_KEY=$(cat /run/secrets/aws_secret_access_key) \ - ./bootstrap_cache.sh \ - && echo "barretenberg/cpp: Success" - -# Bootstrap cache for barretenberg/ts -RUN --mount=type=secret,id=aws_access_key_id \ - --mount=type=secret,id=aws_secret_access_key \ - cd barretenberg/ts \ - && AWS_ACCESS_KEY_ID=$(cat /run/secrets/aws_access_key_id) \ - AWS_SECRET_ACCESS_KEY=$(cat /run/secrets/aws_secret_access_key) \ - ./bootstrap_cache.sh \ - && echo "barretenberg/ts: Success" + ./bootstrap_cache.sh' \ + && echo "barretenberg: Success" # ---------- BUILD NOIR ---------- -COPY noir.tar.gz . -RUN tar -xzf noir.tar.gz \ - && rm noir.tar.gz && git add . \ +ADD noir noir +RUN git add . \ && git commit -m "Update git metadata" >/dev/null # Bootstrap cache for Noir RUN --mount=type=secret,id=aws_access_key_id \ --mount=type=secret,id=aws_secret_access_key \ - cd noir \ + bash -c 'cd noir \ && AWS_ACCESS_KEY_ID=$(cat /run/secrets/aws_access_key_id) \ AWS_SECRET_ACCESS_KEY=$(cat /run/secrets/aws_secret_access_key) \ - ./bootstrap_cache.sh \ + ./bootstrap_cache.sh' \ && echo "noir: Success" # ---------- BUILD L1 CONTRACTS ---------- -COPY l1-contracts.tar.gz . -RUN tar -xzf l1-contracts.tar.gz \ - && rm l1-contracts.tar.gz && git add . \ +ADD l1-contracts l1-contracts +RUN git add . \ && git commit -m "Update git metadata" >/dev/null # Bootstrap cache for L1 Contracts RUN --mount=type=secret,id=aws_access_key_id \ --mount=type=secret,id=aws_secret_access_key \ - cd l1-contracts \ + bash -c 'cd l1-contracts \ && AWS_ACCESS_KEY_ID=$(cat /run/secrets/aws_access_key_id) \ AWS_SECRET_ACCESS_KEY=$(cat /run/secrets/aws_secret_access_key) \ - ./bootstrap_cache.sh \ + ./bootstrap_cache.sh' \ && echo "l1-contracts: Success" # ---------- BUILD AVM TRANSPILER ---------- -COPY avm-transpiler.tar.gz . -RUN tar -xzf avm-transpiler.tar.gz \ - && rm avm-transpiler.tar.gz && git add . \ +ADD avm-transpiler avm-transpiler +RUN git add . \ && git commit -m "Update git metadata" >/dev/null # Bootstrap cache for AVM Transpiler RUN --mount=type=secret,id=aws_access_key_id \ --mount=type=secret,id=aws_secret_access_key \ - cd avm-transpiler \ + bash -c 'cd avm-transpiler \ && AWS_ACCESS_KEY_ID=$(cat /run/secrets/aws_access_key_id) \ AWS_SECRET_ACCESS_KEY=$(cat /run/secrets/aws_secret_access_key) \ - ./bootstrap_cache.sh \ + ./bootstrap_cache.sh' \ && echo "avm-transpiler: Success" # ---------- BUILD NOIR PROJECTS ---------- -COPY noir-projects.tar.gz . -RUN tar -xzf noir-projects.tar.gz \ - && rm noir-projects.tar.gz && git add . \ +ADD noir-projects noir-projects +RUN git add . \ && git commit -m "Update git metadata" >/dev/null # Bootstrap cache for Noir Projects RUN --mount=type=secret,id=aws_access_key_id \ --mount=type=secret,id=aws_secret_access_key \ - cd noir-projects \ + bash -c 'cd noir-projects \ && AWS_ACCESS_KEY_ID=$(cat /run/secrets/aws_access_key_id) \ AWS_SECRET_ACCESS_KEY=$(cat /run/secrets/aws_secret_access_key) \ - ./bootstrap_cache.sh \ + ./bootstrap_cache.sh' \ && echo "noir-projects: Success" # ---------- BUILD YARN PROJECT ---------- -COPY yarn-project.tar.gz . -RUN tar -xzf yarn-project.tar.gz \ - && rm yarn-project.tar.gz && git add . \ +ADD yarn-project yarn-project +RUN git add . \ && git commit -m "Update git metadata" >/dev/null # Build yarn-project directly (no cache script) diff --git a/avm-transpiler/src/instructions.rs b/avm-transpiler/src/instructions.rs index 74318b0b5ac..aaadc278ffa 100644 --- a/avm-transpiler/src/instructions.rs +++ b/avm-transpiler/src/instructions.rs @@ -8,7 +8,7 @@ use crate::opcodes::AvmOpcode; /// A simple representation of an AVM instruction for the purpose /// of generating an AVM bytecode from Brillig. -/// Note: this does structure not impose rules like "ADD instruction must have 3 operands" +/// Note: this structure does not impose rules like "ADD instruction must have 3 operands" /// That job is left to the instruction decoder, not this thin transpiler. pub struct AvmInstruction { pub opcode: AvmOpcode, @@ -18,7 +18,7 @@ pub struct AvmInstruction { /// The 0th bit corresponds to an instruction's 0th offset arg, 1st to 1st, etc... pub indirect: Option, - /// Some instructions have a destination xor input tag + /// Some instructions have a tag, its usage will depend on the instruction. /// Its usage will depend on the instruction. pub tag: Option, diff --git a/avm-transpiler/src/opcodes.rs b/avm-transpiler/src/opcodes.rs index 3584d63b643..a3189c30df5 100644 --- a/avm-transpiler/src/opcodes.rs +++ b/avm-transpiler/src/opcodes.rs @@ -40,8 +40,8 @@ pub enum AvmOpcode { RETURNDATASIZE, RETURNDATACOPY, // Control flow - JUMP_16, - JUMPI_16, + JUMP_32, + JUMPI_32, INTERNALCALL, INTERNALRETURN, // Memory @@ -130,8 +130,8 @@ impl AvmOpcode { // Machine State // Machine State - Internal Control Flow - AvmOpcode::JUMP_16 => "JUMP_16", - AvmOpcode::JUMPI_16 => "JUMPI_16", + AvmOpcode::JUMP_32 => "JUMP_32", + AvmOpcode::JUMPI_32 => "JUMPI_32", AvmOpcode::INTERNALCALL => "INTERNALCALL", AvmOpcode::INTERNALRETURN => "INTERNALRETURN", // Machine State - Memory diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index 799d0017b6c..d1442196a80 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -233,20 +233,20 @@ pub fn brillig_to_avm( BrilligOpcode::Jump { location } => { let avm_loc = brillig_pcs_to_avm_pcs[*location]; avm_instrs.push(AvmInstruction { - opcode: AvmOpcode::JUMP_16, - operands: vec![make_operand(16, &avm_loc)], + opcode: AvmOpcode::JUMP_32, + operands: vec![make_operand(32, &avm_loc)], ..Default::default() }); } BrilligOpcode::JumpIf { condition, location } => { let avm_loc = brillig_pcs_to_avm_pcs[*location]; avm_instrs.push(AvmInstruction { - opcode: AvmOpcode::JUMPI_16, + opcode: AvmOpcode::JUMPI_32, indirect: Some( AddressingModeBuilder::default().direct_operand(condition).build(), ), operands: vec![ - make_operand(16, &avm_loc), + make_operand(32, &avm_loc), make_operand(16, &condition.to_usize()), ], ..Default::default() @@ -298,7 +298,7 @@ pub fn brillig_to_avm( let avm_loc = brillig_pcs_to_avm_pcs[*location]; avm_instrs.push(AvmInstruction { opcode: AvmOpcode::INTERNALCALL, - operands: vec![AvmOperand::U16 { value: avm_loc as u16 }], + operands: vec![AvmOperand::U32 { value: avm_loc as u32 }], ..Default::default() }); } diff --git a/avm-transpiler/src/transpile_contract.rs b/avm-transpiler/src/transpile_contract.rs index 10b9367a4ab..0c38dbe1e0b 100644 --- a/avm-transpiler/src/transpile_contract.rs +++ b/avm-transpiler/src/transpile_contract.rs @@ -6,11 +6,8 @@ use serde::{Deserialize, Serialize}; use acvm::acir::circuit::Program; use noirc_errors::debug_info::ProgramDebugInfo; -use crate::transpile::{ - brillig_to_avm, map_brillig_pcs_to_avm_pcs, patch_assert_message_pcs, patch_debug_info_pcs, -}; -use crate::utils::{extract_brillig_from_acir_program, extract_static_assert_messages}; -use fxhash::FxHashMap as HashMap; +use crate::transpile::{brillig_to_avm, map_brillig_pcs_to_avm_pcs, patch_debug_info_pcs}; +use crate::utils::extract_brillig_from_acir_program; /// Representation of a contract with some transpiled functions #[derive(Debug, Serialize, Deserialize)] @@ -51,7 +48,6 @@ pub struct AvmContractFunctionArtifact { )] pub debug_symbols: ProgramDebugInfo, pub brillig_names: Vec, - pub assert_messages: HashMap, } /// Representation of an ACIR contract function but with @@ -96,16 +92,11 @@ impl From for TranspiledContractArtifact { // Extract Brillig Opcodes from acir let acir_program = function.bytecode; let brillig_bytecode = extract_brillig_from_acir_program(&acir_program); - let assert_messages = extract_static_assert_messages(&acir_program); info!("Extracted Brillig program has {} instructions", brillig_bytecode.len()); // Map Brillig pcs to AVM pcs (index is Brillig PC, value is AVM PC) let brillig_pcs_to_avm_pcs = map_brillig_pcs_to_avm_pcs(brillig_bytecode); - // Patch the assert messages with updated PCs - let assert_messages = - patch_assert_message_pcs(assert_messages, &brillig_pcs_to_avm_pcs); - // Transpile to AVM let avm_bytecode = brillig_to_avm(brillig_bytecode, &brillig_pcs_to_avm_pcs); @@ -132,7 +123,6 @@ impl From for TranspiledContractArtifact { bytecode: base64::prelude::BASE64_STANDARD.encode(avm_bytecode), debug_symbols: ProgramDebugInfo { debug_infos }, brillig_names: function.brillig_names, - assert_messages, }, )); } else { diff --git a/avm-transpiler/src/utils.rs b/avm-transpiler/src/utils.rs index 3f5269f5daa..018276e4b6d 100644 --- a/avm-transpiler/src/utils.rs +++ b/avm-transpiler/src/utils.rs @@ -1,11 +1,9 @@ -use fxhash::FxHashMap as HashMap; - use acvm::acir::circuit::brillig::BrilligFunctionId; use acvm::{AcirField, FieldElement}; use log::{debug, info, trace}; use acvm::acir::brillig::Opcode as BrilligOpcode; -use acvm::acir::circuit::{AssertionPayload, Opcode, Program}; +use acvm::acir::circuit::{Opcode, Program}; use crate::instructions::{AvmInstruction, AvmOperand}; use crate::opcodes::AvmOpcode; @@ -39,33 +37,6 @@ pub fn extract_brillig_from_acir_program( &program.unconstrained_functions[0].bytecode } -/// Assertion messages that are static strings are stored in the assert_messages map of the ACIR program. -pub fn extract_static_assert_messages(program: &Program) -> HashMap { - assert_eq!( - program.functions.len(), - 1, - "An AVM program should have only a single ACIR function with a 'BrilligCall'" - ); - let main_function = &program.functions[0]; - main_function - .assert_messages - .iter() - .filter_map(|(location, payload)| { - if let AssertionPayload::StaticString(static_string) = payload { - Some(( - location - .to_brillig_location() - .expect("Assert message is not for the brillig function") - .0, - static_string.clone(), - )) - } else { - None - } - }) - .collect() -} - /// Print inputs, outputs, and instructions in a Brillig program pub fn dbg_print_brillig_program(brillig_bytecode: &[BrilligOpcode]) { trace!("Printing Brillig program..."); diff --git a/aztec-up/README.md b/aztec-up/README.md index 285ed9c9070..d3cc08d04fd 100644 --- a/aztec-up/README.md +++ b/aztec-up/README.md @@ -6,12 +6,12 @@ bash -i <(curl -s https://install.aztec.network) That is all. -This will install into `~/.aztec/bin` a collection of scripts to help running aztec containers, and will update -a users `PATH` variable in their shell startup script so they can be found. +This will install into `~/.aztec/bin` a collection of scripts to help with running aztec containers, and will update +the user's `PATH` variable in their shell startup script so they can be found. - `aztec` - The infrastructure container. -- `aztec-cli` - A command line tool for interacting with infrastructure. -- `aztec-nargo` - A build of `nargo` from `noir` that is guaranteed to be version aligned. Provides compiler, lsp and more. +- `aztec-cli` - A command-line tool for interacting with infrastructure. +- `aztec-nargo` - A build of `nargo` from `noir` that is guaranteed to be version-aligned. Provides compiler, lsp and more. - `aztec-sandbox` - A wrapper around docker-compose that launches services needed for sandbox testing. - `aztec-up` - A tool to upgrade the aztec toolchain to the latest, or specific versions. - `aztec-builder` - A useful tool for projects to generate ABIs and update their dependencies. @@ -22,10 +22,10 @@ After installed, you can use `aztec-up` to upgrade or install specific versions. VERSION=master aztec-up ``` -This will install the container built from master branch. +This will install the container built from the master branch. ``` VERSION=1.2.3 aztec-up ``` -This will install tagged release version 1.2.3. +This will install the tagged release version 1.2.3. diff --git a/barretenberg/.gitrepo b/barretenberg/.gitrepo index 599eac197a0..ea633838f37 100644 --- a/barretenberg/.gitrepo +++ b/barretenberg/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/AztecProtocol/barretenberg branch = master - commit = 8b4fa8cbcb9e3bf94962569aa3f9857fe8452a7a - parent = 9d66c1abca1af9ddb0715627fad87c2efc612a1d + commit = ad85ad9e120dd3d526b46ab44fe5cc0e848c6b30 + parent = 4fc6f8b44b7e58d982151732fa6d9691e73635bc method = merge cmdver = 0.4.6 diff --git a/barretenberg/CHANGELOG.md b/barretenberg/CHANGELOG.md index 81ffd32a1da..7cabdb6979c 100644 --- a/barretenberg/CHANGELOG.md +++ b/barretenberg/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog +## [0.62.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg-v0.61.0...barretenberg-v0.62.0) (2024-11-01) + + +### ⚠ BREAKING CHANGES + +* **avm:** use 32 bit locations ([#9596](https://github.com/AztecProtocol/aztec-packages/issues/9596)) + +### Features + +* **avm:** Use 32 bit locations ([#9596](https://github.com/AztecProtocol/aztec-packages/issues/9596)) ([5f38696](https://github.com/AztecProtocol/aztec-packages/commit/5f386963b06752087c2600949cbb4bb2910b25ef)) +* Biggroup_goblin handles points at infinity + 1.8x reduction in ECCVM size ([#9366](https://github.com/AztecProtocol/aztec-packages/issues/9366)) ([9211d8a](https://github.com/AztecProtocol/aztec-packages/commit/9211d8afbd0fe31043ea593675ce5a72c1dc7e4e)) +* Faster square roots ([#2694](https://github.com/AztecProtocol/aztec-packages/issues/2694)) ([722ec5c](https://github.com/AztecProtocol/aztec-packages/commit/722ec5c3dfdc2a5e467528ed94a25677f8800087)) +* Spartan proving ([#9584](https://github.com/AztecProtocol/aztec-packages/issues/9584)) ([392114a](https://github.com/AztecProtocol/aztec-packages/commit/392114a0a66bd580175ff7a07b0c6d899d69be8f)) + + +### Bug Fixes + +* Ensuring translator range constraint polynomials are zeroes outside of minicircuit ([#9251](https://github.com/AztecProtocol/aztec-packages/issues/9251)) ([04dd2c4](https://github.com/AztecProtocol/aztec-packages/commit/04dd2c4c959d5d80e527be9c71504c051e3c5929)) +* Resolution of bugs from bigfield audits ([#9547](https://github.com/AztecProtocol/aztec-packages/issues/9547)) ([feace70](https://github.com/AztecProtocol/aztec-packages/commit/feace70727f9e5a971809955030a8ea88ce84f4a)) + + +### Miscellaneous + +* Bb sanitizers on master ([#9564](https://github.com/AztecProtocol/aztec-packages/issues/9564)) ([747bff1](https://github.com/AztecProtocol/aztec-packages/commit/747bff1acbca3d263a765db82baa6ef9ca58c372)) +* Pass on docker_fast.sh ([#9615](https://github.com/AztecProtocol/aztec-packages/issues/9615)) ([1c53459](https://github.com/AztecProtocol/aztec-packages/commit/1c53459ff31b50ba808e204c532885c9b0f69e39)) + ## [0.61.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg-v0.60.0...barretenberg-v0.61.0) (2024-10-30) diff --git a/barretenberg/Earthfile b/barretenberg/Earthfile index 6a43fdff5b5..6030d85a771 100644 --- a/barretenberg/Earthfile +++ b/barretenberg/Earthfile @@ -50,6 +50,7 @@ barretenberg-acir-tests-bb-ultra-plonk: # Run every acir test through native bb build prove_then_verify flow for UltraPlonk. # This ensures we test independent pk construction through real/garbage witness data paths. RUN FLOW=prove_then_verify ./run_acir_tests.sh + RUN FLOW=prove_then_verify RECURSIVE=true ./run_acir_tests.sh assert_statement double_verify_proof barretenberg-acir-tests-bb-ultra-honk: FROM ../build-images/+from-registry @@ -67,6 +68,7 @@ barretenberg-acir-tests-bb-ultra-honk: # Run the acir test through native bb build prove_then_verify_ultra_honk flow # Note that the script will skip the Plonk related tests RUN FLOW=prove_then_verify_ultra_honk HONK=true ./run_acir_tests.sh + RUN FLOW=prove_then_verify_ultra_honk HONK=true RECURSIVE=true ./run_acir_tests.sh assert_statement double_verify_honk_proof # Construct and verify a UltraHonk proof for a single program RUN FLOW=prove_and_verify_ultra_honk ./run_acir_tests.sh pedersen_hash @@ -173,4 +175,4 @@ barretenberg-acir-tests-bb.js: # Run fold_basic test through bb.js which runs ClientIVC on fold basic RUN BIN=../ts/dest/node/main.js FLOW=fold_and_verify_program ./run_acir_tests.sh fold_basic # Run 1_mul through bb.js build, all_cmds flow, to test all cli args. - RUN BIN=../ts/dest/node/main.js FLOW=all_cmds ./run_acir_tests.sh 1_mul + RUN BIN=../ts/dest/node/main.js FLOW=all_cmds ./run_acir_tests.sh 1_mul \ No newline at end of file diff --git a/barretenberg/README.md b/barretenberg/README.md index 82e5d65486f..07a4bb6eaba 100644 --- a/barretenberg/README.md +++ b/barretenberg/README.md @@ -34,7 +34,7 @@ Barretenberg (or `bb` for short) is an optimized elliptic curve library for the - [WASM](#wasm) - [How to run](#how-to-run) - [Debugging](#debugging) - - [Debugging Verifification Failures](#debugging-verifification-failures) + - [Debugging Verification Failures](#debugging-verifification-failures) - [Improving LLDB Debugging](#improving-lldb-debugging) - [Using Tracy to Profile Memory/CPU](#using-tracy-to-profile-memorycpu) @@ -393,7 +393,7 @@ cmake --build --preset default --target run_ecc_bench #### Debugging Verifification Failures -The CicuitChecker::check_circuit function is used to get the gate index and block information about a failing circuit constraint. +The CircuitChecker::check_circuit function is used to get the gate index and block information about a failing circuit constraint. If you are in a scenario where you have a failing call to check_circuit and wish to get more information out of it than just the gate index, you can use this feature to get a stack trace, see example below. Usage instructions: diff --git a/barretenberg/acir_tests/Dockerfile.bb.sol b/barretenberg/acir_tests/Dockerfile.bb.sol index 2f89581c185..9b09ba86d66 100644 --- a/barretenberg/acir_tests/Dockerfile.bb.sol +++ b/barretenberg/acir_tests/Dockerfile.bb.sol @@ -18,8 +18,7 @@ COPY . . # This includes the basic `assert_statement` test that contains a single public input # and the recursive aggregation circuits which use the Keccak based prover. # -# NOTE: When circuits are marked `recursive` it means the backend will use a prover that -# produces SNARK recursion friendly proofs, while the solidity verifier expects proofs -# whose transcript uses Keccak hashing. +# NOTE: The solidity verifier expects proofs whose transcript uses Keccak hashing, +# for which we have to invoke the backend prover without the `--recursive` flag. RUN (cd sol-test && yarn) RUN PARALLEL=1 FLOW=sol ./run_acir_tests.sh assert_statement double_verify_proof double_verify_nested_proof diff --git a/barretenberg/acir_tests/README.md b/barretenberg/acir_tests/README.md index 4d644c672b7..5a33c7d05e6 100644 --- a/barretenberg/acir_tests/README.md +++ b/barretenberg/acir_tests/README.md @@ -5,7 +5,7 @@ The aim is to verify acir tests verify through a given backend binary. "Backend - bb (native CLI) - bb.js (typescript CLI) - bb.js-dev (symlink in your PATH that runs the typescript CLI via ts-node) -- bb.js.browser (script in `headless-test` that runs a a test through bb.js in a browser instance via playwright) +- bb.js.browser (script in `headless-test` that runs a test through bb.js in a browser instance via playwright) To run: @@ -34,7 +34,7 @@ $ BIN=../ts/bb.js-dev VERBOSE=1 ./run_acir_tests.sh 1_mul $ BIN=./headless-test/bb.js.browser VERBOSE=1 ./run_acir_tests.sh 1_mul ``` -You can specify a different testing "flow" with with `FLOW` environment variable. Flows are in the `flows` dir. +You can specify a different testing "flow" with `FLOW` environment variable. Flows are in the `flows` dir. The default flow is `prove_and_verify`, which is the quickest way to... prove and verify. It's used to test the acir test vectors actually all pass in whichever version of the backend is being run. The `all_cmds` flow tests all the supported commands on the binary. Slower, but is there to test the cli. @@ -49,14 +49,14 @@ $ FLOW=all_cmds ./run_acir_tests.sh 1_mul This means we have to generate the proof specific inputs using our backend and pass it back into `double_verify_proof` to regenerate the accurate witness. The following is a temporary solution to manually regenerate the inputs for `double_verify_proof` on a specific Noir branch. -First find `acir_tests/gen_inner_proof_inputs.sh`. Change the $BRANCH env var to your working branch and $PROOF_NAME to your first input you want to recursively verify. The script is going to generate the proof system specific verification key output and proof for the `assert_statement_recursive` test. +First find `acir_tests/gen_inner_proof_inputs.sh`. Change the $BRANCH env var to your working branch and $PROOF_NAME to your first input you want to recursively verify. The script is going to generate the proof system specific verification key output and proof for the `assert_statement` test. To run: ``` ./gen_inner_proof_inputs.sh ``` -To generate a new input you can run the script again. To generate a new file under `assert_statement_recursive/proofs/` be sure to change the $PROOF_NAME inside of the script. +To generate a new input you can run the script again. To generate a new file under `assert_statement/proofs/` be sure to change the $PROOF_NAME inside of the script. You can then copy these inputs over to your working branch in Noir and regenerate the witness for `double_verify_proof`. You can then change the branch in `run_acir_tests.sh` to this Noir working branch as well and `double_verify_proof` should pass. -The same process should then be repeated, but now `double_verify_proof_recursive` will be the circuit for which we will be generating recursive inputs using `gen_inner_proof_inputs.sh`. The recursive artifacts should then supplied as inputs to `double_verify_nested_proof`. \ No newline at end of file +The same process should then be repeated, but now `double_verify_proof_recursive` will be the circuit for which we will be generating recursive inputs using `gen_inner_proof_inputs.sh`. The recursive artifacts should then be supplied as inputs to `double_verify_nested_proof`. diff --git a/barretenberg/acir_tests/flows/prove_and_verify.sh b/barretenberg/acir_tests/flows/prove_and_verify.sh index 4d905538991..49dc9a86001 100755 --- a/barretenberg/acir_tests/flows/prove_and_verify.sh +++ b/barretenberg/acir_tests/flows/prove_and_verify.sh @@ -2,7 +2,11 @@ set -eu VFLAG=${VERBOSE:+-v} +FLAGS="-c $CRS_PATH $VFLAG" +if [ "${RECURSIVE}" = "true" ]; then + FLAGS="$FLAGS --recursive" +fi # This is the fastest flow, because it only generates pk/vk once, gate count once, etc. # It may not catch all class of bugs. -$BIN prove_and_verify $VFLAG -c $CRS_PATH -b ./target/program.json +$BIN prove_and_verify $FLAGS -b ./target/program.json diff --git a/barretenberg/acir_tests/flows/prove_and_verify_ultra_honk.sh b/barretenberg/acir_tests/flows/prove_and_verify_ultra_honk.sh index 16f2fd7f398..be9ad0e1231 100755 --- a/barretenberg/acir_tests/flows/prove_and_verify_ultra_honk.sh +++ b/barretenberg/acir_tests/flows/prove_and_verify_ultra_honk.sh @@ -2,5 +2,9 @@ set -eu VFLAG=${VERBOSE:+-v} +FLAGS="-c $CRS_PATH $VFLAG" +if [ "${RECURSIVE}" = "true" ]; then + FLAGS="$FLAGS --recursive" +fi -$BIN prove_and_verify_ultra_honk $VFLAG -c $CRS_PATH -b ./target/program.json +$BIN prove_and_verify_ultra_honk $FLAGS -b ./target/program.json diff --git a/barretenberg/acir_tests/flows/prove_and_verify_ultra_honk_program.sh b/barretenberg/acir_tests/flows/prove_and_verify_ultra_honk_program.sh index 65a6e400226..350bc5630ba 100755 --- a/barretenberg/acir_tests/flows/prove_and_verify_ultra_honk_program.sh +++ b/barretenberg/acir_tests/flows/prove_and_verify_ultra_honk_program.sh @@ -2,5 +2,9 @@ set -eu VFLAG=${VERBOSE:+-v} +FLAGS="-c $CRS_PATH $VFLAG" +if [ "${RECURSIVE}" = "true" ]; then + FLAGS="$FLAGS --recursive" +fi -$BIN prove_and_verify_ultra_honk_program $VFLAG -c $CRS_PATH -b ./target/program.json +$BIN prove_and_verify_ultra_honk_program $FLAGS -b ./target/program.json diff --git a/barretenberg/acir_tests/flows/prove_then_verify.sh b/barretenberg/acir_tests/flows/prove_then_verify.sh index 08d8ea21057..d12a12de216 100755 --- a/barretenberg/acir_tests/flows/prove_then_verify.sh +++ b/barretenberg/acir_tests/flows/prove_then_verify.sh @@ -4,6 +4,9 @@ set -eu VFLAG=${VERBOSE:+-v} BFLAG="-b ./target/program.json" FLAGS="-c $CRS_PATH $VFLAG" +if [ "${RECURSIVE}" = "true" ]; then + FLAGS="$FLAGS --recursive" +fi # Test we can perform the proof/verify flow. # This ensures we test independent pk construction through real/garbage witness data paths. diff --git a/barretenberg/acir_tests/flows/prove_then_verify_ultra_honk.sh b/barretenberg/acir_tests/flows/prove_then_verify_ultra_honk.sh index ac3bb9bc962..aaec7e54017 100755 --- a/barretenberg/acir_tests/flows/prove_then_verify_ultra_honk.sh +++ b/barretenberg/acir_tests/flows/prove_then_verify_ultra_honk.sh @@ -4,6 +4,9 @@ set -eux VFLAG=${VERBOSE:+-v} BFLAG="-b ./target/program.json" FLAGS="-c $CRS_PATH $VFLAG" +if [ "${RECURSIVE}" = "true" ]; then + FLAGS="$FLAGS --recursive" +fi # Test we can perform the proof/verify flow. # This ensures we test independent pk construction through real/garbage witness data paths. diff --git a/barretenberg/acir_tests/gen_inner_proof_inputs.sh b/barretenberg/acir_tests/gen_inner_proof_inputs.sh index 38392d84d89..9b68f0a1998 100755 --- a/barretenberg/acir_tests/gen_inner_proof_inputs.sh +++ b/barretenberg/acir_tests/gen_inner_proof_inputs.sh @@ -20,7 +20,7 @@ export BRANCH ./clone_test_vectors.sh -cd acir_tests/assert_statement_recursive +cd acir_tests/assert_statement PROOF_DIR=$PWD/proofs PROOF_PATH=$PROOF_DIR/$PROOF_NAME @@ -28,7 +28,7 @@ VFLAG=${VERBOSE:+-v} RFLAG=${RECURSIVE:+-r} echo "Write VK to file for assert_statement..." -$BIN write_vk $VFLAG -c $CRS_PATH -o ./target/vk +$BIN write_vk $VFLAG -c $CRS_PATH -o ./target/vk --recursive echo "Write VK as fields for recursion..." $BIN vk_as_fields $VFLAG -c $CRS_PATH -k ./target/vk -o ./target/vk_fields.json @@ -36,7 +36,7 @@ $BIN vk_as_fields $VFLAG -c $CRS_PATH -k ./target/vk -o ./target/vk_fields.json echo "Generate proof to file..." [ -d "$PROOF_DIR" ] || mkdir $PWD/proofs [ -e "$PROOF_PATH" ] || touch $PROOF_PATH -$BIN prove $VFLAG -c $CRS_PATH -b ./target/program.json -o "./proofs/$PROOF_NAME" +$BIN prove $VFLAG -c $CRS_PATH -b ./target/program.json -o "./proofs/$PROOF_NAME" --recursive echo "Write proof as fields for recursion..." $BIN proof_as_fields $VFLAG -c $CRS_PATH -p "./proofs/$PROOF_NAME" -k ./target/vk -o "./proofs/${PROOF_NAME}_fields.json" diff --git a/barretenberg/acir_tests/regenerate_verify_honk_proof_inputs.sh b/barretenberg/acir_tests/regenerate_verify_honk_proof_inputs.sh index ba811246596..e19abaabed7 100755 --- a/barretenberg/acir_tests/regenerate_verify_honk_proof_inputs.sh +++ b/barretenberg/acir_tests/regenerate_verify_honk_proof_inputs.sh @@ -17,7 +17,7 @@ fi export BRANCH # the program for which a proof will be recursively verified -PROGRAM=assert_statement_recursive +PROGRAM=assert_statement # the program containing the recursive verifier RECURSIVE_PROGRAM=verify_honk_proof diff --git a/barretenberg/acir_tests/run_acir_tests.sh b/barretenberg/acir_tests/run_acir_tests.sh index 1b8d413afe6..b31b8708e89 100755 --- a/barretenberg/acir_tests/run_acir_tests.sh +++ b/barretenberg/acir_tests/run_acir_tests.sh @@ -2,6 +2,7 @@ # Env var overrides: # BIN: to specify a different binary to test with (e.g. bb.js or bb.js-dev). # VERBOSE: to enable logging for each test. +# RECURSIVE: to enable --recursive for each test. set -eu # Catch when running in parallel @@ -19,6 +20,7 @@ VERBOSE=${VERBOSE:-} TEST_NAMES=("$@") # We get little performance benefit over 16 cores (in fact it can be worse). HARDWARE_CONCURRENCY=${HARDWARE_CONCURRENCY:-16} +RECURSIVE=${RECURSIVE:-false} FLOW_SCRIPT=$(realpath ./flows/${FLOW}.sh) @@ -28,7 +30,7 @@ else BIN=$(realpath $(which $BIN)) fi -export BIN CRS_PATH VERBOSE BRANCH +export BIN CRS_PATH VERBOSE BRANCH RECURSIVE # copy the gzipped acir test data from noir/noir-repo/test_programs to barretenberg/acir_tests ./clone_test_vectors.sh @@ -47,12 +49,12 @@ SKIP_ARRAY+=(regression_5045) # if HONK is false, we should skip verify_honk_proof if [ "$HONK" = false ]; then # Don't run programs with Honk recursive verifier - SKIP_ARRAY+=(verify_honk_proof double_verify_honk_proof double_verify_honk_proof_recursive) + SKIP_ARRAY+=(verify_honk_proof double_verify_honk_proof) fi if [ "$HONK" = true ]; then # Don't run programs with Plonk recursive verifier(s) - SKIP_ARRAY+=(single_verify_proof double_verify_proof double_verify_proof_recursive double_verify_nested_proof) + SKIP_ARRAY+=(single_verify_proof double_verify_proof double_verify_nested_proof) fi function test() { diff --git a/barretenberg/bbup/README.md b/barretenberg/bbup/README.md index 818dfe3fafa..a2c009fe5d0 100644 --- a/barretenberg/bbup/README.md +++ b/barretenberg/bbup/README.md @@ -45,7 +45,7 @@ You can install any specific version of `bb` with the `-v` flag. Example: bbup -v 0.56.0 ``` -You can also can pass [any Noir version](https://github.com/noir-lang/noir/tags) with the `-nv` flag, or specify `nightly` for the nightly version. Examples: +You can also pass [any Noir version](https://github.com/noir-lang/noir/tags) with the `-nv` flag, or specify `nightly` for the nightly version. Examples: ```bash bbup # installs the barretenberg version matching your current nargo version diff --git a/barretenberg/bootstrap_cache.sh b/barretenberg/bootstrap_cache.sh new file mode 100755 index 00000000000..252e25f2e9f --- /dev/null +++ b/barretenberg/bootstrap_cache.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -eu + +cd "$(dirname "$0")" + +# Run both tasks in the background +(cd cpp && ./bootstrap_cache.sh "$@") & +pid_cpp=$! +(cd ts && ./bootstrap_cache.sh "$@") & +pid_ts=$! + +# Wait for both processes and capture any non-zero exit codes +wait $pid_cpp || exit_code=$? +wait $pid_ts || exit_code=$? + +# Exit with the first non-zero exit code, if any +exit ${exit_code:-0} diff --git a/barretenberg/cpp/CMakeLists.txt b/barretenberg/cpp/CMakeLists.txt index ac795dac9f7..59e4a2565d4 100644 --- a/barretenberg/cpp/CMakeLists.txt +++ b/barretenberg/cpp/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.24 FATAL_ERROR) project( Barretenberg DESCRIPTION "BN254 elliptic curve library, and PLONK SNARK prover" - VERSION 0.61.0 # x-release-please-version + VERSION 0.62.0 # x-release-please-version LANGUAGES CXX C ) # Insert version into `bb` config file diff --git a/barretenberg/cpp/Earthfile b/barretenberg/cpp/Earthfile index 72b3fb7f218..390a7af6544 100644 --- a/barretenberg/cpp/Earthfile +++ b/barretenberg/cpp/Earthfile @@ -107,7 +107,15 @@ preset-msan-check: # install SRS needed for proving COPY --dir ./srs_db/+build/. srs_db RUN echo "Warning: If ./bin/client_ivc_tests is not found, there may be build failures above." - RUN cd build && ./bin/client_ivc_tests --gtest_also_run_disabled_tests + RUN cd build && ./bin/client_ivc_tests --gtest_filter="*BasicStructured" + +preset-check: + ARG preset + FROM +source + RUN cmake --preset $preset -Bbuild && cmake --build build --target client_ivc_tests + # install SRS needed for proving + COPY --dir ./srs_db/+build/. srs_db + RUN cd build && ./bin/client_ivc_tests --gtest_filter="*BasicStructured" preset-wasm: ARG TARGETARCH diff --git a/barretenberg/cpp/pil/avm/README.txt b/barretenberg/cpp/pil/avm/README.txt index cf63996a3c2..8d24c888096 100644 --- a/barretenberg/cpp/pil/avm/README.txt +++ b/barretenberg/cpp/pil/avm/README.txt @@ -4,7 +4,7 @@ Applied heuristic to assess the cost of a relation is given below. This will be used to determine whether it is worth to merge some relations into one. -N_mul = number of mulitplication in the relation +N_mul = number of multiplication in the relation N_add = number of additions/subtraction in the relation deg = degree of the relation (total degree of the polynomial) @@ -18,4 +18,4 @@ Future: There is an optimization in sumcheck protocol allowing to skip some enabled over 2 adjacent rows). However, this feature is not yet enabled in the AVM context. This might change the decision on whether some relations should be merged or not. Basically, merging relations would decrease the - likelihood to be disabled over adjacent rows. \ No newline at end of file + likelihood to be disabled over adjacent rows. diff --git a/barretenberg/cpp/src/CMakeLists.txt b/barretenberg/cpp/src/CMakeLists.txt index 018021e33f9..85fbaf19f38 100644 --- a/barretenberg/cpp/src/CMakeLists.txt +++ b/barretenberg/cpp/src/CMakeLists.txt @@ -63,6 +63,7 @@ endif() add_subdirectory(barretenberg/client_ivc) add_subdirectory(barretenberg/bb) +add_subdirectory(barretenberg/boomerang_value_detection) add_subdirectory(barretenberg/circuit_checker) add_subdirectory(barretenberg/commitment_schemes) add_subdirectory(barretenberg/commitment_schemes_recursion) diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index 052ea3774e6..3cfad6d4e46 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -148,13 +148,13 @@ std::string honk_vk_to_json(std::vector& data) * @return true if the proof is valid * @return false if the proof is invalid */ -bool proveAndVerify(const std::string& bytecodePath, const std::string& witnessPath) +bool proveAndVerify(const std::string& bytecodePath, const bool recursive, const std::string& witnessPath) { auto constraint_system = get_constraint_system(bytecodePath, /*honk_recursion=*/false); auto witness = get_witness(witnessPath); acir_proofs::AcirComposer acir_composer{ 0, verbose_logging }; - acir_composer.create_finalized_circuit(constraint_system, witness); + acir_composer.create_finalized_circuit(constraint_system, recursive, witness); init_bn254_crs(acir_composer.get_finalized_dyadic_circuit_size()); Timer pk_timer; @@ -179,7 +179,9 @@ bool proveAndVerify(const std::string& bytecodePath, const std::string& witnessP } template -bool proveAndVerifyHonkAcirFormat(acir_format::AcirFormat constraint_system, acir_format::WitnessVector witness) +bool proveAndVerifyHonkAcirFormat(acir_format::AcirFormat constraint_system, + const bool recursive, + acir_format::WitnessVector witness) { using Builder = Flavor::CircuitBuilder; using Prover = UltraProver_; @@ -191,7 +193,7 @@ bool proveAndVerifyHonkAcirFormat(acir_format::AcirFormat constraint_system, aci honk_recursion = true; } // Construct a bberg circuit from the acir representation - auto builder = acir_format::create_circuit(constraint_system, 0, witness, honk_recursion); + auto builder = acir_format::create_circuit(constraint_system, recursive, 0, witness, honk_recursion); // Construct Honk proof Prover prover{ builder }; @@ -213,7 +215,8 @@ bool proveAndVerifyHonkAcirFormat(acir_format::AcirFormat constraint_system, aci * @param bytecodePath Path to serialized acir circuit data * @param witnessPath Path to serialized acir witness data */ -template bool proveAndVerifyHonk(const std::string& bytecodePath, const std::string& witnessPath) +template +bool proveAndVerifyHonk(const std::string& bytecodePath, const bool recursive, const std::string& witnessPath) { bool honk_recursion = false; if constexpr (IsAnyOf) { @@ -223,7 +226,7 @@ template bool proveAndVerifyHonk(const std::string& bytec auto constraint_system = get_constraint_system(bytecodePath, honk_recursion); auto witness = get_witness(witnessPath); - return proveAndVerifyHonkAcirFormat(constraint_system, witness); + return proveAndVerifyHonkAcirFormat(constraint_system, recursive, witness); } /** @@ -235,18 +238,16 @@ template bool proveAndVerifyHonk(const std::string& bytec * follow. */ template -bool proveAndVerifyHonkProgram(const std::string& bytecodePath, const std::string& witnessPath) +bool proveAndVerifyHonkProgram(const std::string& bytecodePath, const bool recursive, const std::string& witnessPath) { bool honk_recursion = false; if constexpr (IsAnyOf) { honk_recursion = true; } auto program_stack = acir_format::get_acir_program_stack(bytecodePath, witnessPath, honk_recursion); - while (!program_stack.empty()) { auto stack_item = program_stack.back(); - - if (!proveAndVerifyHonkAcirFormat(stack_item.constraints, stack_item.witness)) { + if (!proveAndVerifyHonkAcirFormat(stack_item.constraints, recursive, stack_item.witness)) { return false; } program_stack.pop_back(); @@ -366,7 +367,8 @@ void client_ivc_prove_output_all_msgpack(const std::string& bytecodePath, bool is_kernel = false; for (Program& program : folding_stack) { // Construct a bberg circuit from the acir representation then accumulate it into the IVC - auto circuit = create_circuit(program.constraints, 0, program.witness, false, ivc.goblin.op_queue); + auto circuit = + create_circuit(program.constraints, true, 0, program.witness, false, ivc.goblin.op_queue); // Set the internal is_kernel flag based on the local mechanism only if it has not already been set to true if (!circuit.databus_propagation_data.is_kernel) { @@ -463,8 +465,12 @@ bool foldAndVerifyProgram(const std::string& bytecodePath, const std::string& wi auto stack_item = program_stack.back(); // Construct a bberg circuit from the acir representation - auto builder = acir_format::create_circuit( - stack_item.constraints, 0, stack_item.witness, /*honk_recursion=*/false, ivc.goblin.op_queue); + auto builder = acir_format::create_circuit(stack_item.constraints, + /*recursive=*/true, + 0, + stack_item.witness, + /*honk_recursion=*/false, + ivc.goblin.op_queue); // Set the internal is_kernel flag to trigger automatic appending of kernel logic if true builder.databus_propagation_data.is_kernel = is_kernel; @@ -514,7 +520,7 @@ void client_ivc_prove_output_all(const std::string& bytecodePath, // Construct a bberg circuit from the acir representation auto circuit = acir_format::create_circuit( - stack_item.constraints, 0, stack_item.witness, false, ivc.goblin.op_queue); + stack_item.constraints, true, 0, stack_item.witness, false, ivc.goblin.op_queue); circuit.databus_propagation_data.is_kernel = is_kernel; is_kernel = !is_kernel; // toggle on/off so every second circuit is intepreted as a kernel @@ -652,16 +658,19 @@ void prove_tube(const std::string& output_path) * * @param bytecodePath Path to the file containing the serialized circuit * @param witnessPath Path to the file containing the serialized witness - * @param recursive Whether to use recursive proof generation of non-recursive * @param outputPath Path to write the proof to + * @param recursive Whether to use recursive proof generation of non-recursive */ -void prove(const std::string& bytecodePath, const std::string& witnessPath, const std::string& outputPath) +void prove(const std::string& bytecodePath, + const std::string& witnessPath, + const std::string& outputPath, + const bool recursive) { auto constraint_system = get_constraint_system(bytecodePath, /*honk_recursion=*/false); auto witness = get_witness(witnessPath); acir_proofs::AcirComposer acir_composer{ 0, verbose_logging }; - acir_composer.create_finalized_circuit(constraint_system, witness); + acir_composer.create_finalized_circuit(constraint_system, recursive, witness); init_bn254_crs(acir_composer.get_finalized_dyadic_circuit_size()); acir_composer.init_proving_key(); auto proof = acir_composer.create_proof(); @@ -685,7 +694,8 @@ void prove(const std::string& bytecodePath, const std::string& witnessPath, cons * * @param bytecodePath Path to the file containing the serialized circuit */ -template void gateCount(const std::string& bytecodePath, bool honk_recursion) +template +void gateCount(const std::string& bytecodePath, bool recursive, bool honk_recursion) { // All circuit reports will be built into the string below std::string functions_string = "{\"functions\": [\n "; @@ -693,7 +703,7 @@ template void gateCount(const std::stri size_t i = 0; for (auto constraint_system : constraint_systems) { auto builder = acir_format::create_circuit( - constraint_system, 0, {}, honk_recursion, std::make_shared(), true); + constraint_system, recursive, 0, {}, honk_recursion, std::make_shared(), true); builder.finalize_circuit(/*ensure_nonzero=*/true); size_t circuit_size = builder.num_gates; vinfo("Calculated circuit size in gateCount: ", circuit_size); @@ -767,12 +777,13 @@ bool verify(const std::string& proof_path, const std::string& vk_path) * * @param bytecodePath Path to the file containing the serialized circuit * @param outputPath Path to write the verification key to + * @param recursive Whether to create a SNARK friendly circuit and key */ -void write_vk(const std::string& bytecodePath, const std::string& outputPath) +void write_vk(const std::string& bytecodePath, const std::string& outputPath, const bool recursive) { auto constraint_system = get_constraint_system(bytecodePath, false); acir_proofs::AcirComposer acir_composer{ 0, verbose_logging }; - acir_composer.create_finalized_circuit(constraint_system); + acir_composer.create_finalized_circuit(constraint_system, recursive); acir_composer.finalize_circuit(); init_bn254_crs(acir_composer.get_finalized_dyadic_circuit_size()); acir_composer.init_proving_key(); @@ -787,11 +798,11 @@ void write_vk(const std::string& bytecodePath, const std::string& outputPath) } } -void write_pk(const std::string& bytecodePath, const std::string& outputPath) +void write_pk(const std::string& bytecodePath, const std::string& outputPath, const bool recursive) { auto constraint_system = get_constraint_system(bytecodePath, /*honk_recursion=*/false); acir_proofs::AcirComposer acir_composer{ 0, verbose_logging }; - acir_composer.create_finalized_circuit(constraint_system); + acir_composer.create_finalized_circuit(constraint_system, recursive); acir_composer.finalize_circuit(); init_bn254_crs(acir_composer.get_finalized_dyadic_circuit_size()); auto pk = acir_composer.init_proving_key(); @@ -1069,7 +1080,9 @@ bool avm_verify(const std::filesystem::path& proof_path, const std::filesystem:: * @return UltraProver_ */ template -UltraProver_ compute_valid_prover(const std::string& bytecodePath, const std::string& witnessPath) +UltraProver_ compute_valid_prover(const std::string& bytecodePath, + const std::string& witnessPath, + const bool recursive) { using Builder = Flavor::CircuitBuilder; using Prover = UltraProver_; @@ -1084,7 +1097,7 @@ UltraProver_ compute_valid_prover(const std::string& bytecodePath, const witness = get_witness(witnessPath); } - auto builder = acir_format::create_circuit(constraint_system, 0, witness, honk_recursion); + auto builder = acir_format::create_circuit(constraint_system, recursive, 0, witness, honk_recursion); auto prover = Prover{ builder }; init_bn254_crs(prover.proving_key->proving_key.circuit_size); return std::move(prover); @@ -1102,13 +1115,16 @@ UltraProver_ compute_valid_prover(const std::string& bytecodePath, const * @param outputPath Path to write the proof to */ template -void prove_honk(const std::string& bytecodePath, const std::string& witnessPath, const std::string& outputPath) +void prove_honk(const std::string& bytecodePath, + const std::string& witnessPath, + const std::string& outputPath, + const bool recursive) { // using Builder = Flavor::CircuitBuilder; using Prover = UltraProver_; // Construct Honk proof - Prover prover = compute_valid_prover(bytecodePath, witnessPath); + Prover prover = compute_valid_prover(bytecodePath, witnessPath, recursive); auto proof = prover.construct_proof(); if (outputPath == "-") { writeRawBytesToStdout(to_buffer(proof)); @@ -1163,13 +1179,14 @@ template bool verify_honk(const std::string& proof_path, * @param bytecodePath Path to the file containing the serialized circuit * @param outputPath Path to write the verification key to */ -template void write_vk_honk(const std::string& bytecodePath, const std::string& outputPath) +template +void write_vk_honk(const std::string& bytecodePath, const std::string& outputPath, const bool recursive) { using Prover = UltraProver_; using VerificationKey = Flavor::VerificationKey; // Construct a verification key from a partial form of the proving key which only has precomputed entities - Prover prover = compute_valid_prover(bytecodePath, ""); + Prover prover = compute_valid_prover(bytecodePath, "", recursive); VerificationKey vk(prover.proving_key->proving_key); auto serialized_vk = to_buffer(vk); @@ -1193,7 +1210,8 @@ template void write_vk_honk(const std::string& bytecodePa template void write_recursion_inputs_honk(const std::string& bytecodePath, const std::string& witnessPath, - const std::string& outputPath) + const std::string& outputPath, + const bool recursive) { using Builder = Flavor::CircuitBuilder; using Prover = UltraProver_; @@ -1201,9 +1219,9 @@ void write_recursion_inputs_honk(const std::string& bytecodePath, using FF = Flavor::FF; bool honk_recursion = true; - auto constraints = get_constraint_system(bytecodePath, /*honk_recursion=*/true); + auto constraints = get_constraint_system(bytecodePath, honk_recursion); auto witness = get_witness(witnessPath); - auto builder = acir_format::create_circuit(constraints, 0, witness, honk_recursion); + auto builder = acir_format::create_circuit(constraints, recursive, 0, witness, honk_recursion); // Construct Honk proof and verification key Prover prover{ builder }; @@ -1282,14 +1300,18 @@ template void vk_as_fields_honk(const std::string& vk_pat * @param bytecodePath Path to the file containing the serialized circuit * @param witnessPath Path to the file containing the serialized witness * @param outputPath Directory into which we write the proof and verification key data + * @param recursive Whether to a build SNARK friendly proof */ -void prove_output_all(const std::string& bytecodePath, const std::string& witnessPath, const std::string& outputPath) +void prove_output_all(const std::string& bytecodePath, + const std::string& witnessPath, + const std::string& outputPath, + const bool recursive) { auto constraint_system = get_constraint_system(bytecodePath, /*honk_recursion=*/false); auto witness = get_witness(witnessPath); acir_proofs::AcirComposer acir_composer{ 0, verbose_logging }; - acir_composer.create_finalized_circuit(constraint_system, witness); + acir_composer.create_finalized_circuit(constraint_system, recursive, witness); acir_composer.finalize_circuit(); init_bn254_crs(acir_composer.get_finalized_dyadic_circuit_size()); acir_composer.init_proving_key(); @@ -1335,11 +1357,13 @@ void prove_output_all(const std::string& bytecodePath, const std::string& witnes * @param bytecodePath Path to the file containing the serialized circuit * @param witnessPath Path to the file containing the serialized witness * @param outputPath Directory into which we write the proof and verification key data + * @param recursive Whether to build a SNARK friendly proof */ template void prove_honk_output_all(const std::string& bytecodePath, const std::string& witnessPath, - const std::string& outputPath) + const std::string& outputPath, + const bool recursive) { using Builder = Flavor::CircuitBuilder; using Prover = UltraProver_; @@ -1353,7 +1377,7 @@ void prove_honk_output_all(const std::string& bytecodePath, auto constraint_system = get_constraint_system(bytecodePath, honk_recursion); auto witness = get_witness(witnessPath); - auto builder = acir_format::create_circuit(constraint_system, 0, witness, honk_recursion); + auto builder = acir_format::create_circuit(constraint_system, recursive, 0, witness, honk_recursion); // Construct Honk proof Prover prover{ builder }; @@ -1421,6 +1445,7 @@ int main(int argc, char* argv[]) std::string vk_path = get_option(args, "-k", "./target/vk"); std::string pk_path = get_option(args, "-r", "./target/pk"); bool honk_recursion = flag_present(args, "-h"); + bool recursive = flag_present(args, "--recursive"); // Not every flavor handles it. CRS_PATH = get_option(args, "-c", CRS_PATH); // Skip CRS initialization for any command which doesn't require the CRS. @@ -1429,21 +1454,20 @@ int main(int argc, char* argv[]) return 0; } if (command == "prove_and_verify") { - return proveAndVerify(bytecode_path, witness_path) ? 0 : 1; + return proveAndVerify(bytecode_path, recursive, witness_path) ? 0 : 1; } if (command == "prove_and_verify_ultra_honk") { - return proveAndVerifyHonk(bytecode_path, witness_path) ? 0 : 1; + return proveAndVerifyHonk(bytecode_path, recursive, witness_path) ? 0 : 1; } if (command == "prove_and_verify_mega_honk") { - return proveAndVerifyHonk(bytecode_path, witness_path) ? 0 : 1; + return proveAndVerifyHonk(bytecode_path, recursive, witness_path) ? 0 : 1; } if (command == "prove_and_verify_ultra_honk_program") { - return proveAndVerifyHonkProgram(bytecode_path, witness_path) ? 0 : 1; + return proveAndVerifyHonkProgram(bytecode_path, recursive, witness_path) ? 0 : 1; } if (command == "prove_and_verify_mega_honk_program") { - return proveAndVerifyHonkProgram(bytecode_path, witness_path) ? 0 : 1; + return proveAndVerifyHonkProgram(bytecode_path, recursive, witness_path) ? 0 : 1; } - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1050) we need a verify_client_ivc bb cli command // TODO(#7371): remove this if (command == "client_ivc_prove_output_all_msgpack") { std::filesystem::path output_dir = get_option(args, "-o", "./target"); @@ -1469,16 +1493,16 @@ int main(int argc, char* argv[]) if (command == "prove") { std::string output_path = get_option(args, "-o", "./proofs/proof"); - prove(bytecode_path, witness_path, output_path); + prove(bytecode_path, witness_path, output_path, recursive); } else if (command == "prove_output_all") { std::string output_path = get_option(args, "-o", "./proofs"); - prove_output_all(bytecode_path, witness_path, output_path); + prove_output_all(bytecode_path, witness_path, output_path, recursive); } else if (command == "prove_ultra_honk_output_all") { std::string output_path = get_option(args, "-o", "./proofs"); - prove_honk_output_all(bytecode_path, witness_path, output_path); + prove_honk_output_all(bytecode_path, witness_path, output_path, recursive); } else if (command == "prove_mega_honk_output_all") { std::string output_path = get_option(args, "-o", "./proofs"); - prove_honk_output_all(bytecode_path, witness_path, output_path); + prove_honk_output_all(bytecode_path, witness_path, output_path, recursive); } else if (command == "client_ivc_prove_output_all") { std::string output_path = get_option(args, "-o", "./target"); client_ivc_prove_output_all(bytecode_path, witness_path, output_path); @@ -1491,9 +1515,9 @@ int main(int argc, char* argv[]) auto tube_vk_path = output_path + "/vk"; return verify_honk(tube_proof_path, tube_vk_path) ? 0 : 1; } else if (command == "gates") { - gateCount(bytecode_path, honk_recursion); + gateCount(bytecode_path, recursive, honk_recursion); } else if (command == "gates_mega_honk") { - gateCount(bytecode_path, honk_recursion); + gateCount(bytecode_path, recursive, honk_recursion); } else if (command == "verify") { return verify(proof_path, vk_path) ? 0 : 1; } else if (command == "contract") { @@ -1504,10 +1528,10 @@ int main(int argc, char* argv[]) contract_honk(output_path, vk_path); } else if (command == "write_vk") { std::string output_path = get_option(args, "-o", "./target/vk"); - write_vk(bytecode_path, output_path); + write_vk(bytecode_path, output_path, recursive); } else if (command == "write_pk") { std::string output_path = get_option(args, "-o", "./target/pk"); - write_pk(bytecode_path, output_path); + write_pk(bytecode_path, output_path, recursive); } else if (command == "proof_as_fields") { std::string output_path = get_option(args, "-o", proof_path + "_fields.json"); proof_as_fields(proof_path, vk_path, output_path); @@ -1516,7 +1540,7 @@ int main(int argc, char* argv[]) vk_as_fields(vk_path, output_path); } else if (command == "write_recursion_inputs_honk") { std::string output_path = get_option(args, "-o", "./target"); - write_recursion_inputs_honk(bytecode_path, witness_path, output_path); + write_recursion_inputs_honk(bytecode_path, witness_path, output_path, recursive); #ifndef DISABLE_AZTEC_VM } else if (command == "avm_prove") { std::filesystem::path avm_calldata_path = get_option(args, "--avm-calldata", "./target/avm_calldata.bin"); @@ -1533,31 +1557,31 @@ int main(int argc, char* argv[]) #endif } else if (command == "prove_ultra_honk") { std::string output_path = get_option(args, "-o", "./proofs/proof"); - prove_honk(bytecode_path, witness_path, output_path); + prove_honk(bytecode_path, witness_path, output_path, recursive); } else if (command == "prove_ultra_keccak_honk") { std::string output_path = get_option(args, "-o", "./proofs/proof"); - prove_honk(bytecode_path, witness_path, output_path); + prove_honk(bytecode_path, witness_path, output_path, recursive); } else if (command == "prove_ultra_keccak_honk_output_all") { std::string output_path = get_option(args, "-o", "./proofs/proof"); - prove_honk_output_all(bytecode_path, witness_path, output_path); + prove_honk_output_all(bytecode_path, witness_path, output_path, recursive); } else if (command == "verify_ultra_honk") { return verify_honk(proof_path, vk_path) ? 0 : 1; } else if (command == "verify_ultra_keccak_honk") { return verify_honk(proof_path, vk_path) ? 0 : 1; } else if (command == "write_vk_ultra_honk") { std::string output_path = get_option(args, "-o", "./target/vk"); - write_vk_honk(bytecode_path, output_path); + write_vk_honk(bytecode_path, output_path, recursive); } else if (command == "write_vk_ultra_keccak_honk") { std::string output_path = get_option(args, "-o", "./target/vk"); - write_vk_honk(bytecode_path, output_path); + write_vk_honk(bytecode_path, output_path, recursive); } else if (command == "prove_mega_honk") { std::string output_path = get_option(args, "-o", "./proofs/proof"); - prove_honk(bytecode_path, witness_path, output_path); + prove_honk(bytecode_path, witness_path, output_path, recursive); } else if (command == "verify_mega_honk") { return verify_honk(proof_path, vk_path) ? 0 : 1; } else if (command == "write_vk_mega_honk") { std::string output_path = get_option(args, "-o", "./target/vk"); - write_vk_honk(bytecode_path, output_path); + write_vk_honk(bytecode_path, output_path, recursive); } else if (command == "proof_as_fields_honk") { std::string output_path = get_option(args, "-o", proof_path + "_fields.json"); proof_as_fields_honk(proof_path, output_path); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/basics_bench/basics.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/basics_bench/basics.bench.cpp index 8591e02d73a..586199aea53 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/basics_bench/basics.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/basics_bench/basics.bench.cpp @@ -24,6 +24,7 @@ #include "barretenberg/common/op_count.hpp" #include "barretenberg/common/thread.hpp" #include "barretenberg/ecc/curves/bn254/bn254.hpp" +#include "barretenberg/numeric/random/engine.hpp" #include "barretenberg/polynomials/polynomial.hpp" #include "barretenberg/srs/global_crs.hpp" #include @@ -466,6 +467,19 @@ void pippenger(State& state) benchmark::DoNotOptimize(ck->commit(pol)); } } + +void bn254fr_random(State& state) +{ + numeric::RNG& engine = numeric::get_randomness(); + for (auto _ : state) { + state.PauseTiming(); + size_t num_cycles = 1UL << static_cast(state.range(0)); + state.ResumeTiming(); + for (size_t i = 0; i < num_cycles; i++) { + benchmark::DoNotOptimize(fr::random_element(&engine)); + } + } +} } // namespace BENCHMARK(parallel_for_field_element_addition)->Unit(kMicrosecond)->DenseRange(0, MAX_REPETITION_LOG); @@ -485,4 +499,5 @@ BENCHMARK(sequential_copy)->Unit(kMicrosecond)->DenseRange(20, 25); BENCHMARK(uint_multiplication)->Unit(kMicrosecond)->DenseRange(12, 27); BENCHMARK(uint_extended_multiplication)->Unit(kMicrosecond)->DenseRange(12, 27); BENCHMARK(pippenger)->Unit(kMicrosecond)->DenseRange(16, 20)->Setup(DoPippengerSetup)->Iterations(5); +BENCHMARK(bn254fr_random)->Unit(kMicrosecond)->DenseRange(10, 20); BENCHMARK_MAIN(); \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/CMakeLists.txt new file mode 100644 index 00000000000..dae39eee2de --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/CMakeLists.txt @@ -0,0 +1 @@ +barretenberg_module(boomerang_value_detection stdlib_circuit_builders circuit_checker stdlib_primitives numeric stdlib_aes128 stdlib_sha256 stdlib_blake2s stdlib_blake3s) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.cpp new file mode 100644 index 00000000000..1fe02660f08 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.cpp @@ -0,0 +1,824 @@ +#include "graph.hpp" +#include +#include + +using namespace bb::plookup; +using namespace bb; + +/** + * @brief this method removes duplicate variables from a gate, + * converts variables from a gate to real variables, and then + * updates variable gates count for real variable indexes + */ + +template +inline void Graph_::process_gate_variables(UltraCircuitBuilder& ultra_circuit_builder, + std::vector& gate_variables) +{ + auto unique_variables = std::unique(gate_variables.begin(), gate_variables.end()); + gate_variables.erase(unique_variables, gate_variables.end()); + if (gate_variables.empty()) { + return; + } + for (size_t i = 0; i < gate_variables.size(); i++) { + gate_variables[i] = this->to_real(ultra_circuit_builder, gate_variables[i]); + } + for (const auto& variable_index : gate_variables) { + variables_gate_counts[variable_index] += 1; + } +} + +/** + * @brief this method implements connected components from arithmetic gates + * @tparam FF + * @param ultra_circuit_builder + * @param index + * @return std::vector + */ + +template +inline std::vector Graph_::get_arithmetic_gate_connected_component( + bb::UltraCircuitBuilder& ultra_circuit_builder, size_t index) +{ + auto& arithmetic_block = ultra_circuit_builder.blocks.arithmetic; + uint32_t left_idx = arithmetic_block.w_l()[index]; + uint32_t right_idx = arithmetic_block.w_r()[index]; + uint32_t out_idx = arithmetic_block.w_o()[index]; + uint32_t fourth_idx = arithmetic_block.w_4()[index]; + auto q_m = arithmetic_block.q_m()[index]; + auto q_1 = arithmetic_block.q_1()[index]; + auto q_2 = arithmetic_block.q_2()[index]; + auto q_3 = arithmetic_block.q_3()[index]; + auto q_4 = arithmetic_block.q_4()[index]; + std::vector gate_variables = {}; + if (q_m != 0 || q_1 != 1 || q_2 != 0 || q_3 != 0 || q_4 != 0) { + // this is not the gate for fix_witness, so we have to process this gate + if (arithmetic_block.q_arith()[index] > 0) { + if (q_m != 0) { + gate_variables.emplace_back(left_idx); + gate_variables.emplace_back(right_idx); + } + if (q_1 != 0) { + gate_variables.emplace_back(left_idx); + } + if (q_2 != 0) { + gate_variables.emplace_back(right_idx); + } + if (q_3 != 0) { + gate_variables.emplace_back(out_idx); + } + if (q_4 != 0) { + gate_variables.emplace_back(fourth_idx); + } + if (arithmetic_block.q_arith()[index] == 2) { + // We have to use w_4_shift from the next gate + // if and only if the current gate isn't last, cause we can't + // look into the next gate + if (index != arithmetic_block.size() - 1) { + uint32_t fourth_shift_idx = arithmetic_block.w_4()[index + 1]; + gate_variables.emplace_back(fourth_shift_idx); + } + } + if (arithmetic_block.q_arith()[index] == 3) { + // TODO(daniel): want to process this case later + ASSERT(false); + } + } + } + this->process_gate_variables(ultra_circuit_builder, gate_variables); + return gate_variables; +} + +/** + * @brief this method creates connected components from elliptic gates + * @tparam FF + * @param ultra_circuit_builder + * @param index + * @return std::vector + */ + +template +inline std::vector Graph_::get_elliptic_gate_connected_component( + bb::UltraCircuitBuilder& ultra_circuit_builder, size_t index) +{ + auto& elliptic_block = ultra_circuit_builder.blocks.elliptic; + std::vector gate_variables = {}; + bool is_elliptic_gate = elliptic_block.q_elliptic()[index] == 1; + bool is_elliptic_add_gate = elliptic_block.q_1()[index] != 0 && elliptic_block.q_m()[index] == 0; + bool is_elliptic_dbl_gate = elliptic_block.q_1()[index] == 0 && elliptic_block.q_m()[index] == 1; + if (is_elliptic_gate) { + auto right_idx = elliptic_block.w_r()[index]; + auto out_idx = elliptic_block.w_o()[index]; + gate_variables.emplace_back(right_idx); + gate_variables.emplace_back(out_idx); + if (index != elliptic_block.size() - 1) { + if (is_elliptic_add_gate) { + // if this gate is ecc_add_gate, we have to get indices x2, x3, y3, y2 from the next gate + gate_variables.emplace_back(elliptic_block.w_l()[index + 1]); + gate_variables.emplace_back(elliptic_block.w_r()[index + 1]); + gate_variables.emplace_back(elliptic_block.w_o()[index + 1]); + gate_variables.emplace_back(elliptic_block.w_4()[index + 1]); + } + if (is_elliptic_dbl_gate) { + // if this gate is ecc_dbl_gate, we have to indices x3, y3 from right and output wires + gate_variables.emplace_back(elliptic_block.w_r()[index + 1]); + gate_variables.emplace_back(elliptic_block.w_o()[index + 1]); + } + } + } + this->process_gate_variables(ultra_circuit_builder, gate_variables); + return gate_variables; +} + +/** + * @brief this method creates connected components from sorted constraints + * + * @tparam FF + * @param ultra_circuit_builder + * @param index + * @return std::vector + */ + +template +inline std::vector Graph_::get_sort_constraint_connected_component( + bb::UltraCircuitBuilder& ultra_circuit_builder, size_t index) +{ + auto& delta_range_block = ultra_circuit_builder.blocks.delta_range; + std::vector gate_variables = {}; + if (delta_range_block.q_delta_range()[index] == 1) { + auto left_idx = delta_range_block.w_l()[index]; + auto right_idx = delta_range_block.w_r()[index]; + auto out_idx = delta_range_block.w_o()[index]; + auto fourth_idx = delta_range_block.w_4()[index]; + gate_variables.insert(gate_variables.end(), { left_idx, right_idx, out_idx, fourth_idx }); + } + this->process_gate_variables(ultra_circuit_builder, gate_variables); + return gate_variables; +} + +/** + * @brief this method creates connected components from plookup gates + * + * @tparam FF + * @param ultra_circuit_builder + * @param index + * @return std::vector + */ + +template +inline std::vector Graph_::get_plookup_gate_connected_component( + bb::UltraCircuitBuilder& ultra_circuit_builder, size_t index) +{ + std::vector gate_variables; + auto& lookup_block = ultra_circuit_builder.blocks.lookup; + auto q_2 = lookup_block.q_2()[index]; + auto q_m = lookup_block.q_m()[index]; + auto q_c = lookup_block.q_c()[index]; + auto left_idx = lookup_block.w_l()[index]; + auto right_idx = lookup_block.w_r()[index]; + auto out_idx = lookup_block.w_o()[index]; + gate_variables.emplace_back(left_idx); + gate_variables.emplace_back(right_idx); + gate_variables.emplace_back(out_idx); + if (index < lookup_block.size() - 1) { + if (q_2 != 0 || q_m != 0 || q_c != 0) { + if (q_2 != 0) { + gate_variables.emplace_back(lookup_block.w_l()[index + 1]); + } + if (q_m != 0) { + gate_variables.emplace_back(lookup_block.w_r()[index + 1]); + } + if (q_c != 0) { + gate_variables.emplace_back(lookup_block.w_o()[index + 1]); + } + } + } + this->process_gate_variables(ultra_circuit_builder, gate_variables); + return gate_variables; +} + +/** + * @brief Construct a new Graph from Ultra Circuit Builder + * @tparam FF + * @param ultra_circuit_constructor + */ + +template Graph_::Graph_(bb::UltraCircuitBuilder& ultra_circuit_constructor) +{ + this->variables_gate_counts = + std::unordered_map(ultra_circuit_constructor.real_variable_index.size()); + this->variable_adjacency_lists = + std::unordered_map>(ultra_circuit_constructor.real_variable_index.size()); + this->variables_degree = std::unordered_map(ultra_circuit_constructor.real_variable_index.size()); + for (const auto& variable_index : ultra_circuit_constructor.real_variable_index) { + variables_gate_counts[variable_index] = 0; + variables_degree[variable_index] = 0; + variable_adjacency_lists[variable_index] = {}; + } + + std::map constant_variable_indices = ultra_circuit_constructor.constant_variable_indices; + const auto& arithmetic_block = ultra_circuit_constructor.blocks.arithmetic; + auto arithmetic_gates_numbers = arithmetic_block.size(); + bool arithmetic_gates_exist = arithmetic_gates_numbers > 0; + if (arithmetic_gates_exist) { + for (size_t i = 0; i < arithmetic_gates_numbers; i++) { + auto gate_variables = this->get_arithmetic_gate_connected_component(ultra_circuit_constructor, i); + this->connect_all_variables_in_vector(ultra_circuit_constructor, gate_variables, false); + } + } + const auto& elliptic_block = ultra_circuit_constructor.blocks.elliptic; + auto elliptic_gates_numbers = elliptic_block.size(); + bool elliptic_gates_exist = elliptic_gates_numbers > 0; + if (elliptic_gates_exist) { + for (size_t i = 0; i < elliptic_gates_numbers; i++) { + std::vector gate_variables = + this->get_elliptic_gate_connected_component(ultra_circuit_constructor, i); + this->connect_all_variables_in_vector(ultra_circuit_constructor, gate_variables, false); + } + } + const auto& range_block = ultra_circuit_constructor.blocks.delta_range; + auto range_gates = range_block.size(); + bool range_gates_exists = range_gates > 0; + if (range_gates_exists) { + std::vector sorted_variables; + for (size_t i = 0; i < range_gates; i++) { + auto current_gate = this->get_sort_constraint_connected_component(ultra_circuit_constructor, i); + if (current_gate.empty()) { + this->connect_all_variables_in_vector(ultra_circuit_constructor, sorted_variables, true); + sorted_variables.clear(); + } else { + sorted_variables.insert(sorted_variables.end(), current_gate.begin(), current_gate.end()); + } + } + } + + const auto& lookup_block = ultra_circuit_constructor.blocks.lookup; + auto lookup_gates = lookup_block.size(); + bool lookup_gates_exists = lookup_gates > 0; + if (lookup_gates_exists) { + for (size_t i = 0; i < lookup_gates; i++) { + std::vector variable_indices = + this->get_plookup_gate_connected_component(ultra_circuit_constructor, i); + this->connect_all_variables_in_vector(ultra_circuit_constructor, variable_indices, false); + } + } +} + +/** + * @brief this method checks whether the variable with given index is not constant + * @tparam FF + * @param ultra_circuit_builder + * @param variable_index + * @return true + * @return false + */ + +template +bool Graph_::check_is_not_constant_variable(bb::UltraCircuitBuilder& ultra_circuit_builder, + const uint32_t& variable_index) +{ + bool is_not_constant = true; + auto constant_variable_indices = ultra_circuit_builder.constant_variable_indices; + for (const auto& pair : constant_variable_indices) { + if (pair.second == ultra_circuit_builder.real_variable_index[variable_index]) { + is_not_constant = false; + break; + } + } + return is_not_constant; +} + +/** + * @brief this method adds connection between 2 variables, if they are in one gate, they are not constrant variables, + * and they have different indexes + * @tparam FF + * @param ultra_circuit_builder + * @param variables_vector + * @param is_sorted_variables + */ + +template +void Graph_::connect_all_variables_in_vector(bb::UltraCircuitBuilder& ultra_circuit_builder, + const std::vector& variables_vector, + bool is_sorted_variables) +{ + if (variables_vector.empty()) { + return; + } + if (is_sorted_variables) { + for (size_t i = 0; i < variables_vector.size() - 1; i++) { + if (variables_vector[i] != ultra_circuit_builder.zero_idx && + variables_vector[i + 1] != ultra_circuit_builder.zero_idx && + variables_vector[i] != variables_vector[i + 1]) { + { + bool first_variable_is_not_constant = + this->check_is_not_constant_variable(ultra_circuit_builder, variables_vector[i]); + bool second_variable_is_not_constant = + this->check_is_not_constant_variable(ultra_circuit_builder, variables_vector[i + 1]); + if (first_variable_is_not_constant && second_variable_is_not_constant) { + this->add_new_edge(variables_vector[i], variables_vector[i + 1]); + } + } + } + } + } else { + for (size_t i = 0; i < variables_vector.size() - 1; i++) { + for (size_t j = i + 1; j < variables_vector.size(); j++) { + if (variables_vector[i] != ultra_circuit_builder.zero_idx && + variables_vector[j] != ultra_circuit_builder.zero_idx && + variables_vector[i] != variables_vector[j]) { + + bool first_variable_is_not_constant = + this->check_is_not_constant_variable(ultra_circuit_builder, variables_vector[i]); + bool second_variable_is_not_constant = + this->check_is_not_constant_variable(ultra_circuit_builder, variables_vector[j]); + if (first_variable_is_not_constant && second_variable_is_not_constant) { + this->add_new_edge(variables_vector[i], variables_vector[j]); + } + } + } + } + } +} + +/** + * @brief this method creates an edge between two variables in graph. All needed checks in a function above + * @tparam FF + * @param first_variable_index + * @param second_variable_index + */ + +template +void Graph_::add_new_edge(const uint32_t& first_variable_index, const uint32_t& second_variable_index) +{ + variable_adjacency_lists[first_variable_index].emplace_back(second_variable_index); + variable_adjacency_lists[second_variable_index].emplace_back(first_variable_index); + variables_degree[first_variable_index] += 1; + variables_degree[second_variable_index] += 1; +} + +/** + * @brief this method implements depth-first search algorithm for undirected graphs + * @tparam FF + * @param variable_index + * @param is_used + * @param connected_component + */ + +template +void Graph_::depth_first_search(const uint32_t& variable_index, + std::unordered_set& is_used, + std::vector& connected_component) +{ + std::stack variable_stack; + variable_stack.push(variable_index); + while (!variable_stack.empty()) { + uint32_t current_index = variable_stack.top(); + variable_stack.pop(); + if (!is_used.contains(current_index)) { + is_used.insert(current_index); + connected_component.emplace_back(current_index); + for (const auto& it : variable_adjacency_lists[current_index]) { + variable_stack.push(it); + } + } + } +} + +/** + * @brief this methond finds all connected components in the graph described by adjacency lists + * @tparam FF + * @return std::vector> + */ + +template std::vector> Graph_::find_connected_components() +{ + std::unordered_set is_used; + std::vector> connected_components; + for (const auto& pair : variable_adjacency_lists) { + if (pair.first != 0 && variables_degree[pair.first] > 0) { + if (!is_used.contains(pair.first)) { + std::vector connected_component; + this->depth_first_search(pair.first, is_used, connected_component); + std::sort(connected_component.begin(), connected_component.end()); + connected_components.emplace_back(connected_component); + } + } + } + return connected_components; +} + +/** + * @brief this method removes variables that were created in a function decompose_into_default_range + * because they are false cases and don't give any useful information about security of the circuit. + * decompose_into_default_range function creates addition gates with shifts for intermediate variables, + * i.e. variables from left, right and output wires. They have variable gates count = 1 or 2, but they are not + * dangerous. so, we have to remove these variables from the analyzer. The situation is dangerous, if first variable + * from accumulators have variables gate count = 1. It means that it was used only in decompose gate, and it's not + * properly constrained. + * @tparam FF + * @param ultra_circuit_constructor + * @param variables_in_one_gate + * @param index + * @return size_t + */ + +template +inline size_t Graph_::process_current_decompose_chain(bb::UltraCircuitBuilder& ultra_circuit_constructor, + std::unordered_set& variables_in_one_gate, + size_t index) +{ + auto& arithmetic_block = ultra_circuit_constructor.blocks.arithmetic; + auto zero_idx = ultra_circuit_constructor.zero_idx; + size_t current_index = index; + std::vector accumulators_indices; + while (true) { + // we have to remove left, right and output wires of the current gate, cause they'are new_limbs, and they are + // useless for the analyzer + auto fourth_idx = arithmetic_block.w_4()[current_index]; + accumulators_indices.emplace_back(this->to_real(ultra_circuit_constructor, fourth_idx)); + auto left_idx = arithmetic_block.w_l()[current_index]; + if (left_idx != zero_idx) { + variables_in_one_gate.erase(this->to_real(ultra_circuit_constructor, left_idx)); + } + auto right_idx = arithmetic_block.w_r()[current_index]; + if (right_idx != zero_idx) { + variables_in_one_gate.erase(this->to_real(ultra_circuit_constructor, right_idx)); + } + auto out_idx = arithmetic_block.w_o()[current_index]; + if (out_idx != zero_idx) { + variables_in_one_gate.erase(this->to_real(ultra_circuit_constructor, out_idx)); + } + auto q_arith = arithmetic_block.q_arith()[current_index]; + if (q_arith == 1 || current_index == arithmetic_block.size() - 1) { + // this is the last gate in this chain, or we can't go next, so we have to stop a loop + break; + } + current_index++; + } + for (size_t i = 0; i < accumulators_indices.size(); i++) { + if (i == 0) { + // the first variable in accumulators is the variable which decompose was created. So, we have to decrement + // variable_gate_counts for this variable + variables_gate_counts[accumulators_indices[i]] -= 1; + } else { + // next accumulators are useless variables that are not interested for the analyzer. So, for these variables + // we can nullify variables_gate_counts + variables_gate_counts[accumulators_indices[i]] = 0; + } + } + // we don't want to make variables_gate_counts for intermediate variables negative, so, can go to the next gates + return current_index; +} + +/** + * @brief this method gets the endpoints of the decompose chains. For that it has to clean variable_index + from unnecessary variables for example, left, right, output wires and go through all decompose chain + * @tparam FF + * @param ultra_circuit_builder + * @param variables_in_one_gate + * @param decompose_variables + */ + +template +inline void Graph_::remove_unnecessary_decompose_variables(bb::UltraCircuitBuilder& ultra_circuit_builder, + std::unordered_set& variables_in_one_gate, + const std::unordered_set& decompose_variables) +{ + auto is_power_two = [&](const uint256_t& number) { return number > 0 && ((number & (number - 1)) == 0); }; + auto find_position = [&](uint32_t variable_index) { + return decompose_variables.contains(this->to_real(ultra_circuit_builder, variable_index)); + }; + auto& arithmetic_block = ultra_circuit_builder.blocks.arithmetic; + if (arithmetic_block.size() > 0) { + for (size_t i = 0; i < arithmetic_block.size(); i++) { + auto q_1 = arithmetic_block.q_1()[i]; + auto q_2 = arithmetic_block.q_2()[i]; + auto q_3 = arithmetic_block.q_3()[i]; + // big addition gate from decompose has selectors, which have the next property: + // q_1 = (1) << shifts[0], target_range_bitnum * (3 * i), + // q_2 = (1) << shifts[1], target_range_bitnum * (3 * i + 1), + // q_3 = (1) << shifts[2], target_range_bitnum * (3 * i + 2) + // so, they are power of two and satisfying the following equality: q_2 * q_2 = q_1 * q_3 + // this way we can differ them from other arithmetic gates + bool q_1_is_power_two = is_power_two(q_1); + bool q_2_is_power_two = is_power_two(q_2); + bool q_3_is_power_two = is_power_two(q_3); + if (q_2 * q_2 == q_1 * q_3 && q_1_is_power_two && q_2_is_power_two && q_3_is_power_two) { + uint32_t left_idx = arithmetic_block.w_l()[i]; + uint32_t right_idx = arithmetic_block.w_r()[i]; + uint32_t out_idx = arithmetic_block.w_o()[i]; + uint32_t fourth_idx = arithmetic_block.w_4()[i]; + bool find_left = find_position(left_idx); + bool find_right = find_position(right_idx); + bool find_out = find_position(out_idx); + bool find_fourth = find_position(fourth_idx); + if (((find_left && find_right && find_out) || (find_left && find_right && !find_out) || + (find_left && find_right && !find_out) || (find_left && !find_right && !find_out)) && + !find_fourth) { + i = this->process_current_decompose_chain(ultra_circuit_builder, variables_in_one_gate, i); + } + } + } + } +} +/** + * @brief this method removes false positive cass variables from aes plookup tables. + * AES_SBOX_MAP, AES_SPARSE_MAP, AES_SPARSE_NORMALIZE tables are used in read_from_1_to_2_table function which + * return values C2[0], so C3[0] isn't used anymore in these cases, but this situation isn't dangerous. + * So, we have to remove these variables. + * @tparam FF + * @param variables_in_one_gate + * @param ultra_circuit_builder + * @param table_id + * @param gate_index + */ +template +inline void Graph_::remove_unnecessary_aes_plookup_variables(std::unordered_set& variables_in_one_gate, + UltraCircuitBuilder& ultra_circuit_builder, + BasicTableId& table_id, + size_t gate_index) +{ + + auto find_position = [&](uint32_t real_variable_index) { + return variables_in_one_gate.contains(real_variable_index); + }; + std::unordered_set aes_plookup_tables{ BasicTableId::AES_SBOX_MAP, + BasicTableId::AES_SPARSE_MAP, + BasicTableId::AES_SPARSE_NORMALIZE }; + auto& lookup_block = ultra_circuit_builder.blocks.lookup; + if (aes_plookup_tables.contains(table_id)) { + uint32_t real_out_idx = this->to_real(ultra_circuit_builder, lookup_block.w_o()[gate_index]); + uint32_t real_right_idx = this->to_real(ultra_circuit_builder, lookup_block.w_r()[gate_index]); + if (variables_gate_counts[real_out_idx] != 1 || variables_gate_counts[real_right_idx] != 1) { + bool find_out = find_position(real_out_idx); + auto q_c = lookup_block.q_c()[gate_index]; + if (q_c == 0) { + if (find_out) { + variables_in_one_gate.erase(real_out_idx); + } + } + } + } +} + +/** + * @brief this method removes false cases in sha256 lookup tables. + * tables which are enumerated in the unordered set sha256_plookup_tables + * are used in read_from_1_to_2_table function which return C2[0], so C3[0] + * isn't used anymore, but this situation isn't dangerous. So, we have to remove these variables. + * @tparam FF + * @param variables_in_one_gate + * @param ultra_circuit_builder + * @param table_id + * @param gate_index + */ + +template +inline void Graph_::remove_unnecessary_sha256_plookup_variables(std::unordered_set& variables_in_one_gate, + UltraCircuitBuilder& ultra_circuit_builder, + BasicTableId& table_id, + size_t gate_index) +{ + + auto find_position = [&](uint32_t real_variable_index) { + return variables_in_one_gate.contains(real_variable_index); + }; + auto& lookup_block = ultra_circuit_builder.blocks.lookup; + std::unordered_set sha256_plookup_tables{ BasicTableId::SHA256_WITNESS_SLICE_3, + BasicTableId::SHA256_WITNESS_SLICE_7_ROTATE_4, + BasicTableId::SHA256_WITNESS_SLICE_8_ROTATE_7, + BasicTableId::SHA256_WITNESS_SLICE_14_ROTATE_1, + BasicTableId::SHA256_BASE16, + BasicTableId::SHA256_BASE16_ROTATE2, + BasicTableId::SHA256_BASE16_ROTATE6, + BasicTableId::SHA256_BASE16_ROTATE7, + BasicTableId::SHA256_BASE16_ROTATE8, + BasicTableId::SHA256_BASE28, + BasicTableId::SHA256_BASE28_ROTATE3, + BasicTableId::SHA256_BASE28_ROTATE6 }; + if (sha256_plookup_tables.contains(table_id)) { + uint32_t real_right_idx = this->to_real(ultra_circuit_builder, lookup_block.w_r()[gate_index]); + uint32_t real_out_idx = this->to_real(ultra_circuit_builder, lookup_block.w_o()[gate_index]); + if (variables_gate_counts[real_out_idx] != 1 || variables_gate_counts[real_right_idx] != 1) { + // auto q_m = lookup_block.q_m()[gate_index]; + auto q_c = lookup_block.q_c()[gate_index]; + bool find_out = find_position(real_out_idx); + // bool find_right = find_position(real_right_idx); + if (q_c == 0) { + if (find_out) { + variables_in_one_gate.erase(real_out_idx); + } + } + if (table_id == SHA256_BASE16_ROTATE2 || table_id == SHA256_BASE28_ROTATE6) { + // we want to remove false cases for special tables even though their selectors != 0 + // because they are used in read_from_1_to_2_table function, and they aren't dangerous + variables_in_one_gate.erase(real_out_idx); + } + } + } +} + +/** + * @brief this method removes false cases in lookup table for a given gate. + * it uses all functions above for lookup tables to remove all variables that appear in one gate, + * if they are not dangerous + * @tparam FF + * @param ultra_circuit_builder + * @param variables_in_one_gate + * @param gate_index + */ + +template +inline void Graph_::process_current_plookup_gate(bb::UltraCircuitBuilder& ultra_circuit_builder, + std::unordered_set& variables_in_one_gate, + size_t gate_index) +{ + auto find_position = [&](uint32_t real_variable_index) { + return variables_in_one_gate.contains(real_variable_index); + }; + auto& lookup_block = ultra_circuit_builder.blocks.lookup; + auto& lookup_tables = ultra_circuit_builder.lookup_tables; + auto table_index = static_cast(lookup_block.q_3()[gate_index]); + for (const auto& table : lookup_tables) { + if (table.table_index == table_index) { + std::set column_1(table.column_1.begin(), table.column_1.end()); + std::set column_2(table.column_2.begin(), table.column_2.end()); + std::set column_3(table.column_3.begin(), table.column_3.end()); + bb::plookup::BasicTableId table_id = table.id; + // false cases for AES + this->remove_unnecessary_aes_plookup_variables( + variables_in_one_gate, ultra_circuit_builder, table_id, gate_index); + // false cases for sha256 + this->remove_unnecessary_sha256_plookup_variables( + variables_in_one_gate, ultra_circuit_builder, table_id, gate_index); + // if the amount of unique elements from columns of plookup tables = 1, it means that + // variable from this column aren't used and we can remove it. + if (column_1.size() == 1) { + uint32_t left_idx = lookup_block.w_l()[gate_index]; + uint32_t real_left_idx = this->to_real(ultra_circuit_builder, left_idx); + bool find_left = find_position(real_left_idx); + if (find_left) { + variables_in_one_gate.erase(real_left_idx); + } + } + if (column_2.size() == 1) { + uint32_t real_right_idx = this->to_real(ultra_circuit_builder, lookup_block.w_r()[gate_index]); + bool find_right = find_position(real_right_idx); + if (find_right) { + variables_in_one_gate.erase(real_right_idx); + } + } + if (column_3.size() == 1) { + uint32_t real_out_idx = this->to_real(ultra_circuit_builder, lookup_block.w_o()[gate_index]); + bool find_out = find_position(real_out_idx); + if (find_out) { + variables_in_one_gate.erase(real_out_idx); + } + } + } + } +} + +/** + * @brief this method removes false cases plookup variables from variables in one gate + * @tparam FF + * @param ultra_circuit_builder + * @param variables_in_one_gate + */ + +template +inline void Graph_::remove_unnecessary_plookup_variables(bb::UltraCircuitBuilder& ultra_circuit_builder, + std::unordered_set& variables_in_one_gate) +{ + auto& lookup_block = ultra_circuit_builder.blocks.lookup; + if (lookup_block.size() > 0) { + for (size_t i = 0; i < lookup_block.size(); i++) { + this->process_current_plookup_gate(ultra_circuit_builder, variables_in_one_gate, i); + } + } +} + +/** + * @brief this method returns a final set of variables that were in one gate + * @tparam FF + * @param ultra_circuit_builder + * @return std::unordered_set + */ + +template +std::unordered_set Graph_::show_variables_in_one_gate(bb::UltraCircuitBuilder& ultra_circuit_builder) +{ + std::unordered_set variables_in_one_gate; + for (const auto& pair : variables_gate_counts) { + bool is_not_constant_variable = this->check_is_not_constant_variable(ultra_circuit_builder, pair.first); + if (pair.second == 1 && pair.first != 0 && is_not_constant_variable) { + variables_in_one_gate.insert(pair.first); + } + } + auto range_lists = ultra_circuit_builder.range_lists; + std::unordered_set decompose_varialbes; + for (auto& pair : range_lists) { + for (auto& elem : pair.second.variable_indices) { + bool is_not_constant_variable = this->check_is_not_constant_variable(ultra_circuit_builder, elem); + if (variables_gate_counts[ultra_circuit_builder.real_variable_index[elem]] == 1 && + is_not_constant_variable) { + decompose_varialbes.insert(ultra_circuit_builder.real_variable_index[elem]); + } + } + } + this->remove_unnecessary_decompose_variables(ultra_circuit_builder, variables_in_one_gate, decompose_varialbes); + this->remove_unnecessary_plookup_variables(ultra_circuit_builder, variables_in_one_gate); + return variables_in_one_gate; +} + +/** + * @brief this method returns connected component with a given index and size of this component + * sometimes for debugging we want to check the size one of the connected component, so it would be + * useful to know its size + * @param connected_components + * @param index + * @return std::pair, size_t> + */ + +std::pair, size_t> get_connected_component_with_index( + const std::vector>& connected_components, size_t index) +{ + auto connected_component = connected_components[index]; + auto size = connected_component.size(); + return std::make_pair(connected_component, size); +} + +/** + * @brief this method prints graph as vertices and their adjacency lists + * example: we have an undirected graph from 3 variables: a, b, c. + * we have edges: a - b, b - c, c - a. + * so, there will be next adjacency lists: + * a: b -> c -> 0\ + * b: a -> c -> 0\ + * c: a -> b -> 0\ + * @tparam FF + */ + +template void Graph_::print_graph() +{ + for (const auto& elem : variable_adjacency_lists) { + info("variable with index", elem.first); + if (variable_adjacency_lists[elem.first].empty()) { + info("is isolated"); + } else { + for (const auto& it : elem.second) { + info(it); + } + } + } +} + +/** + * @brief this method prints all connected components that were found in the graph + * @tparam FF + */ + +template void Graph_::print_connected_components() +{ + auto connected_components = find_connected_components(); + for (size_t i = 0; i < connected_components.size(); i++) { + info("printing the ", i + 1, " connected component:"); + for (const auto& it : connected_components[i]) { + info(it, " "); + } + } +} + +/** + * @brief this method prints a number of gates for each variable. + * while processing the arithmetic circuit, we count for each variable the number of gates it has participated in. + * sometimes for debugging purposes it is useful to see how many gates each variable has participated in. + * @tparam FF + */ + +template void Graph_::print_variables_gate_counts() +{ + for (const auto& it : variables_gate_counts) { + info("number of gates with variables ", it.first, " == ", it.second); + } +} + +/** + * @brief this method prints a number of edges for each variable. + * while processing the arithmetic circuit, we conut for each variable the number of edges, i.e. connections with other + * variables though the gates. perhaps in the future counting the number of edges for each vertex can be useful for + * analysis, and this function will be used for debugging. + * @tparam FF + */ + +template void Graph_::print_variables_edge_counts() +{ + for (const auto& it : variables_degree) { + if (it.first != 0) { + info("variable index = ", it.first, "number of edges for this variable = ", it.second); + } + } +} + +template class Graph_; \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.hpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.hpp new file mode 100644 index 00000000000..c4c88e1e159 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.hpp @@ -0,0 +1,119 @@ +#pragma once +#include "barretenberg/stdlib_circuit_builders/standard_circuit_builder.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" +#include +#include +#include +#include +#include +#include + +/* + * this class describes arithmetic circuit as an undirected graph, where vertices are variables from circuit. + * edges describe connections between variables through gates. We want to find variables that weren't properly + * constrainted/some connections were missed using additional metrics, like in how much gate variable was and number of + * connected components in the graph. if variable was in one connected component, it means that this variable wasn't + * constrained properly. if number of connected components > 1, it means that there were missed some connections between + * variables. + */ +template class Graph_ { + public: + Graph_() = default; + Graph_(const Graph_& other) = delete; + Graph_(Graph_&& other) = delete; + Graph_& operator=(const Graph_& other) = delete; + Graph_&& operator=(Graph_&& other) = delete; + Graph_(const bb::StandardCircuitBuilder_& circuit_constructor); + Graph_(bb::UltraCircuitBuilder& ultra_circuit_constructor); + + uint32_t to_real(bb::UltraCircuitBuilder& ultra_circuit_constructor, const uint32_t& variable_index) + { + return ultra_circuit_constructor.real_variable_index[variable_index]; + }; + void process_gate_variables(bb::UltraCircuitBuilder& ultra_circuit_constructor, + std::vector& gate_variables); + + std::unordered_map get_variables_gate_counts() { return this->variables_gate_counts; }; + + std::vector get_arithmetic_gate_connected_component(bb::UltraCircuitBuilder& ultra_circuit_builder, + size_t index); + std::vector get_elliptic_gate_connected_component(bb::UltraCircuitBuilder& ultra_circuit_builder, + size_t index); + std::vector get_plookup_gate_connected_component(bb::UltraCircuitBuilder& ultra_circuit_builder, + size_t index); + std::vector get_sort_constraint_connected_component(bb::UltraCircuitBuilder& ultra_circuit_builder, + size_t index); + + void add_new_edge(const uint32_t& first_variable_index, const uint32_t& second_variable_index); + std::vector get_variable_adjacency_list(const uint32_t& variable_index) + { + return variable_adjacency_lists[variable_index]; + }; + + void depth_first_search(const uint32_t& variable_index, + std::unordered_set& is_used, + std::vector& connected_component); + std::vector> find_connected_components(); + + std::vector find_variables_with_degree_one(); + std::unordered_set get_variables_in_one_gate(); + + bool find_arithmetic_gate_for_variable(bb::UltraCircuitBuilder& ultra_circuit_builder, + const uint32_t& variable_idx); + bool find_elliptic_gate_for_variable(bb::UltraCircuitBuilder& ultra_circuit_builder, const uint32_t& variable_idx); + bool find_lookup_gate_for_variable(bb::UltraCircuitBuilder& ultra_circuit_builder, const uint32_t& variable_idx); + + size_t get_distance_between_variables(const uint32_t& first_variable_index, const uint32_t& second_variable_index); + bool check_vertex_in_connected_component(const std::vector& connected_component, + const uint32_t& var_index); + + void connect_all_variables_in_vector(bb::UltraCircuitBuilder& ultra_circuit_builder, + const std::vector& variables_vector, + bool is_sorted_variables); + bool check_is_not_constant_variable(bb::UltraCircuitBuilder& ultra_circuit_builder, const uint32_t& variable_index); + + std::pair, size_t> get_connected_component_with_index( + const std::vector>& connected_components, size_t index); + + std::unordered_set get_variables_in_one_gate_without_range_constraints( + bb::UltraCircuitBuilder& ultra_circuit_builder); + + size_t process_current_decompose_chain(bb::UltraCircuitBuilder& ultra_circuit_constructor, + std::unordered_set& variables_in_one_gate, + size_t index); + void process_current_plookup_gate(bb::UltraCircuitBuilder& ultra_circuit_builder, + std::unordered_set& variables_in_one_gate, + size_t gate_index); + void remove_unnecessary_decompose_variables(bb::UltraCircuitBuilder& ultra_circuit_builder, + std::unordered_set& variables_in_on_gate, + const std::unordered_set& decompose_variables); + void remove_unnecessary_plookup_variables(bb::UltraCircuitBuilder& ultra_circuit_builder, + std::unordered_set& variables_in_on_gate); + std::unordered_set show_variables_in_one_gate(bb::UltraCircuitBuilder& ultra_circuit_builder); + + void remove_unnecessary_aes_plookup_variables(std::unordered_set& variables_in_one_gate, + bb::UltraCircuitBuilder& ultra_circuit_builder, + bb::plookup::BasicTableId& table_id, + size_t gate_index); + void remove_unnecessary_sha256_plookup_variables(std::unordered_set& variables_in_one_gate, + bb::UltraCircuitBuilder& ultra_circuit_builder, + bb::plookup::BasicTableId& table_id, + size_t gate_index); + + void print_graph(); + void print_connected_components(); + void print_variables_gate_counts(); + void print_variables_edge_counts(); + ~Graph_() = default; + + private: + std::unordered_map> + variable_adjacency_lists; // we use this data structure to contain information about variables and their + // connections between each other + std::unordered_map + variables_gate_counts; // we use this data structure to count, how many gates use every variable + std::unordered_map + variables_degree; // we use this data structure to count, how many every variable have edges +}; + +using Graph = Graph_; \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description.test.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description.test.cpp new file mode 100644 index 00000000000..26f50cc8db1 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description.test.cpp @@ -0,0 +1,661 @@ +#include "barretenberg/boomerang_value_detection/graph.hpp" +#include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/crypto/aes128/aes128.hpp" +#include "barretenberg/crypto/generators/generator_data.hpp" +#include "barretenberg/crypto/pedersen_commitment/pedersen.hpp" +#include "barretenberg/stdlib/encryption/aes128/aes128.hpp" +#include "barretenberg/stdlib_circuit_builders/mock_circuits.hpp" +#include "barretenberg/stdlib_circuit_builders/plookup_tables/fixed_base/fixed_base.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" + +#include + +using namespace bb; + +/** + * @brief this test checks graph description of the circuit with arithmetic gates + the number of connected components = the number of pair (i, j), 0<=i, j <16, i.e 256 + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_arithmetic_gates) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + for (size_t i = 0; i < 16; ++i) { + for (size_t j = 0; j < 16; ++j) { + uint64_t left = static_cast(j); + uint64_t right = static_cast(i); + uint32_t left_idx = circuit_constructor.add_variable(fr(left)); + uint32_t right_idx = circuit_constructor.add_variable(fr(right)); + uint32_t result_idx = circuit_constructor.add_variable(fr(left ^ right)); + + uint32_t add_idx = + circuit_constructor.add_variable(fr(left) + fr(right) + circuit_constructor.get_variable(result_idx)); + circuit_constructor.create_big_add_gate( + { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); + } + } + + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + auto variables_in_one_gate = graph.show_variables_in_one_gate(circuit_constructor); + bool result = num_connected_components == 256; + EXPECT_EQ(result, true); +} + +/** + * @brief This test checks graph description of Ultra Circuit Builder with arithmetic gates with shifts + * It must be one connected component, cause all gates have shifts + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_arithmetic_gates_with_shifts) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + for (size_t i = 0; i < 16; ++i) { + for (size_t j = 0; j < 16; ++j) { + uint64_t left = static_cast(j); + uint64_t right = static_cast(i); + uint32_t left_idx = circuit_constructor.add_variable(fr(left)); + uint32_t right_idx = circuit_constructor.add_variable(fr(right)); + uint32_t result_idx = circuit_constructor.add_variable(fr(left ^ right)); + + uint32_t add_idx = + circuit_constructor.add_variable(fr(left) + fr(right) + circuit_constructor.get_variable(result_idx)); + circuit_constructor.create_big_add_gate( + { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }, true); + } + } + + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + bool result = num_connected_components == 1; + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks graph description of the circuit with boolean gates. + all variables must be isolated and the number of connected components = 0, all variables in one gate + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_boolean_gates) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + for (size_t i = 0; i < 20; ++i) { + fr a = fr::zero(); + uint32_t a_idx = circuit_constructor.add_variable(a); + circuit_constructor.create_bool_gate(a_idx); + } + + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + auto variables_in_one_gate = graph.show_variables_in_one_gate(circuit_constructor); + bool result = num_connected_components == 0; + EXPECT_EQ(result, true); + EXPECT_EQ(variables_in_one_gate.size(), 20); +} + +/** + * @brief this test checks graph decription for the circuit with one elliptic addition gate. + * The result is one connected component for 6 variables: + * x1, y1, x2, y2, x3, y3 + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_elliptic_add_gate) +{ + typedef grumpkin::g1::affine_element affine_element; + typedef grumpkin::g1::element element; + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + affine_element p1 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 0); + + affine_element p2 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 1); + affine_element p3(element(p1) + element(p2)); + + uint32_t x1 = circuit_constructor.add_variable(p1.x); + uint32_t y1 = circuit_constructor.add_variable(p1.y); + uint32_t x2 = circuit_constructor.add_variable(p2.x); + uint32_t y2 = circuit_constructor.add_variable(p2.y); + uint32_t x3 = circuit_constructor.add_variable(p3.x); + uint32_t y3 = circuit_constructor.add_variable(p3.y); + + circuit_constructor.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, 1 }); + + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + bool result = num_connected_components == 1; + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks graph description of the circuit with one elliptic double gate. + The result is one connected component for 4 variables: + x1, y1, x3, y3 + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_elliptic_double_gate) +{ + typedef grumpkin::g1::affine_element affine_element; + typedef grumpkin::g1::element element; + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + affine_element p1 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 0); + affine_element p3(element(p1).dbl()); + + uint32_t x1 = circuit_constructor.add_variable(p1.x); + uint32_t y1 = circuit_constructor.add_variable(p1.y); + uint32_t x3 = circuit_constructor.add_variable(p3.x); + uint32_t y3 = circuit_constructor.add_variable(p3.y); + + circuit_constructor.create_ecc_dbl_gate({ x1, y1, x3, y3 }); + + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + bool result = num_connected_components == 1; + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks the graph description of the circuit has elliptic addition and multiplication + gates together. The result is 2 connected components: + x1, y1, x2, y2, x3, y3, x4, y4 + x5, y5, x6, y6, x7, y7, x8, y8 + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_elliptic_together) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + typedef grumpkin::g1::affine_element affine_element; + typedef grumpkin::g1::element element; + + affine_element p1 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 0); + affine_element p2 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 1); + affine_element p3(element(p1) + element(p2)); + + uint32_t x1 = circuit_constructor.add_variable(p1.x); + uint32_t y1 = circuit_constructor.add_variable(p1.y); + uint32_t x2 = circuit_constructor.add_variable(p2.x); + uint32_t y2 = circuit_constructor.add_variable(p2.y); + uint32_t x3 = circuit_constructor.add_variable(p3.x); + uint32_t y3 = circuit_constructor.add_variable(p3.y); + + circuit_constructor.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, 1 }); + affine_element p4(element(p3).dbl()); + uint32_t x4 = circuit_constructor.add_variable(p4.x); + uint32_t y4 = circuit_constructor.add_variable(p4.y); + circuit_constructor.create_ecc_dbl_gate({ x3, y3, x4, y4 }); + + affine_element p5 = crypto::pedersen_commitment::commit_native({ bb::fr(2) }, 1); + affine_element p6 = crypto::pedersen_commitment::commit_native({ bb::fr(3) }, 1); + affine_element p7(element(p5) + element(p6)); + + uint32_t x5 = circuit_constructor.add_variable(p5.x); + uint32_t y5 = circuit_constructor.add_variable(p5.y); + uint32_t x6 = circuit_constructor.add_variable(p6.x); + uint32_t y6 = circuit_constructor.add_variable(p6.y); + uint32_t x7 = circuit_constructor.add_variable(p7.x); + uint32_t y7 = circuit_constructor.add_variable(p7.y); + + circuit_constructor.create_ecc_add_gate({ x5, y5, x6, y6, x7, y7, 1 }); + affine_element p8(element(p7).dbl()); + uint32_t x8 = circuit_constructor.add_variable(p8.x); + uint32_t y8 = circuit_constructor.add_variable(p8.y); + circuit_constructor.create_ecc_dbl_gate({ x7, y7, x8, y8 }); + + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + bool result = num_connected_components == 2; + EXPECT_EQ(result, true); +} + +/** + * @brief this test check graph description of the circuit with 2 sort_constraint. The result is 2 connected components: + a_idx, b_idx, c_idx, d_idx + e_idx, f_idx, g_idx, h_idx + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_sort_constraints) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + fr a = fr::one(); + fr b = fr(2); + fr c = fr(3); + fr d = fr(4); + + auto a_idx = circuit_constructor.add_variable(a); + auto b_idx = circuit_constructor.add_variable(b); + auto c_idx = circuit_constructor.add_variable(c); + auto d_idx = circuit_constructor.add_variable(d); + circuit_constructor.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); + + fr e = fr(5); + fr f = fr(6); + fr g = fr(7); + fr h = fr(8); + auto e_idx = circuit_constructor.add_variable(e); + auto f_idx = circuit_constructor.add_variable(f); + auto g_idx = circuit_constructor.add_variable(g); + auto h_idx = circuit_constructor.add_variable(h); + circuit_constructor.create_sort_constraint({ e_idx, f_idx, g_idx, h_idx }); + + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components[0].size(), 4); + EXPECT_EQ(connected_components[1].size(), 4); + EXPECT_EQ(connected_components.size(), 2); +} + +/** + * @brief this test checks graph description of the circuit with 2 sorted_constraints with edges. + The result is 2 connected components: + a_idx, b_idx, ... , h_idx + a1_idx, b1_idx, ..., h1_idx + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_sort_constraints_with_edges) +{ + fr a = fr::one(); + fr b = fr(2); + fr c = fr(3); + fr d = fr(4); + fr e = fr(5); + fr f = fr(6); + fr g = fr(7); + fr h = fr(8); + + UltraCircuitBuilder circuit_constructor; + auto a_idx = circuit_constructor.add_variable(a); + auto b_idx = circuit_constructor.add_variable(b); + auto c_idx = circuit_constructor.add_variable(c); + auto d_idx = circuit_constructor.add_variable(d); + auto e_idx = circuit_constructor.add_variable(e); + auto f_idx = circuit_constructor.add_variable(f); + auto g_idx = circuit_constructor.add_variable(g); + auto h_idx = circuit_constructor.add_variable(h); + circuit_constructor.create_sort_constraint_with_edges( + { a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, h); + + fr a1 = fr(9); + fr b1 = fr(10); + fr c1 = fr(11); + fr d1 = fr(12); + fr e1 = fr(13); + fr f1 = fr(14); + fr g1 = fr(15); + fr h1 = fr(16); + + auto a1_idx = circuit_constructor.add_variable(a1); + auto b1_idx = circuit_constructor.add_variable(b1); + auto c1_idx = circuit_constructor.add_variable(c1); + auto d1_idx = circuit_constructor.add_variable(d1); + auto e1_idx = circuit_constructor.add_variable(e1); + auto f1_idx = circuit_constructor.add_variable(f1); + auto g1_idx = circuit_constructor.add_variable(g1); + auto h1_idx = circuit_constructor.add_variable(h1); + + circuit_constructor.create_sort_constraint_with_edges( + { a1_idx, b1_idx, c1_idx, d1_idx, e1_idx, f1_idx, g1_idx, h1_idx }, a1, h1); + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + bool result = num_connected_components == 2; + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks graph decription for circuit with gates that were created from plookup accumulators + the result is one connected component + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_with_plookup_accumulators) +{ + UltraCircuitBuilder circuit_builder = UltraCircuitBuilder(); + + fr input_value = fr::random_element(); + const fr input_lo = static_cast(input_value).slice(0, plookup::fixed_base::table::BITS_PER_LO_SCALAR); + const auto input_lo_index = circuit_builder.add_variable(input_lo); + + const auto sequence_data_lo = plookup::get_lookup_accumulators(plookup::MultiTableId::FIXED_BASE_LEFT_LO, input_lo); + + const auto lookup_witnesses = circuit_builder.create_gates_from_plookup_accumulators( + plookup::MultiTableId::FIXED_BASE_LEFT_LO, sequence_data_lo, input_lo_index); + + const size_t num_lookups = plookup::fixed_base::table::NUM_TABLES_PER_LO_MULTITABLE; + + EXPECT_EQ(num_lookups, lookup_witnesses[plookup::ColumnIdx::C1].size()); + + Graph graph = Graph(circuit_builder); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + bool result = num_connected_components == 1; + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks variable gates counts for variable from arithmetic gates without shifts + in circuit + */ + +TEST(boomerang_ultra_circuit_constructor, test_variables_gates_counts_for_arithmetic_gate) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + for (size_t i = 0; i < 25; ++i) { + for (size_t j = 0; j < 25; ++j) { + uint64_t left = static_cast(j); + uint64_t right = static_cast(i); + uint32_t left_idx = circuit_constructor.add_variable(fr(left)); + uint32_t right_idx = circuit_constructor.add_variable(fr(right)); + uint32_t result_idx = circuit_constructor.add_variable(fr(left ^ right)); + + uint32_t add_idx = + circuit_constructor.add_variable(fr(left) + fr(right) + circuit_constructor.get_variable(result_idx)); + circuit_constructor.create_big_add_gate( + { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); + } + } + + Graph graph = Graph(circuit_constructor); + auto variables_gate_counts = graph.get_variables_gate_counts(); + bool result = true; + for (const auto pair : variables_gate_counts) { + result = result && (pair.first > 0 ? (pair.second == 1) : (pair.second == 0)); + } + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks variables gates count for variable in circuit with gates with shifts. + * All variables except for zero index, which index == 0 mod 4 and index != 4 have gates count == 2. + * Other variables have gates count = 1. + */ + +TEST(boomerang_ultra_circuit_constructor, test_variables_gates_counts_for_arithmetic_gate_with_shifts) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + for (size_t i = 0; i < 25; ++i) { + for (size_t j = 0; j < 25; ++j) { + uint64_t left = static_cast(j); + uint64_t right = static_cast(i); + uint32_t left_idx = circuit_constructor.add_variable(fr(left)); + uint32_t right_idx = circuit_constructor.add_variable(fr(right)); + uint32_t result_idx = circuit_constructor.add_variable(fr(left ^ right)); + + uint32_t add_idx = + circuit_constructor.add_variable(fr(left) + fr(right) + circuit_constructor.get_variable(result_idx)); + circuit_constructor.create_big_add_gate( + { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }, true); + } + } + + Graph graph = Graph(circuit_constructor); + bool result = true; + auto variables_gate_counts = graph.get_variables_gate_counts(); + for (const auto& pair : variables_gate_counts) { + if (pair.first > 0) { + result = result && (pair.first % 4 == 0 && pair.first != 4 ? (pair.second == 2) : (pair.second == 1)); + } else { + result = result && (pair.second == 0); + } + } + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks variables gates count for variables in circuit with boolean gates + * all variables except for zero index must have gates count = 1. + */ + +TEST(boomerang_ultra_circuit_constructor, test_variables_gates_counts_for_boolean_gates) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + for (size_t i = 0; i < 20; ++i) { + fr a = fr::zero(); + uint32_t a_idx = circuit_constructor.add_variable(a); + circuit_constructor.create_bool_gate(a_idx); + } + + Graph graph = Graph(circuit_constructor); + auto variables_gate_counts = graph.get_variables_gate_counts(); + bool result = true; + for (const auto& part : variables_gate_counts) { + result = result && (part.first == 0 ? (part.second == 0) : (part.second == 1)); + } + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks variables gate counts in circuit with sorted constraints. + * all variables in 2 connected components must have gates count = 1 + */ + +TEST(boomerang_ultra_circuit_constructor, test_variables_gates_counts_for_sorted_constraints) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + fr a = fr::one(); + fr b = fr(2); + fr c = fr(3); + fr d = fr(4); + + auto a_idx = circuit_constructor.add_variable(a); + auto b_idx = circuit_constructor.add_variable(b); + auto c_idx = circuit_constructor.add_variable(c); + auto d_idx = circuit_constructor.add_variable(d); + circuit_constructor.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); + + fr e = fr(5); + fr f = fr(6); + fr g = fr(7); + fr h = fr(8); + auto e_idx = circuit_constructor.add_variable(e); + auto f_idx = circuit_constructor.add_variable(f); + auto g_idx = circuit_constructor.add_variable(g); + auto h_idx = circuit_constructor.add_variable(h); + circuit_constructor.create_sort_constraint({ e_idx, f_idx, g_idx, h_idx }); + + Graph graph = Graph(circuit_constructor); + auto variables_gate_counts = graph.get_variables_gate_counts(); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 2); + bool result = true; + for (size_t i = 0; i < connected_components[0].size(); i++) { + result = result && (variables_gate_counts[connected_components[0][i]] == 1); + } + + for (size_t i = 0; i < connected_components[1].size(); i++) { + result = result && (variables_gate_counts[connected_components[1][i]] == 1); + } + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks variable gates count for variables in circuit with sorted constraints with edges + * all variables in 2 connected components must have gates count = 1 + */ + +TEST(boomerang_ultra_circuit_constructor, test_variables_gates_counts_for_sorted_constraints_with_edges) +{ + fr a = fr::one(); + fr b = fr(2); + fr c = fr(3); + fr d = fr(4); + fr e = fr(5); + fr f = fr(6); + fr g = fr(7); + fr h = fr(8); + + UltraCircuitBuilder circuit_constructor; + auto a_idx = circuit_constructor.add_variable(a); + auto b_idx = circuit_constructor.add_variable(b); + auto c_idx = circuit_constructor.add_variable(c); + auto d_idx = circuit_constructor.add_variable(d); + auto e_idx = circuit_constructor.add_variable(e); + auto f_idx = circuit_constructor.add_variable(f); + auto g_idx = circuit_constructor.add_variable(g); + auto h_idx = circuit_constructor.add_variable(h); + circuit_constructor.create_sort_constraint_with_edges( + { a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, h); + + fr a1 = fr(9); + fr b1 = fr(10); + fr c1 = fr(11); + fr d1 = fr(12); + fr e1 = fr(13); + fr f1 = fr(14); + fr g1 = fr(15); + fr h1 = fr(16); + + auto a1_idx = circuit_constructor.add_variable(a1); + auto b1_idx = circuit_constructor.add_variable(b1); + auto c1_idx = circuit_constructor.add_variable(c1); + auto d1_idx = circuit_constructor.add_variable(d1); + auto e1_idx = circuit_constructor.add_variable(e1); + auto f1_idx = circuit_constructor.add_variable(f1); + auto g1_idx = circuit_constructor.add_variable(g1); + auto h1_idx = circuit_constructor.add_variable(h1); + + circuit_constructor.create_sort_constraint_with_edges( + { a1_idx, b1_idx, c1_idx, d1_idx, e1_idx, f1_idx, g1_idx, h1_idx }, a1, h1); + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + auto variables_gate_counts = graph.get_variables_gate_counts(); + bool result = true; + for (size_t i = 0; i < connected_components[0].size(); i++) { + result = result && (variables_gate_counts[connected_components[0][i]] == 1); + } + + for (size_t i = 0; i < connected_components[1].size(); i++) { + result = result && (variables_gate_counts[connected_components[1][i]] == 1); + } + EXPECT_EQ(connected_components.size(), 2); + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks variables gates count for variables in circuit with 1 elliptic addition gates + * all variables in connected components must have gates count = 1 + */ + +TEST(boomerang_ultra_circuit_constructor, test_variables_gates_counts_for_ecc_add_gates) +{ + typedef grumpkin::g1::affine_element affine_element; + typedef grumpkin::g1::element element; + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + affine_element p1 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 0); + + affine_element p2 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 1); + affine_element p3(element(p1) + element(p2)); + + uint32_t x1 = circuit_constructor.add_variable(p1.x); + uint32_t y1 = circuit_constructor.add_variable(p1.y); + uint32_t x2 = circuit_constructor.add_variable(p2.x); + uint32_t y2 = circuit_constructor.add_variable(p2.y); + uint32_t x3 = circuit_constructor.add_variable(p3.x); + uint32_t y3 = circuit_constructor.add_variable(p3.y); + + circuit_constructor.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, 1 }); + + Graph graph = Graph(circuit_constructor); + auto variables_gate_counts = graph.get_variables_gate_counts(); + auto connected_components = graph.find_connected_components(); + bool result = (variables_gate_counts[connected_components[0][0]] == 1) && + (variables_gate_counts[connected_components[0][1]] == 1) && + (variables_gate_counts[connected_components[0][2]] == 1) && + (variables_gate_counts[connected_components[0][3]] == 1) && + (variables_gate_counts[connected_components[0][4]] == 1) && + (variables_gate_counts[connected_components[0][5]] == 1); + EXPECT_EQ(connected_components.size(), 1); + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks variables gates count for variables in circuit with 1 elliptic double gates + * all variables in connected components must have gates count = 1. + */ + +TEST(boomerang_ultra_circuit_constructor, test_variables_gates_counts_for_ecc_dbl_gate) +{ + typedef grumpkin::g1::affine_element affine_element; + typedef grumpkin::g1::element element; + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + affine_element p1 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 0); + affine_element p3(element(p1).dbl()); + + uint32_t x1 = circuit_constructor.add_variable(p1.x); + uint32_t y1 = circuit_constructor.add_variable(p1.y); + uint32_t x3 = circuit_constructor.add_variable(p3.x); + uint32_t y3 = circuit_constructor.add_variable(p3.y); + + circuit_constructor.create_ecc_dbl_gate({ x1, y1, x3, y3 }); + + Graph graph = Graph(circuit_constructor); + auto variables_gate_counts = graph.get_variables_gate_counts(); + auto connected_components = graph.find_connected_components(); + + bool result = (variables_gate_counts[connected_components[0][0]] == 1) && + (variables_gate_counts[connected_components[0][1]] == 1) && + (variables_gate_counts[connected_components[0][2]] == 1) && + (variables_gate_counts[connected_components[0][3]] == 1); + + EXPECT_EQ(connected_components.size(), 1); + EXPECT_EQ(result, true); +} + +std::vector add_variables(UltraCircuitBuilder& circuit_constructor, std::vector variables) +{ + std::vector res; + for (size_t i = 0; i < variables.size(); i++) { + res.emplace_back(circuit_constructor.add_variable(variables[i])); + } + return res; +} + +/** + * @brief this test checks graph description of circuit with range constraints. + * all variables must be in one connected component. + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_range_constraints) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + auto indices = add_variables(circuit_constructor, { 1, 2, 3, 4 }); + for (size_t i = 0; i < indices.size(); i++) { + circuit_constructor.create_new_range_constraint(indices[i], 5); + } + circuit_constructor.create_sort_constraint(indices); + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); +} + +/** + * @brief this checks graph description of circuit with decompose function. + * all variables must be in one connected component + */ + +TEST(boomerang_ultra_circuit_constructor, composed_range_constraint) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + auto c = fr::random_element(); + auto d = uint256_t(c).slice(0, 133); + auto e = fr(d); + auto a_idx = circuit_constructor.add_variable(fr(e)); + circuit_constructor.create_add_gate( + { a_idx, circuit_constructor.zero_idx, circuit_constructor.zero_idx, 1, 0, 0, -fr(e) }); + circuit_constructor.decompose_into_default_range(a_idx, 134); + + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_aes128.test.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_aes128.test.cpp new file mode 100644 index 00000000000..9db54f96429 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_aes128.test.cpp @@ -0,0 +1,123 @@ +#include "barretenberg/boomerang_value_detection/graph.hpp" +#include "barretenberg/common/test.hpp" +#include "barretenberg/crypto/aes128/aes128.hpp" +#include "barretenberg/crypto/generators/generator_data.hpp" +#include "barretenberg/crypto/pedersen_commitment/pedersen.hpp" +#include "barretenberg/stdlib/encryption/aes128/aes128.hpp" +#include "barretenberg/stdlib_circuit_builders/mock_circuits.hpp" +#include "barretenberg/stdlib_circuit_builders/plookup_tables/fixed_base/fixed_base.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" + +#include + +using namespace bb; +using namespace bb::stdlib; + +using Builder = UltraCircuitBuilder; +typedef stdlib::field_t field_pt; +typedef stdlib::witness_t witness_pt; + +bool check_in_vector(const std::vector& input_vector, const uint32_t& real_var_index) +{ + for (const auto& elem : input_vector) { + if (elem.witness_index == real_var_index) { + return true; + } + } + return false; +} + +/** + * @brief this test checks graph description of circuit for AES128CBC + * graph must be consist from one connected component + */ + +TEST(boomerang_stdlib_aes, test_graph_for_aes_64_bytes) +{ + uint8_t key[16]{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; + uint8_t iv[16]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; + uint8_t in[64]{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 }; + + const auto convert_bytes = [](uint8_t* data) { + uint256_t converted(0); + for (uint64_t i = 0; i < 16; ++i) { + uint256_t to_add = uint256_t((uint64_t)(data[i])) << uint256_t((15 - i) * 8); + converted += to_add; + } + return converted; + }; + + auto builder = Builder(); + + std::vector in_field{ + witness_pt(&builder, fr(convert_bytes(in))), + witness_pt(&builder, fr(convert_bytes(in + 16))), + witness_pt(&builder, fr(convert_bytes(in + 32))), + witness_pt(&builder, fr(convert_bytes(in + 48))), + }; + + field_pt key_field(witness_pt(&builder, fr(convert_bytes(key)))); + field_pt iv_field(witness_pt(&builder, fr(convert_bytes(iv)))); + + const auto result = stdlib::aes128::encrypt_buffer_cbc(in_field, iv_field, key_field); + + Graph graph = Graph(builder); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + bool graph_result = num_connected_components == 1; + + EXPECT_EQ(graph_result, true); +} + +/** + * @brief this test checks variables gate counts for variables in circuit for AES128CBC + * Some variables can be from input/output vectors, or they are key and iv, and they have variable + * gates count = 1, because it's the circuit for test. So, we can ignore these variables + */ + +TEST(boomerang_stdlib_aes, test_variable_gates_count_for_aes128cbc) +{ + + uint8_t key[16]{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; + uint8_t iv[16]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; + uint8_t in[64]{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 }; + + const auto convert_bytes = [](uint8_t* data) { + uint256_t converted(0); + for (uint64_t i = 0; i < 16; ++i) { + uint256_t to_add = uint256_t((uint64_t)(data[i])) << uint256_t((15 - i) * 8); + converted += to_add; + } + return converted; + }; + + auto builder = Builder(); + + std::vector in_field{ + witness_pt(&builder, fr(convert_bytes(in))), + witness_pt(&builder, fr(convert_bytes(in + 16))), + witness_pt(&builder, fr(convert_bytes(in + 32))), + witness_pt(&builder, fr(convert_bytes(in + 48))), + }; + + field_pt key_field(witness_pt(&builder, fr(convert_bytes(key)))); + field_pt iv_field(witness_pt(&builder, fr(convert_bytes(iv)))); + + const auto result = stdlib::aes128::encrypt_buffer_cbc(in_field, iv_field, key_field); + + Graph graph = Graph(builder); + std::unordered_set variables_in_one_gate = graph.show_variables_in_one_gate(builder); + for (const auto& elem : variables_in_one_gate) { + bool result1 = check_in_vector(in_field, elem); + bool result2 = check_in_vector(result, elem); + bool check = + (result1 == 1) || (result2 == 1) || (elem == key_field.witness_index) || (elem == iv_field.witness_index); + EXPECT_EQ(check, true); + } +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_blake2s.test.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_blake2s.test.cpp new file mode 100644 index 00000000000..934e92b568a --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_blake2s.test.cpp @@ -0,0 +1,57 @@ +#include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/crypto/blake2s/blake2s.hpp" +#include "barretenberg/stdlib/hash/blake2s/blake2s.hpp" +#include "barretenberg/stdlib/hash/blake2s/blake2s_plookup.hpp" +#include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp" +#include "barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" +#include "graph.hpp" +#include + +using namespace bb; +using namespace bb::stdlib; + +using Builder = UltraCircuitBuilder; + +using field_ct = field_t; +using witness_ct = witness_t; +using byte_array_ct = byte_array; +using byte_array_plookup = byte_array; +using public_witness_t = public_witness_t; + +/** + * @brief this tests check graph description of circuit for blake2s for one and two blocks. + * all graphs must have one connected component. + */ + +TEST(boomerang_stdlib_blake2s, test_graph_for_blake2s_single_block_plookup) +{ + Builder builder; + std::string input = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01"; + std::vector input_v(input.begin(), input.end()); + + byte_array_plookup input_arr(&builder, input_v); + byte_array_plookup output = blake2s(input_arr); + + Graph graph = Graph(builder); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); +} + +TEST(boomerang_stdlib_blake2s, test_graph_for_blake2s_double_block_plookup) +{ + Builder builder; + std::string input = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + std::vector input_v(input.begin(), input.end()); + + byte_array_plookup input_arr(&builder, input_v); + byte_array_plookup output = blake2s(input_arr); + + auto expected = crypto::blake2s(input_v); + + EXPECT_EQ(output.get_value(), std::vector(expected.begin(), expected.end())); + + Graph graph = Graph(builder); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); +} diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_blake3s.test.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_blake3s.test.cpp new file mode 100644 index 00000000000..d826f1e080b --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_blake3s.test.cpp @@ -0,0 +1,58 @@ +#include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/common/streams.hpp" +#include "barretenberg/crypto/blake3s/blake3s.hpp" +#include "barretenberg/stdlib/hash/blake3s/blake3s.hpp" +#include "barretenberg/stdlib/hash/blake3s/blake3s_plookup.hpp" +#include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp" +#include "barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.hpp" +#include "graph.hpp" +#include + +using namespace bb; + +using byte_array = stdlib::byte_array; +using public_witness_t = stdlib::public_witness_t; +using byte_array_plookup = stdlib::byte_array; +using public_witness_t_plookup = stdlib::public_witness_t; +using UltraBuilder = UltraCircuitBuilder; + +/** + * @brief this tests check that graph description of circuit for blake3s for different blocks. + * All graphs must have one connected component + */ + +TEST(boomerang_stdlib_blake3s, test_single_block_plookup) +{ + auto builder = UltraBuilder(); + std::string input = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01"; + std::vector input_v(input.begin(), input.end()); + + byte_array_plookup input_arr(&builder, input_v); + byte_array_plookup output = stdlib::blake3s(input_arr); + + std::vector expected = blake3::blake3s(input_v); + + EXPECT_EQ(output.get_value(), expected); + + Graph graph = Graph(builder); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); +} + +TEST(boomerang_stdlib_blake3s, test_double_block_plookup) +{ + auto builder = UltraBuilder(); + std::string input = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + std::vector input_v(input.begin(), input.end()); + + byte_array_plookup input_arr(&builder, input_v); + byte_array_plookup output = stdlib::blake3s(input_arr); + + std::vector expected = blake3::blake3s(input_v); + + EXPECT_EQ(output.get_value(), expected); + + Graph graph = Graph(builder); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_sha256.test.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_sha256.test.cpp new file mode 100644 index 00000000000..0370398d81d --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_sha256.test.cpp @@ -0,0 +1,71 @@ +#include "barretenberg/boomerang_value_detection/graph.hpp" +#include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/common/test.hpp" +#include "barretenberg/crypto/sha256/sha256.hpp" +#include "barretenberg/stdlib/hash/sha256/sha256.hpp" +#include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp" +#include "barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.hpp" +#include "barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" + +#include "barretenberg/numeric/bitop/rotate.hpp" +#include "barretenberg/numeric/bitop/sparse_form.hpp" +#include "barretenberg/numeric/random/engine.hpp" + +using namespace bb; +using namespace bb::stdlib; + +using Builder = UltraCircuitBuilder; + +using byte_array_ct = byte_array; +using packed_byte_array_ct = packed_byte_array; +using field_ct = field_t; + +/** + all these tests check graph description for sha256 circuits. All circuits have to consist from 1 connected component + */ + +TEST(boomerang_stdlib_sha256, test_graph_for_sha256_55_bytes) +{ + // 55 bytes is the largest number of bytes that can be hashed in a single block, + // accounting for the single padding bit, and the 64 size bits required by the SHA-256 standard. + auto builder = Builder(); + packed_byte_array_ct input(&builder, "An 8 character password? Snow White and the 7 Dwarves.."); + + packed_byte_array_ct output_bits = stdlib::sha256(input); + + std::vector output = output_bits.to_unverified_byte_slices(4); + + Graph graph = Graph(builder); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); +} + +HEAVY_TEST(boomerang_stdlib_sha256, test_graph_for_sha256_NIST_vector_five) +{ + typedef stdlib::field_t field_pt; + typedef stdlib::packed_byte_array packed_byte_array_pt; + + auto builder = UltraCircuitBuilder(); + + packed_byte_array_pt input( + &builder, + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAA"); + + packed_byte_array_pt output_bits = stdlib::sha256(input); + + std::vector output = output_bits.to_unverified_byte_slices(4); + + Graph graph = Graph(builder); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/variable_gates_count.test.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/variable_gates_count.test.cpp new file mode 100644 index 00000000000..d07ebeeac4f --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/variable_gates_count.test.cpp @@ -0,0 +1,130 @@ +#include "barretenberg/boomerang_value_detection/graph.hpp" +#include "barretenberg/crypto/aes128/aes128.hpp" +#include "barretenberg/crypto/generators/generator_data.hpp" +#include "barretenberg/crypto/pedersen_commitment/pedersen.hpp" +#include "barretenberg/stdlib/encryption/aes128/aes128.hpp" +#include "barretenberg/stdlib_circuit_builders/mock_circuits.hpp" +#include "barretenberg/stdlib_circuit_builders/plookup_tables/fixed_base/fixed_base.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" + +#include + +using namespace bb; + +TEST(boomerang_ultra_circuit_constructor, test_variable_gates_count_for_decompose) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + auto c = fr::random_element(); + auto d = uint256_t(c).slice(0, 133); + auto e = fr(d); + auto a_idx = circuit_constructor.add_variable(fr(e)); + circuit_constructor.create_add_gate( + { a_idx, circuit_constructor.zero_idx, circuit_constructor.zero_idx, 1, 0, 0, -fr(e) }); + circuit_constructor.decompose_into_default_range(a_idx, 134); + + Graph graph = Graph(circuit_constructor); + std::unordered_set variables_in_on_gate = graph.show_variables_in_one_gate(circuit_constructor); + EXPECT_EQ(variables_in_on_gate.size(), 1); +} + +TEST(boomerang_ultra_circuit_constructor, test_variable_gates_count_for_decompose2) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + auto c = fr::random_element(); + auto d = uint256_t(c).slice(0, 41); + auto e = fr(d); + auto a_idx = circuit_constructor.add_variable(fr(e)); + circuit_constructor.create_add_gate( + { a_idx, circuit_constructor.zero_idx, circuit_constructor.zero_idx, 1, 0, 0, -fr(e) }); + circuit_constructor.decompose_into_default_range(a_idx, 42); + + Graph graph = Graph(circuit_constructor); + auto variables_in_on_gate = graph.show_variables_in_one_gate(circuit_constructor); + EXPECT_EQ(variables_in_on_gate.size(), 1); +} + +TEST(boomerang_utils, test_selectors_for_decompose) +{ + auto is_power_two = [&](const uint256_t& number) { return number > 0 && ((number & (number - 1)) == 0); }; + const uint64_t target_range_bitnum = 14; + size_t i = 0; + const uint64_t shifts[3]{ + target_range_bitnum * (3 * i), + target_range_bitnum * (3 * i + 1), + target_range_bitnum * (3 * i + 2), + }; + uint256_t q_1 = uint256_t(1) << shifts[0]; + uint256_t q_2 = uint256_t(1) << shifts[1]; + uint256_t q_3 = uint256_t(1) << shifts[2]; + bool q_1_is_power_two = is_power_two(q_1); + bool q_2_is_power_two = is_power_two(q_2); + bool q_3_is_power_two = is_power_two(q_3); + EXPECT_EQ(q_2 * q_2, q_1 * q_3); + EXPECT_EQ(q_1_is_power_two, true); + EXPECT_EQ(q_2_is_power_two, true); + EXPECT_EQ(q_3_is_power_two, true); +} + +TEST(boomerang_ultra_circuit_constructor, test_variable_gates_count_for_two_decomposes) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + auto c1 = fr::random_element(); + auto c2 = fr::random_element(); + auto d1 = uint256_t(c1).slice(0, 41); + auto d2 = uint256_t(c2).slice(0, 41); + auto e1 = fr(d1); + auto e2 = fr(d2); + auto a1_idx = circuit_constructor.add_variable(fr(e1)); + auto a2_idx = circuit_constructor.add_variable(fr(e2)); + circuit_constructor.create_add_gate( + { a1_idx, circuit_constructor.zero_idx, circuit_constructor.zero_idx, 1, 0, 0, -fr(e1) }); + circuit_constructor.create_add_gate( + { a2_idx, circuit_constructor.zero_idx, circuit_constructor.zero_idx, 1, 0, 0, -fr(e2) }); + circuit_constructor.decompose_into_default_range(a1_idx, 42); + circuit_constructor.decompose_into_default_range(a2_idx, 42); + + Graph graph = Graph(circuit_constructor); + std::unordered_set variables_in_one_gate = graph.show_variables_in_one_gate(circuit_constructor); + EXPECT_EQ(variables_in_one_gate.size(), 2); +} + +TEST(boomerang_ultra_circuit_constructor, test_decompose_with_boolean_gates) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + auto c1 = fr::random_element(); + auto c2 = fr::random_element(); + auto d1 = uint256_t(c1).slice(0, 41); + auto d2 = uint256_t(c2).slice(0, 41); + auto e1 = fr(d1); + auto e2 = fr(d2); + auto a1_idx = circuit_constructor.add_variable(fr(e1)); + auto a2_idx = circuit_constructor.add_variable(fr(e2)); + circuit_constructor.decompose_into_default_range(a1_idx, 42); + circuit_constructor.decompose_into_default_range(a2_idx, 42); + + for (size_t i = 0; i < 20; ++i) { + fr a = fr::zero(); + uint32_t a_idx = circuit_constructor.add_variable(a); + circuit_constructor.create_bool_gate(a_idx); + } + + Graph graph = Graph(circuit_constructor); + std::unordered_set variables_in_one_gate = graph.show_variables_in_one_gate(circuit_constructor); + EXPECT_EQ(variables_in_one_gate.size(), 22); +} + +TEST(boomerang_ultra_circuit_constructor, test_decompose_for_6_bit_number) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + auto c = fr::random_element(); + auto d = uint256_t(c).slice(0, 5); + auto e = fr(d); + auto a_idx = circuit_constructor.add_variable(fr(d)); + circuit_constructor.create_add_gate( + { a_idx, circuit_constructor.zero_idx, circuit_constructor.zero_idx, 1, 0, 0, -fr(e) }); + circuit_constructor.decompose_into_default_range(a_idx, 6); + + Graph graph = Graph(circuit_constructor); + std::unordered_set variables_in_on_gate = graph.show_variables_in_one_gate(circuit_constructor); + EXPECT_EQ(variables_in_on_gate.size(), 1); +} diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/variables_gate_counts.sha256.test.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/variables_gate_counts.sha256.test.cpp new file mode 100644 index 00000000000..c85b25a5217 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/variables_gate_counts.sha256.test.cpp @@ -0,0 +1,180 @@ +#include "barretenberg/boomerang_value_detection/graph.hpp" +#include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/common/test.hpp" +#include "barretenberg/crypto/sha256/sha256.hpp" +#include "barretenberg/stdlib/hash/sha256/sha256.hpp" +#include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp" +#include "barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.hpp" +#include "barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" + +#include "barretenberg/numeric/bitop/rotate.hpp" +#include "barretenberg/numeric/bitop/sparse_form.hpp" +#include "barretenberg/numeric/random/engine.hpp" + +#include + +#include + +using namespace bb; +using namespace bb::stdlib; + +using Builder = UltraCircuitBuilder; + +using byte_array_ct = byte_array; +using packed_byte_array_ct = packed_byte_array; +using witness_ct = stdlib::witness_t; +using field_ct = field_t; + +bool check_in_byte_array(const uint32_t& real_var_index, const packed_byte_array_ct& byte_array) +{ + std::vector> limbs = byte_array.get_limbs(); + for (const auto& elem : limbs) { + if (elem.witness_index == real_var_index) { + return true; + } + } + return false; +} + +bool check_in_range_lists(const uint32_t& real_var_index, const uint64_t& target_range, const Builder& builder) +{ + auto range_lists = builder.range_lists; + auto target_list = range_lists[target_range]; + for (const auto elem : target_list.variable_indices) { + if (elem == real_var_index) { + return true; + } + } + return false; +} + +/** + * @brief all these tests check circuits for sha256 NIST VECTORS to find variables that won't properly constrained, + * i.e. have variable gates count = 1. Some variables can be from input/output vectors or from range_constraints, + * and they are not dangerous. + */ + +TEST(boomerang_stdlib_sha256, test_variables_gate_counts_for_sha256_55_bytes) +{ + // 55 bytes is the largest number of bytes that can be hashed in a single block, + // accounting for the single padding bit, and the 64 size bits required by the SHA-256 standard. + auto builder = Builder(); + packed_byte_array_ct input(&builder, "An 8 character password? Snow White and the 7 Dwarves.."); + + packed_byte_array_ct output_bits = stdlib::sha256(input); + + // std::vector output = output_bits.to_unverified_byte_slices(4); + + Graph graph = Graph(builder); + std::unordered_set variables_in_on_gate = graph.show_variables_in_one_gate(builder); + std::vector vector_variables_in_on_gate(variables_in_on_gate.begin(), variables_in_on_gate.end()); + std::sort(vector_variables_in_on_gate.begin(), vector_variables_in_on_gate.end()); + for (const auto& elem : vector_variables_in_on_gate) { + bool result1 = check_in_byte_array(elem, input); + bool result2 = check_in_byte_array(elem, output_bits); + bool result3 = check_in_range_lists(elem, 3, builder); + bool check = (result1 == 1) || (result2 == 1) || (result3 == 1); + EXPECT_EQ(check, true); + } +} + +TEST(boomerang_stdlib_sha256, test_variable_gates_count_for_sha256_NIST_vector_one) +{ + auto builder = Builder(); + packed_byte_array_ct input(&builder, "abc"); + packed_byte_array_ct output_bits = stdlib::sha256(input); + + Graph graph = Graph(builder); + std::unordered_set variables_in_one_gate = graph.show_variables_in_one_gate(builder); + for (const auto& elem : variables_in_one_gate) { + bool result1 = check_in_byte_array(elem, input); + bool result2 = check_in_byte_array(elem, output_bits); + bool result3 = check_in_range_lists(elem, 3, builder); + bool check = (result1 == 1) || (result2 == 1) || (result3 == 1); + EXPECT_EQ(check, true); + } +} + +TEST(boomerang_stdlib_sha256, test_variable_gates_count_for_sha256_NIST_vector_two) +{ + auto builder = Builder(); + + packed_byte_array_ct input(&builder, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); + + packed_byte_array_ct output_bits = stdlib::sha256(input); + Graph graph = Graph(builder); + std::unordered_set variables_in_one_gate = graph.show_variables_in_one_gate(builder); + for (const auto& elem : variables_in_one_gate) { + bool result1 = check_in_byte_array(elem, input); + bool result2 = check_in_byte_array(elem, output_bits); + bool result3 = check_in_range_lists(elem, 3, builder); + bool check = (result1 == 1) || (result2 == 1) || (result3 == 1); + EXPECT_EQ(check, true); + } +} + +TEST(boomerang_stdlib_sha256, test_variable_gates_count_sha256_NIST_vector_three) +{ + auto builder = Builder(); + + // one byte, 0xbd + packed_byte_array_ct input(&builder, std::vector{ 0xbd }); + packed_byte_array_ct output_bits = stdlib::sha256(input); + Graph graph = Graph(builder); + std::unordered_set variables_in_one_gate = graph.show_variables_in_one_gate(builder); + for (const auto& elem : variables_in_one_gate) { + bool result1 = check_in_byte_array(elem, input); + bool result2 = check_in_byte_array(elem, output_bits); + bool result3 = check_in_range_lists(elem, 3, builder); + bool check = (result1 == 1) || (result2 == 1) || (result3 == 1); + EXPECT_EQ(check, true); + } +} + +TEST(boomerang_stdlib_sha256, test_variable_gates_count_sha256_NIST_vector_four) +{ + auto builder = Builder(); + + // 4 bytes, 0xc98c8e55 + packed_byte_array_ct input(&builder, std::vector{ 0xc9, 0x8c, 0x8e, 0x55 }); + packed_byte_array_ct output_bits = stdlib::sha256(input); + Graph graph = Graph(builder); + std::unordered_set variables_in_one_gate = graph.show_variables_in_one_gate(builder); + for (const auto& elem : variables_in_one_gate) { + bool result1 = check_in_byte_array(elem, input); + bool result2 = check_in_byte_array(elem, output_bits); + bool result3 = check_in_range_lists(elem, 3, builder); + bool check = (result1 == 1) || (result2 == 1) || (result3 == 1); + EXPECT_EQ(check, true); + } +} + +HEAVY_TEST(boomerang_stdlib_sha256, test_variable_gates_count_for_sha256_NIST_vector_five) +{ + auto builder = Builder(); + + packed_byte_array_ct input( + &builder, + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAA"); + + packed_byte_array_ct output_bits = stdlib::sha256(input); + Graph graph = Graph(builder); + std::unordered_set variables_in_on_gate = graph.show_variables_in_one_gate(builder); + for (const auto& elem : variables_in_on_gate) { + bool result1 = check_in_byte_array(elem, input); + bool result2 = check_in_byte_array(elem, output_bits); + bool result3 = check_in_range_lists(elem, 3, builder); + bool check = (result1 == 1) || (result2 == 1) || (result3 == 1); + EXPECT_EQ(check, true); + } +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_builder.test.cpp b/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_builder.test.cpp index e7a3b8eaf52..ae2b514f434 100644 --- a/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_builder.test.cpp +++ b/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_builder.test.cpp @@ -611,22 +611,20 @@ TEST(UltraCircuitConstructor, NonNativeFieldMultiplication) const auto split_into_limbs = [&](const uint512_t& input) { constexpr size_t NUM_BITS = 68; - std::array limbs; + std::array limbs; limbs[0] = input.slice(0, NUM_BITS).lo; limbs[1] = input.slice(NUM_BITS * 1, NUM_BITS * 2).lo; limbs[2] = input.slice(NUM_BITS * 2, NUM_BITS * 3).lo; limbs[3] = input.slice(NUM_BITS * 3, NUM_BITS * 4).lo; - limbs[4] = fr(input.lo); return limbs; }; - const auto get_limb_witness_indices = [&](const std::array& limbs) { - std::array limb_indices; + const auto get_limb_witness_indices = [&](const std::array& limbs) { + std::array limb_indices; limb_indices[0] = circuit_constructor.add_variable(limbs[0]); limb_indices[1] = circuit_constructor.add_variable(limbs[1]); limb_indices[2] = circuit_constructor.add_variable(limbs[2]); limb_indices[3] = circuit_constructor.add_variable(limbs[3]); - limb_indices[4] = circuit_constructor.add_variable(limbs[4]); return limb_indices; }; const uint512_t BINARY_BASIS_MODULUS = uint512_t(1) << (68 * 4); @@ -671,22 +669,20 @@ TEST(UltraCircuitConstructor, NonNativeFieldMultiplicationSortCheck) const auto split_into_limbs = [&](const uint512_t& input) { constexpr size_t NUM_BITS = 68; - std::array limbs; + std::array limbs; limbs[0] = input.slice(0, NUM_BITS).lo; limbs[1] = input.slice(NUM_BITS * 1, NUM_BITS * 2).lo; limbs[2] = input.slice(NUM_BITS * 2, NUM_BITS * 3).lo; limbs[3] = input.slice(NUM_BITS * 3, NUM_BITS * 4).lo; - limbs[4] = fr(input.lo); return limbs; }; - const auto get_limb_witness_indices = [&](const std::array& limbs) { - std::array limb_indices; + const auto get_limb_witness_indices = [&](const std::array& limbs) { + std::array limb_indices; limb_indices[0] = circuit_constructor.add_variable(limbs[0]); limb_indices[1] = circuit_constructor.add_variable(limbs[1]); limb_indices[2] = circuit_constructor.add_variable(limbs[2]); limb_indices[3] = circuit_constructor.add_variable(limbs[3]); - limb_indices[4] = circuit_constructor.add_variable(limbs[4]); return limb_indices; }; const uint512_t BINARY_BASIS_MODULUS = uint512_t(1) << (68 * 4); diff --git a/barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt index 2ddd66ceb49..1f44c3c2746 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt @@ -13,7 +13,7 @@ set(DSL_DEPENDENCIES stdlib_schnorr stdlib_honk_verifier) -if (NOT WASM) +if (NOT WASM AND NOT DISABLE_AZTEC_VM) list(APPEND DSL_DEPENDENCIES libdeflate::libdeflate_static vm) endif() diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 7b08678ad71..4d5a83a308f 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -399,15 +399,14 @@ AggregationObjectIndices process_avm_recursion_constraints(Builder& builder, */ template <> UltraCircuitBuilder create_circuit(AcirFormat& constraint_system, + bool recursive, const size_t size_hint, const WitnessVector& witness, bool honk_recursion, [[maybe_unused]] std::shared_ptr, bool collect_gates_per_opcode) { - Builder builder{ - size_hint, witness, constraint_system.public_inputs, constraint_system.varnum, constraint_system.recursive - }; + Builder builder{ size_hint, witness, constraint_system.public_inputs, constraint_system.varnum, recursive }; bool has_valid_witness_assignments = !witness.empty(); build_constraints( @@ -429,6 +428,7 @@ UltraCircuitBuilder create_circuit(AcirFormat& constraint_system, */ template <> MegaCircuitBuilder create_circuit(AcirFormat& constraint_system, + [[maybe_unused]] bool recursive, [[maybe_unused]] const size_t size_hint, const WitnessVector& witness, bool honk_recursion, @@ -470,6 +470,7 @@ MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system, // Construct the main kernel circuit logic excluding recursive verifiers auto circuit = create_circuit(constraint_system, + /*recursive=*/false, size_hint, witness, /*honk_recursion=*/false, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp index 79a750b2e89..dba936225f6 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -75,7 +75,6 @@ struct AcirFormat { // of another SNARK. For example, a recursive friendly proof may use Blake3Pedersen for // hashing in its transcript, while we still want a prove that uses Keccak for its transcript in order // to be able to verify SNARKs on Ethereum. - bool recursive; uint32_t num_acir_opcodes; @@ -200,6 +199,12 @@ struct AcirProgramStack { template Builder create_circuit(AcirFormat& constraint_system, + // Specifies whether a prover that produces SNARK recursion friendly proofs should be used. + // The proof produced when this flag is true should be friendly for recursive verification inside + // of another SNARK. For example, a recursive friendly proof may use Blake3Pedersen for + // hashing in its transcript, while we still want a prove that uses Keccak for its transcript in + // order to be able to verify SNARKs on Ethereum. + bool recursive, const size_t size_hint = 0, const WitnessVector& witness = {}, bool honk_recursion = false, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index 199bfac3ca6..d3fb922732e 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp @@ -39,7 +39,6 @@ TEST_F(AcirFormatTests, TestASingleConstraintNoPubInputs) AcirFormat constraint_system{ .varnum = 4, - .recursive = false, .num_acir_opcodes = 1, .public_inputs = {}, .logic_constraints = {}, @@ -71,7 +70,7 @@ TEST_F(AcirFormatTests, TestASingleConstraintNoPubInputs) }; mock_opcode_indices(constraint_system); WitnessVector witness{ 0, 0, 1 }; - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness); auto composer = Composer(); auto prover = composer.create_ultra_with_keccak_prover(builder); @@ -160,7 +159,6 @@ TEST_F(AcirFormatTests, TestLogicGateFromNoirCircuit) AcirFormat constraint_system{ .varnum = 6, - .recursive = false, .num_acir_opcodes = 7, .public_inputs = { 1 }, .logic_constraints = { logic_constraint }, @@ -196,7 +194,7 @@ TEST_F(AcirFormatTests, TestLogicGateFromNoirCircuit) WitnessVector witness{ 5, 10, 15, 5, inverse_of_five, 1, }; - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness); auto composer = Composer(); auto prover = composer.create_ultra_with_keccak_prover(builder); @@ -240,7 +238,6 @@ TEST_F(AcirFormatTests, TestSchnorrVerifyPass) AcirFormat constraint_system{ .varnum = 81, - .recursive = false, .num_acir_opcodes = 76, .public_inputs = {}, .logic_constraints = {}, @@ -302,7 +299,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifyPass) witness[i] = message_string[i]; } - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness); auto composer = Composer(); auto prover = composer.create_ultra_with_keccak_prover(builder); @@ -346,7 +343,6 @@ TEST_F(AcirFormatTests, TestSchnorrVerifySmallRange) }; AcirFormat constraint_system{ .varnum = 81, - .recursive = false, .num_acir_opcodes = 76, .public_inputs = {}, .logic_constraints = {}, @@ -409,7 +405,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifySmallRange) } // TODO: actually sign a schnorr signature! - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness); auto composer = Composer(); auto prover = composer.create_ultra_with_keccak_prover(builder); @@ -455,7 +451,6 @@ TEST_F(AcirFormatTests, TestKeccakPermutation) AcirFormat constraint_system{ .varnum = 51, - .recursive = false, .num_acir_opcodes = 1, .public_inputs = {}, .logic_constraints = {}, @@ -491,7 +486,7 @@ TEST_F(AcirFormatTests, TestKeccakPermutation) 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 }; - auto builder = create_circuit(constraint_system, /*size_hint=*/0, witness); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint=*/0, witness); auto composer = Composer(); auto prover = composer.create_ultra_with_keccak_prover(builder); auto proof = prover.construct_proof(); @@ -529,7 +524,6 @@ TEST_F(AcirFormatTests, TestCollectsGateCounts) AcirFormat constraint_system{ .varnum = 4, - .recursive = false, .num_acir_opcodes = 2, .public_inputs = {}, .logic_constraints = {}, @@ -561,8 +555,13 @@ TEST_F(AcirFormatTests, TestCollectsGateCounts) }; mock_opcode_indices(constraint_system); WitnessVector witness{ 5, 27, 32 }; - auto builder = - create_circuit(constraint_system, /*size_hint*/ 0, witness, false, std::make_shared(), true); + auto builder = create_circuit(constraint_system, + /*recursive*/ false, + /*size_hint*/ 0, + witness, + false, + std::make_shared(), + true); EXPECT_EQ(constraint_system.gates_per_opcode, std::vector({ 2, 1 })); } @@ -655,7 +654,6 @@ TEST_F(AcirFormatTests, TestBigAdd) size_t num_variables = witness_values.size(); AcirFormat constraint_system{ .varnum = static_cast(num_variables + 1), - .recursive = false, .num_acir_opcodes = 1, .public_inputs = {}, .logic_constraints = {}, @@ -687,7 +685,7 @@ TEST_F(AcirFormatTests, TestBigAdd) }; mock_opcode_indices(constraint_system); - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness_values); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness_values); auto composer = Composer(); auto prover = composer.create_prover(builder); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp index 4420223e9dd..3719a9ec15c 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp @@ -154,7 +154,8 @@ TEST_P(AcirIntegrationSingleTest, DISABLED_ProveAndVerifyProgram) false); // TODO(https://github.com/AztecProtocol/barretenberg/issues/1013): Assumes Flavor is not UltraHonk // Construct a bberg circuit from the acir representation - Builder builder = acir_format::create_circuit(acir_program.constraints, 0, acir_program.witness); + Builder builder = + acir_format::create_circuit(acir_program.constraints, /*recursive*/ false, 0, acir_program.witness); // Construct and verify Honk proof if constexpr (IsPlonkFlavor) { @@ -192,7 +193,6 @@ INSTANTIATE_TEST_SUITE_P(AcirTests, "array_to_slice_constant_length", "assert", "assert_statement", - "assert_statement_recursive", "assign_ex", "bigint", "bit_and", @@ -259,7 +259,6 @@ INSTANTIATE_TEST_SUITE_P(AcirTests, "diamond_deps_0", "double_verify_nested_proof", "double_verify_proof", - "double_verify_proof_recursive", "ecdsa_secp256k1", "ecdsa_secp256r1", "ecdsa_secp256r1_3x", @@ -381,7 +380,8 @@ TEST_P(AcirIntegrationFoldingTest, DISABLED_ProveAndVerifyProgramStack) auto program = program_stack.back(); // Construct a bberg circuit from the acir representation - auto builder = acir_format::create_circuit(program.constraints, 0, program.witness); + auto builder = + acir_format::create_circuit(program.constraints, /*recursive*/ false, 0, program.witness); // Construct and verify Honk proof for the individidual circuit EXPECT_TRUE(prove_and_verify_honk(builder)); @@ -408,8 +408,8 @@ TEST_P(AcirIntegrationFoldingTest, DISABLED_FoldAndVerifyProgramStack) auto program = program_stack.back(); // Construct a bberg circuit from the acir representation - auto circuit = - acir_format::create_circuit(program.constraints, 0, program.witness, false, ivc.goblin.op_queue); + auto circuit = acir_format::create_circuit( + program.constraints, /*recursive*/ false, 0, program.witness, false, ivc.goblin.op_queue); ivc.accumulate(circuit); @@ -440,7 +440,8 @@ TEST_F(AcirIntegrationTest, DISABLED_Databus) acir_format::AcirProgram acir_program = get_program_data_from_test_file(test_name); // Construct a bberg circuit from the acir representation - Builder builder = acir_format::create_circuit(acir_program.constraints, 0, acir_program.witness); + Builder builder = + acir_format::create_circuit(acir_program.constraints, /*recursive*/ false, 0, acir_program.witness); // This prints a summary of the types of gates in the circuit builder.blocks.summarize(); @@ -464,7 +465,8 @@ TEST_F(AcirIntegrationTest, DISABLED_DatabusTwoCalldata) acir_format::AcirProgram acir_program = get_program_data_from_test_file(test_name); // Construct a bberg circuit from the acir representation - Builder builder = acir_format::create_circuit(acir_program.constraints, 0, acir_program.witness); + Builder builder = + acir_format::create_circuit(acir_program.constraints, /*recursive*/ false, 0, acir_program.witness); // Check that the databus columns in the builder have been populated as expected const auto& calldata = builder.get_calldata(); @@ -518,7 +520,8 @@ TEST_F(AcirIntegrationTest, DISABLED_UpdateAcirCircuit) // Assumes Flavor is not UltraHonk // Construct a bberg circuit from the acir representation - auto circuit = acir_format::create_circuit(acir_program.constraints, 0, acir_program.witness); + auto circuit = + acir_format::create_circuit(acir_program.constraints, /*recursive*/ false, 0, acir_program.witness); EXPECT_TRUE(CircuitChecker::check(circuit)); @@ -557,7 +560,8 @@ TEST_F(AcirIntegrationTest, DISABLED_HonkRecursion) /*honk_recursion=*/false); // Construct a bberg circuit from the acir representation - auto circuit = acir_format::create_circuit(acir_program.constraints, 0, acir_program.witness); + auto circuit = + acir_format::create_circuit(acir_program.constraints, /*recursive*/ false, 0, acir_program.witness); EXPECT_TRUE(CircuitChecker::check(circuit)); EXPECT_TRUE(prove_and_verify_honk(circuit)); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp index bf7eb2dd8de..2dd6e2df128 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp @@ -800,7 +800,6 @@ AcirFormat circuit_serde_to_acir_format(Program::Circuit const& circuit, bool ho AcirFormat af; // `varnum` is the true number of variables, thus we add one to the index which starts at zero af.varnum = circuit.current_witness_index + 1; - af.recursive = circuit.recursive; af.num_acir_opcodes = static_cast(circuit.opcodes.size()); af.public_inputs = join({ map(circuit.public_parameters.value, [](auto e) { return e.value; }), map(circuit.return_values.value, [](auto e) { return e.value; }) }); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.test.cpp index c4e98e0d88f..bb49d13b0e4 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.test.cpp @@ -117,13 +117,13 @@ class AcirAvmRecursionConstraint : public ::testing::Test { AcirFormat constraint_system; constraint_system.varnum = static_cast(witness.size()); - constraint_system.recursive = false; constraint_system.num_acir_opcodes = static_cast(avm_recursion_constraints.size()); constraint_system.avm_recursion_constraints = avm_recursion_constraints; constraint_system.original_opcode_indices = create_empty_original_opcode_indices(); mock_opcode_indices(constraint_system); - auto outer_circuit = create_circuit(constraint_system, /*size_hint*/ 0, witness, /*honk_recursion=*/true); + auto outer_circuit = + create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness, /*honk_recursion=*/true); return outer_circuit; } }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp index 295b66cdba7..fbb07a8bdc1 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp @@ -172,7 +172,6 @@ TEST_F(BigIntTests, TestBigIntConstraintMultiple) auto contraints5 = generate_big_int_op_constraint(BigIntOperationType::Div, fr(8), fr(2), witness); AcirFormat constraint_system{ .varnum = static_cast(witness.size() + 1), - .recursive = false, .num_acir_opcodes = 5, .public_inputs = {}, .logic_constraints = {}, @@ -210,7 +209,7 @@ TEST_F(BigIntTests, TestBigIntConstraintMultiple) mock_opcode_indices(constraint_system); constraint_system.varnum = static_cast(witness.size() + 1); - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness); auto composer = Composer(); auto prover = composer.create_ultra_with_keccak_prover(builder); @@ -245,7 +244,6 @@ TEST_F(BigIntTests, TestBigIntConstraintSimple) AcirFormat constraint_system{ .varnum = 5, - .recursive = false, .num_acir_opcodes = 3, .public_inputs = {}, .logic_constraints = {}, @@ -280,7 +278,7 @@ TEST_F(BigIntTests, TestBigIntConstraintSimple) WitnessVector witness{ 0, 3, 6, 3, 0, }; - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness); auto composer = Composer(); auto prover = composer.create_ultra_with_keccak_prover(builder); auto proof = prover.construct_proof(); @@ -302,7 +300,6 @@ TEST_F(BigIntTests, TestBigIntConstraintReuse) AcirFormat constraint_system{ .varnum = static_cast(witness.size() + 1), - .recursive = false, .num_acir_opcodes = 5, .public_inputs = {}, .logic_constraints = {}, @@ -343,7 +340,7 @@ TEST_F(BigIntTests, TestBigIntConstraintReuse) constraint_system.varnum = static_cast(witness.size() + 1); mock_opcode_indices(constraint_system); - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness); auto composer = Composer(); auto prover = composer.create_ultra_with_keccak_prover(builder); @@ -364,7 +361,6 @@ TEST_F(BigIntTests, TestBigIntConstraintReuse2) AcirFormat constraint_system{ .varnum = static_cast(witness.size() + 1), - .recursive = false, .num_acir_opcodes = 5, .public_inputs = {}, .logic_constraints = {}, @@ -405,7 +401,7 @@ TEST_F(BigIntTests, TestBigIntConstraintReuse2) constraint_system.varnum = static_cast(witness.size() + 1); mock_opcode_indices(constraint_system); - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness); auto composer = Composer(); auto prover = composer.create_ultra_with_keccak_prover(builder); @@ -447,7 +443,6 @@ TEST_F(BigIntTests, TestBigIntDIV) AcirFormat constraint_system{ .varnum = 5, - .recursive = false, .num_acir_opcodes = 4, .public_inputs = {}, .logic_constraints = {}, @@ -482,13 +477,13 @@ TEST_F(BigIntTests, TestBigIntDIV) WitnessVector witness{ 0, 6, 3, 2, 0, }; - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness); auto composer = Composer(); auto prover = composer.create_ultra_with_keccak_prover(builder); auto proof = prover.construct_proof(); EXPECT_TRUE(CircuitChecker::check(builder)); - auto builder2 = create_circuit(constraint_system, /*size_hint*/ 0, WitnessVector{}); + auto builder2 = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, WitnessVector{}); EXPECT_TRUE(CircuitChecker::check(builder)); auto verifier2 = composer.create_ultra_with_keccak_verifier(builder); EXPECT_EQ(verifier2.verify_proof(proof), true); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp index 8aec43aa612..571172e6876 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp @@ -140,7 +140,6 @@ TEST_F(UltraPlonkRAM, TestBlockConstraint) size_t num_variables = generate_block_constraint(block, witness_values); AcirFormat constraint_system{ .varnum = static_cast(num_variables), - .recursive = false, .num_acir_opcodes = 7, .public_inputs = {}, .logic_constraints = {}, @@ -172,7 +171,7 @@ TEST_F(UltraPlonkRAM, TestBlockConstraint) }; mock_opcode_indices(constraint_system); - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness_values); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness_values); auto composer = Composer(); auto prover = composer.create_prover(builder); @@ -191,7 +190,6 @@ TEST_F(MegaHonk, Databus) AcirFormat constraint_system{ .varnum = static_cast(num_variables), - .recursive = false, .num_acir_opcodes = 1, .public_inputs = {}, .logic_constraints = {}, @@ -224,7 +222,7 @@ TEST_F(MegaHonk, Databus) mock_opcode_indices(constraint_system); // Construct a bberg circuit from the acir representation - auto circuit = acir_format::create_circuit(constraint_system, 0, witness_values); + auto circuit = acir_format::create_circuit(constraint_system, /*recursive*/ false, 0, witness_values); EXPECT_TRUE(prove_and_verify(circuit)); } @@ -297,7 +295,6 @@ TEST_F(MegaHonk, DatabusReturn) AcirFormat constraint_system{ .varnum = static_cast(num_variables), - .recursive = false, .num_acir_opcodes = 2, .public_inputs = {}, .logic_constraints = {}, @@ -330,7 +327,7 @@ TEST_F(MegaHonk, DatabusReturn) mock_opcode_indices(constraint_system); // Construct a bberg circuit from the acir representation - auto circuit = acir_format::create_circuit(constraint_system, 0, witness_values); + auto circuit = acir_format::create_circuit(constraint_system, /*recursive*/ false, 0, witness_values); EXPECT_TRUE(prove_and_verify(circuit)); } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp index 193388efb3b..8f254c77c0d 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp @@ -62,7 +62,6 @@ TEST_F(EcOperations, TestECOperations) AcirFormat constraint_system{ .varnum = static_cast(num_variables + 1), - .recursive = false, .num_acir_opcodes = 1, .public_inputs = {}, .logic_constraints = {}, @@ -94,7 +93,7 @@ TEST_F(EcOperations, TestECOperations) }; mock_opcode_indices(constraint_system); - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness_values); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness_values); auto composer = Composer(); auto prover = composer.create_prover(builder); @@ -198,7 +197,6 @@ TEST_F(EcOperations, TestECMultiScalarMul) size_t num_variables = witness_values.size(); AcirFormat constraint_system{ .varnum = static_cast(num_variables + 1), - .recursive = false, .num_acir_opcodes = 1, .public_inputs = {}, .logic_constraints = {}, @@ -230,7 +228,7 @@ TEST_F(EcOperations, TestECMultiScalarMul) }; mock_opcode_indices(constraint_system); - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness_values); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness_values); auto composer = Composer(); auto prover = composer.create_prover(builder); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp index 041c925122a..e6a9245a640 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp @@ -94,7 +94,6 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintSucceed) size_t num_variables = generate_ecdsa_constraint(ecdsa_k1_constraint, witness_values); AcirFormat constraint_system{ .varnum = static_cast(num_variables), - .recursive = false, .num_acir_opcodes = 1, .public_inputs = {}, .logic_constraints = {}, @@ -126,7 +125,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintSucceed) }; mock_opcode_indices(constraint_system); - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness_values); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness_values); EXPECT_EQ(builder.get_variable(ecdsa_k1_constraint.result), 1); @@ -148,7 +147,6 @@ TEST_F(ECDSASecp256k1, TestECDSACompilesForVerifier) size_t num_variables = generate_ecdsa_constraint(ecdsa_k1_constraint, witness_values); AcirFormat constraint_system{ .varnum = static_cast(num_variables), - .recursive = false, .num_acir_opcodes = 1, .public_inputs = {}, .logic_constraints = {}, @@ -180,7 +178,7 @@ TEST_F(ECDSASecp256k1, TestECDSACompilesForVerifier) }; mock_opcode_indices(constraint_system); - auto builder = create_circuit(constraint_system); + auto builder = create_circuit(constraint_system, /*recursive*/ false); } TEST_F(ECDSASecp256k1, TestECDSAConstraintFail) @@ -197,7 +195,6 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintFail) AcirFormat constraint_system{ .varnum = static_cast(num_variables), - .recursive = false, .num_acir_opcodes = 1, .public_inputs = {}, .logic_constraints = {}, @@ -229,7 +226,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintFail) }; mock_opcode_indices(constraint_system); - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness_values); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness_values); EXPECT_EQ(builder.get_variable(ecdsa_k1_constraint.result), 0); auto composer = Composer(); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp index b6d29989d83..077f51b1bc2 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp @@ -128,7 +128,6 @@ TEST(ECDSASecp256r1, test_hardcoded) AcirFormat constraint_system{ .varnum = static_cast(num_variables), - .recursive = false, .num_acir_opcodes = 1, .public_inputs = {}, .logic_constraints = {}, @@ -165,7 +164,7 @@ TEST(ECDSASecp256r1, test_hardcoded) ecdsa_verify_signature(message, pub_key, signature); EXPECT_EQ(we_ballin, true); - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness_values); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness_values); EXPECT_EQ(builder.get_variable(ecdsa_r1_constraint.result), 1); auto composer = Composer(); @@ -184,7 +183,6 @@ TEST(ECDSASecp256r1, TestECDSAConstraintSucceed) size_t num_variables = generate_ecdsa_constraint(ecdsa_r1_constraint, witness_values); AcirFormat constraint_system{ .varnum = static_cast(num_variables), - .recursive = false, .num_acir_opcodes = 1, .public_inputs = {}, .logic_constraints = {}, @@ -216,7 +214,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintSucceed) }; mock_opcode_indices(constraint_system); - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness_values); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness_values); EXPECT_EQ(builder.get_variable(ecdsa_r1_constraint.result), 1); auto composer = Composer(); @@ -238,7 +236,6 @@ TEST(ECDSASecp256r1, TestECDSACompilesForVerifier) size_t num_variables = generate_ecdsa_constraint(ecdsa_r1_constraint, witness_values); AcirFormat constraint_system{ .varnum = static_cast(num_variables), - .recursive = false, .num_acir_opcodes = 1, .public_inputs = {}, .logic_constraints = {}, @@ -270,7 +267,7 @@ TEST(ECDSASecp256r1, TestECDSACompilesForVerifier) }; mock_opcode_indices(constraint_system); - auto builder = create_circuit(constraint_system); + auto builder = create_circuit(constraint_system, /*recursive*/ false); } TEST(ECDSASecp256r1, TestECDSAConstraintFail) @@ -288,7 +285,6 @@ TEST(ECDSASecp256r1, TestECDSAConstraintFail) AcirFormat constraint_system{ .varnum = static_cast(num_variables), - .recursive = false, .num_acir_opcodes = 1, .public_inputs = {}, .logic_constraints = {}, @@ -320,7 +316,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintFail) }; mock_opcode_indices(constraint_system); - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness_values); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness_values); EXPECT_EQ(builder.get_variable(ecdsa_r1_constraint.result), 0); auto composer = Composer(); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp index 3914ef1c38b..de6005c1942 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp @@ -90,7 +90,6 @@ class AcirHonkRecursionConstraint : public ::testing::Test { AcirFormat constraint_system{ .varnum = 6, - .recursive = true, .num_acir_opcodes = 7, .public_inputs = { 1, 2 }, .logic_constraints = { logic_constraint }, @@ -126,7 +125,8 @@ class AcirHonkRecursionConstraint : public ::testing::Test { WitnessVector witness{ 5, 10, 15, 5, inverse_of_five, 1, }; - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness, /*honk recursion*/ true); + auto builder = + create_circuit(constraint_system, /*recursive*/ true, /*size_hint*/ 0, witness, /*honk recursion*/ true); return builder; } @@ -170,13 +170,13 @@ class AcirHonkRecursionConstraint : public ::testing::Test { AcirFormat constraint_system{}; constraint_system.varnum = static_cast(witness.size()); - constraint_system.recursive = true; constraint_system.num_acir_opcodes = static_cast(honk_recursion_constraints.size()); constraint_system.honk_recursion_constraints = honk_recursion_constraints; constraint_system.original_opcode_indices = create_empty_original_opcode_indices(); mock_opcode_indices(constraint_system); - auto outer_circuit = create_circuit(constraint_system, /*size_hint*/ 0, witness, /*honk recursion*/ true); + auto outer_circuit = + create_circuit(constraint_system, /*recursive*/ true, /*size_hint*/ 0, witness, /*honk recursion*/ true); return outer_circuit; } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.test.cpp index a77b017fdfa..11339b3de7b 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.test.cpp @@ -136,7 +136,6 @@ class IvcRecursionConstraintTest : public ::testing::Test { // Construct a constraint system containing the business logic and ivc recursion constraints program.constraints.varnum = static_cast(program.witness.size()); - program.constraints.recursive = false; program.constraints.num_acir_opcodes = static_cast(ivc_recursion_constraints.size()); program.constraints.poly_triple_constraints = { pub_input_constraint }; program.constraints.ivc_recursion_constraints = ivc_recursion_constraints; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.test.cpp index bc8dd7d8c90..fa8e711d0af 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.test.cpp @@ -62,7 +62,6 @@ TEST_F(MSMTests, TestMSM) AcirFormat constraint_system{ .varnum = 9, - .recursive = false, .num_acir_opcodes = 1, .public_inputs = {}, .logic_constraints = {}, @@ -101,12 +100,12 @@ TEST_F(MSMTests, TestMSM) fr(0), }; - auto builder = create_circuit(constraint_system, /*size_hint=*/0, witness); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint=*/0, witness); auto composer = Composer(); auto prover = composer.create_ultra_with_keccak_prover(builder); auto proof = prover.construct_proof(); - auto builder2 = create_circuit(constraint_system, /*size_hint=*/0, {}); + auto builder2 = create_circuit(constraint_system, /*recursive*/ false, /*size_hint=*/0, {}); auto composer2 = Composer(); auto verifier = composer2.create_ultra_with_keccak_verifier(builder2); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp index 9c07431abe8..6cb0592d9aa 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp @@ -42,7 +42,6 @@ TEST_F(Poseidon2Tests, TestPoseidon2Permutation) AcirFormat constraint_system{ .varnum = 9, - .recursive = false, .num_acir_opcodes = 1, .public_inputs = {}, .logic_constraints = {}, @@ -86,7 +85,7 @@ TEST_F(Poseidon2Tests, TestPoseidon2Permutation) fr(std::string("0x2e11c5cff2a22c64d01304b778d78f6998eff1ab73163a35603f54794c30847a")), }; - auto builder = create_circuit(constraint_system, /*size_hint=*/0, witness); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint=*/0, witness); auto composer = Composer(); auto prover = composer.create_ultra_with_keccak_prover(builder); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index f0f1fe1bff2..0015e9390ae 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -114,11 +114,12 @@ AggregationObjectIndices create_recursion_constraints(Builder& builder, if (!inner_aggregation_indices_all_zero) { std::array aggregation_elements; for (size_t i = 0; i < 4; ++i) { - aggregation_elements[i] = - bn254::BaseField(field_ct::from_witness_index(&builder, aggregation_input[4 * i]), - field_ct::from_witness_index(&builder, aggregation_input[4 * i + 1]), - field_ct::from_witness_index(&builder, aggregation_input[4 * i + 2]), - field_ct::from_witness_index(&builder, aggregation_input[4 * i + 3])); + aggregation_elements[i] = bn254::BaseField::construct_from_limbs( + field_ct::from_witness_index(&builder, aggregation_input[4 * i]), + field_ct::from_witness_index(&builder, aggregation_input[4 * i + 1]), + field_ct::from_witness_index(&builder, aggregation_input[4 * i + 2]), + field_ct::from_witness_index(&builder, aggregation_input[4 * i + 3])); + aggregation_elements[i].assert_is_in_field(); } // If we have a previous aggregation object, assign it to `previous_aggregation` so that it is included diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index 301bd915005..ddaf4e6ac7c 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -87,7 +87,6 @@ Builder create_inner_circuit() AcirFormat constraint_system{ .varnum = 6, - .recursive = true, .num_acir_opcodes = 7, .public_inputs = { 1, 2 }, .logic_constraints = { logic_constraint }, @@ -123,7 +122,7 @@ Builder create_inner_circuit() WitnessVector witness{ 5, 10, 15, 5, inverse_of_five, 1, }; - auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness); + auto builder = create_circuit(constraint_system, /*recursive*/ true, /*size_hint*/ 0, witness); return builder; } @@ -248,7 +247,6 @@ Builder create_outer_circuit(std::vector& inner_circuits) AcirFormat constraint_system{ .varnum = static_cast(witness.size()), - .recursive = false, .num_acir_opcodes = static_cast(recursion_constraints.size()), .public_inputs = {}, .logic_constraints = {}, @@ -280,7 +278,7 @@ Builder create_outer_circuit(std::vector& inner_circuits) }; mock_opcode_indices(constraint_system); - auto outer_circuit = create_circuit(constraint_system, /*size_hint*/ 0, witness); + auto outer_circuit = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness); return outer_circuit; } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp index 7fd958cc60f..a96a5531a0e 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp @@ -1,6 +1,5 @@ #pragma once -#include "barretenberg/common/throw_or_abort.hpp" #include "bincode.hpp" #include "serde.hpp" @@ -1276,24 +1275,8 @@ struct ExpressionOrMemory { }; struct AssertionPayload { - - struct StaticString { - std::string value; - - friend bool operator==(const StaticString&, const StaticString&); - std::vector bincodeSerialize() const; - static StaticString bincodeDeserialize(std::vector); - }; - - struct Dynamic { - std::tuple> value; - - friend bool operator==(const Dynamic&, const Dynamic&); - std::vector bincodeSerialize() const; - static Dynamic bincodeDeserialize(std::vector); - }; - - std::variant value; + uint64_t error_selector; + std::vector payload; friend bool operator==(const AssertionPayload&, const AssertionPayload&); std::vector bincodeSerialize() const; @@ -1365,7 +1348,6 @@ struct Circuit { Program::PublicInputs public_parameters; Program::PublicInputs return_values; std::vector> assert_messages; - bool recursive; friend bool operator==(const Circuit&, const Circuit&); std::vector bincodeSerialize() const; @@ -1395,7 +1377,10 @@ namespace Program { inline bool operator==(const AssertionPayload& lhs, const AssertionPayload& rhs) { - if (!(lhs.value == rhs.value)) { + if (!(lhs.error_selector == rhs.error_selector)) { + return false; + } + if (!(lhs.payload == rhs.payload)) { return false; } return true; @@ -1426,7 +1411,8 @@ void serde::Serializable::serialize(const Program::As Serializer& serializer) { serializer.increase_container_depth(); - serde::Serializable::serialize(obj.value, serializer); + serde::Serializable::serialize(obj.error_selector, serializer); + serde::Serializable::serialize(obj.payload, serializer); serializer.decrease_container_depth(); } @@ -1436,107 +1422,14 @@ Program::AssertionPayload serde::Deserializable::dese { deserializer.increase_container_depth(); Program::AssertionPayload obj; - obj.value = serde::Deserializable::deserialize(deserializer); + obj.error_selector = serde::Deserializable::deserialize(deserializer); + obj.payload = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } namespace Program { -inline bool operator==(const AssertionPayload::StaticString& lhs, const AssertionPayload::StaticString& rhs) -{ - if (!(lhs.value == rhs.value)) { - return false; - } - return true; -} - -inline std::vector AssertionPayload::StaticString::bincodeSerialize() const -{ - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); -} - -inline AssertionPayload::StaticString AssertionPayload::StaticString::bincodeDeserialize(std::vector input) -{ - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw_or_abort("Some input bytes were not read"); - } - return value; -} - -} // end of namespace Program - -template <> -template -void serde::Serializable::serialize( - const Program::AssertionPayload::StaticString& obj, Serializer& serializer) -{ - serde::Serializable::serialize(obj.value, serializer); -} - -template <> -template -Program::AssertionPayload::StaticString serde::Deserializable::deserialize( - Deserializer& deserializer) -{ - Program::AssertionPayload::StaticString obj; - obj.value = serde::Deserializable::deserialize(deserializer); - return obj; -} - -namespace Program { - -inline bool operator==(const AssertionPayload::Dynamic& lhs, const AssertionPayload::Dynamic& rhs) -{ - if (!(lhs.value == rhs.value)) { - return false; - } - return true; -} - -inline std::vector AssertionPayload::Dynamic::bincodeSerialize() const -{ - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); -} - -inline AssertionPayload::Dynamic AssertionPayload::Dynamic::bincodeDeserialize(std::vector input) -{ - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw_or_abort("Some input bytes were not read"); - } - return value; -} - -} // end of namespace Program - -template <> -template -void serde::Serializable::serialize(const Program::AssertionPayload::Dynamic& obj, - Serializer& serializer) -{ - serde::Serializable::serialize(obj.value, serializer); -} - -template <> -template -Program::AssertionPayload::Dynamic serde::Deserializable::deserialize( - Deserializer& deserializer) -{ - Program::AssertionPayload::Dynamic obj; - obj.value = serde::Deserializable::deserialize(deserializer); - return obj; -} - -namespace Program { - inline bool operator==(const BinaryFieldOp& lhs, const BinaryFieldOp& rhs) { if (!(lhs.value == rhs.value)) { @@ -6724,9 +6617,6 @@ inline bool operator==(const Circuit& lhs, const Circuit& rhs) if (!(lhs.assert_messages == rhs.assert_messages)) { return false; } - if (!(lhs.recursive == rhs.recursive)) { - return false; - } return true; } @@ -6761,7 +6651,6 @@ void serde::Serializable::serialize(const Program::Circuit& ob serde::Serializable::serialize(obj.public_parameters, serializer); serde::Serializable::serialize(obj.return_values, serializer); serde::Serializable::serialize(obj.assert_messages, serializer); - serde::Serializable::serialize(obj.recursive, serializer); serializer.decrease_container_depth(); } @@ -6778,7 +6667,6 @@ Program::Circuit serde::Deserializable::deserialize(Deserializ obj.public_parameters = serde::Deserializable::deserialize(deserializer); obj.return_values = serde::Deserializable::deserialize(deserializer); obj.assert_messages = serde::Deserializable::deserialize(deserializer); - obj.recursive = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp index b057ee17493..f59dfb8b9b8 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp @@ -37,7 +37,6 @@ TEST_F(Sha256Tests, TestSha256Compression) AcirFormat constraint_system{ .varnum = 34, - .recursive = false, .num_acir_opcodes = 1, .public_inputs = {}, .logic_constraints = {}, @@ -103,7 +102,7 @@ TEST_F(Sha256Tests, TestSha256Compression) 557795688, static_cast(3481642555) }; - auto builder = create_circuit(constraint_system, /*size_hint=*/0, witness); + auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint=*/0, witness); auto composer = Composer(); auto prover = composer.create_ultra_with_keccak_prover(builder); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp index 341d06d3541..fd1495414b5 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp @@ -30,12 +30,19 @@ AcirComposer::AcirComposer(size_t size_hint, bool verbose) */ template void AcirComposer::create_finalized_circuit(acir_format::AcirFormat& constraint_system, + bool recursive, WitnessVector const& witness, bool collect_gates_per_opcode) { vinfo("building circuit..."); - builder_ = acir_format::create_circuit( - constraint_system, size_hint_, witness, false, std::make_shared(), collect_gates_per_opcode); + vinfo("should be recursive friendly: ", recursive); + builder_ = acir_format::create_circuit(constraint_system, + recursive, + size_hint_, + witness, + false, + std::make_shared(), + collect_gates_per_opcode); finalize_circuit(); vinfo("gates: ", builder_.get_estimated_total_circuit_size()); vinfo("circuit is recursive friendly: ", builder_.is_recursive_circuit); @@ -60,9 +67,11 @@ std::vector AcirComposer::create_proof() vinfo("creating proof..."); std::vector proof; if (builder_.is_recursive_circuit) { + vinfo("creating recursive prover..."); auto prover = composer.create_prover(builder_); proof = prover.construct_proof().proof_data; } else { + vinfo("creating ultra with keccak prover..."); auto prover = composer.create_ultra_with_keccak_prover(builder_); proof = prover.construct_proof().proof_data; } @@ -149,6 +158,7 @@ std::vector AcirComposer::serialize_verification_key_into_fields() } template void AcirComposer::create_finalized_circuit(acir_format::AcirFormat& constraint_system, + bool recursive, WitnessVector const& witness, bool collect_gates_per_opcode); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.hpp index 73ca0b11e11..682f3a51da4 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.hpp @@ -18,6 +18,7 @@ class AcirComposer { template void create_finalized_circuit(acir_format::AcirFormat& constraint_system, + bool recursive, WitnessVector const& witness = {}, bool collect_gates_per_opcode = false); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp index 48e9eb7ef14..16553a32d4a 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp @@ -13,14 +13,12 @@ #include #include -WASM_EXPORT void acir_get_circuit_sizes(uint8_t const* acir_vec, - bool const* honk_recursion, - uint32_t* total, - uint32_t* subgroup) +WASM_EXPORT void acir_get_circuit_sizes( + uint8_t const* acir_vec, bool const* recursive, bool const* honk_recursion, uint32_t* total, uint32_t* subgroup) { auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), *honk_recursion); - auto builder = acir_format::create_circuit(constraint_system, 1 << 19, {}, *honk_recursion); + auto builder = acir_format::create_circuit(constraint_system, recursive, 1 << 19, {}, *honk_recursion); builder.finalize_circuit(/*ensure_nonzero=*/true); *total = htonl((uint32_t)builder.get_finalized_total_circuit_size()); *subgroup = htonl((uint32_t)builder.get_circuit_subgroup_size(builder.get_finalized_total_circuit_size())); @@ -36,41 +34,42 @@ WASM_EXPORT void acir_delete_acir_composer(in_ptr acir_composer_ptr) delete reinterpret_cast(*acir_composer_ptr); } -WASM_EXPORT void acir_init_proving_key(in_ptr acir_composer_ptr, uint8_t const* acir_vec) +WASM_EXPORT void acir_init_proving_key(in_ptr acir_composer_ptr, uint8_t const* acir_vec, bool const* recursive) { auto acir_composer = reinterpret_cast(*acir_composer_ptr); auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), /*honk_recursion=*/false); - acir_composer->create_finalized_circuit(constraint_system); + acir_composer->create_finalized_circuit(constraint_system, *recursive); acir_composer->init_proving_key(); } -WASM_EXPORT void acir_create_proof(in_ptr acir_composer_ptr, - uint8_t const* acir_vec, - uint8_t const* witness_vec, - uint8_t** out) +WASM_EXPORT void acir_create_proof( + in_ptr acir_composer_ptr, uint8_t const* acir_vec, bool const* recursive, uint8_t const* witness_vec, uint8_t** out) { auto acir_composer = reinterpret_cast(*acir_composer_ptr); auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), /*honk_recursion=*/false); auto witness = acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)); - acir_composer->create_finalized_circuit(constraint_system, witness); + acir_composer->create_finalized_circuit(constraint_system, *recursive, witness); acir_composer->init_proving_key(); auto proof_data = acir_composer->create_proof(); *out = to_heap_buffer(proof_data); } -WASM_EXPORT void acir_prove_and_verify_ultra_honk(uint8_t const* acir_vec, uint8_t const* witness_vec, bool* result) +WASM_EXPORT void acir_prove_and_verify_ultra_honk(uint8_t const* acir_vec, + bool const* recursive, + uint8_t const* witness_vec, + bool* result) { auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), /*honk_recursion=*/true); auto witness = acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)); - auto builder = - acir_format::create_circuit(constraint_system, 0, witness, /*honk_recursion=*/true); + auto builder = acir_format::create_circuit( + constraint_system, *recursive, 0, witness, /*honk_recursion=*/true); UltraProver prover{ builder }; auto proof = prover.construct_proof(); @@ -82,7 +81,10 @@ WASM_EXPORT void acir_prove_and_verify_ultra_honk(uint8_t const* acir_vec, uint8 info("verified: ", *result); } -WASM_EXPORT void acir_fold_and_verify_program_stack(uint8_t const* acir_vec, uint8_t const* witness_vec, bool* result) +WASM_EXPORT void acir_fold_and_verify_program_stack(uint8_t const* acir_vec, + bool const* recursive, + uint8_t const* witness_vec, + bool* result) { using ProgramStack = acir_format::AcirProgramStack; using Builder = MegaCircuitBuilder; @@ -102,8 +104,12 @@ WASM_EXPORT void acir_fold_and_verify_program_stack(uint8_t const* acir_vec, uin auto stack_item = program_stack.back(); // Construct a bberg circuit from the acir representation - auto builder = acir_format::create_circuit( - stack_item.constraints, 0, stack_item.witness, /*honk_recursion=*/false, ivc.goblin.op_queue); + auto builder = acir_format::create_circuit(stack_item.constraints, + *recursive, + 0, + stack_item.witness, + /*honk_recursion=*/false, + ivc.goblin.op_queue); builder.databus_propagation_data.is_kernel = is_kernel; is_kernel = !is_kernel; // toggle on/off so every second circuit is intepreted as a kernel @@ -116,14 +122,17 @@ WASM_EXPORT void acir_fold_and_verify_program_stack(uint8_t const* acir_vec, uin info("acir_fold_and_verify_program_stack result: ", *result); } -WASM_EXPORT void acir_prove_and_verify_mega_honk(uint8_t const* acir_vec, uint8_t const* witness_vec, bool* result) +WASM_EXPORT void acir_prove_and_verify_mega_honk(uint8_t const* acir_vec, + bool const* recursive, + uint8_t const* witness_vec, + bool* result) { auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), /*honk_recursion=*/false); auto witness = acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)); - auto builder = - acir_format::create_circuit(constraint_system, 0, witness, /*honk_recursion=*/false); + auto builder = acir_format::create_circuit( + constraint_system, *recursive, 0, witness, /*honk_recursion=*/false); MegaProver prover{ builder }; auto proof = prover.construct_proof(); @@ -155,12 +164,15 @@ WASM_EXPORT void acir_get_verification_key(in_ptr acir_composer_ptr, uint8_t** o *out = to_heap_buffer(to_buffer(*vk)); } -WASM_EXPORT void acir_get_proving_key(in_ptr acir_composer_ptr, uint8_t const* acir_vec, uint8_t** out) +WASM_EXPORT void acir_get_proving_key(in_ptr acir_composer_ptr, + uint8_t const* acir_vec, + bool const* recursive, + uint8_t** out) { auto acir_composer = reinterpret_cast(*acir_composer_ptr); auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), /*honk_recursion=*/false); - acir_composer->create_finalized_circuit(constraint_system); + acir_composer->create_finalized_circuit(constraint_system, *recursive); auto pk = acir_composer->init_proving_key(); // We flatten to a vector first, as that's how we treat it on the calling side. *out = to_heap_buffer(to_buffer(*pk)); @@ -206,14 +218,17 @@ WASM_EXPORT void acir_serialize_verification_key_into_fields(in_ptr acir_compose write(out_key_hash, vk_hash); } -WASM_EXPORT void acir_prove_ultra_honk(uint8_t const* acir_vec, uint8_t const* witness_vec, uint8_t** out) +WASM_EXPORT void acir_prove_ultra_honk(uint8_t const* acir_vec, + bool const* recursive, + uint8_t const* witness_vec, + uint8_t** out) { auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), /*honk_recursion=*/true); auto witness = acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)); - auto builder = - acir_format::create_circuit(constraint_system, 0, witness, /*honk_recursion=*/true); + auto builder = acir_format::create_circuit( + constraint_system, *recursive, 0, witness, /*honk_recursion=*/true); UltraProver prover{ builder }; auto proof = prover.construct_proof(); @@ -235,14 +250,15 @@ WASM_EXPORT void acir_verify_ultra_honk(uint8_t const* proof_buf, uint8_t const* *result = verifier.verify_proof(proof); } -WASM_EXPORT void acir_write_vk_ultra_honk(uint8_t const* acir_vec, uint8_t** out) +WASM_EXPORT void acir_write_vk_ultra_honk(uint8_t const* acir_vec, bool const* recursive, uint8_t** out) { using DeciderProvingKey = DeciderProvingKey_; using VerificationKey = UltraFlavor::VerificationKey; auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), /*honk_recursion=*/true); - auto builder = acir_format::create_circuit(constraint_system, 0, {}, /*honk_recursion=*/true); + auto builder = + acir_format::create_circuit(constraint_system, *recursive, 0, {}, /*honk_recursion=*/true); DeciderProvingKey proving_key(builder); VerificationKey vk(proving_key.proving_key); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp index 9015aeaf94d..2ed4613b666 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp @@ -7,6 +7,7 @@ using namespace bb; WASM_EXPORT void acir_get_circuit_sizes(uint8_t const* constraint_system_buf, + bool const* recursive, bool const* honk_recursion, uint32_t* total, uint32_t* subgroup); @@ -15,7 +16,9 @@ WASM_EXPORT void acir_new_acir_composer(uint32_t const* size_hint, out_ptr out); WASM_EXPORT void acir_delete_acir_composer(in_ptr acir_composer_ptr); -WASM_EXPORT void acir_init_proving_key(in_ptr acir_composer_ptr, uint8_t const* constraint_system_buf); +WASM_EXPORT void acir_init_proving_key(in_ptr acir_composer_ptr, + uint8_t const* constraint_system_buf, + bool const* recursive); /** * It would have been nice to just hold onto the constraint_system in the acir_composer, but we can't waste the @@ -24,6 +27,7 @@ WASM_EXPORT void acir_init_proving_key(in_ptr acir_composer_ptr, uint8_t const* */ WASM_EXPORT void acir_create_proof(in_ptr acir_composer_ptr, uint8_t const* constraint_system_buf, + bool const* recursive, uint8_t const* witness_buf, uint8_t** out); @@ -32,6 +36,7 @@ WASM_EXPORT void acir_create_proof(in_ptr acir_composer_ptr, * */ WASM_EXPORT void acir_prove_and_verify_ultra_honk(uint8_t const* constraint_system_buf, + bool const* recursive, uint8_t const* witness_buf, bool* result); @@ -40,6 +45,7 @@ WASM_EXPORT void acir_prove_and_verify_ultra_honk(uint8_t const* constraint_syst * */ WASM_EXPORT void acir_prove_and_verify_mega_honk(uint8_t const* constraint_system_buf, + bool const* recursive, uint8_t const* witness_buf, bool* result); @@ -48,6 +54,7 @@ WASM_EXPORT void acir_prove_and_verify_mega_honk(uint8_t const* constraint_syste * */ WASM_EXPORT void acir_fold_and_verify_program_stack(uint8_t const* constraint_system_buf, + bool const* recursive, uint8_t const* witness_buf, bool* result); @@ -57,7 +64,10 @@ WASM_EXPORT void acir_init_verification_key(in_ptr acir_composer_ptr); WASM_EXPORT void acir_get_verification_key(in_ptr acir_composer_ptr, uint8_t** out); -WASM_EXPORT void acir_get_proving_key(in_ptr acir_composer_ptr, uint8_t const* acir_vec, uint8_t** out); +WASM_EXPORT void acir_get_proving_key(in_ptr acir_composer_ptr, + uint8_t const* acir_vec, + bool const* recursive, + uint8_t** out); WASM_EXPORT void acir_verify_proof(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result); @@ -72,11 +82,14 @@ WASM_EXPORT void acir_serialize_verification_key_into_fields(in_ptr acir_compose fr::vec_out_buf out_vkey, fr::out_buf out_key_hash); -WASM_EXPORT void acir_prove_ultra_honk(uint8_t const* acir_vec, uint8_t const* witness_vec, uint8_t** out); +WASM_EXPORT void acir_prove_ultra_honk(uint8_t const* acir_vec, + bool const* recursive, + uint8_t const* witness_vec, + uint8_t** out); WASM_EXPORT void acir_verify_ultra_honk(uint8_t const* proof_buf, uint8_t const* vk_buf, bool* result); -WASM_EXPORT void acir_write_vk_ultra_honk(uint8_t const* acir_vec, uint8_t** out); +WASM_EXPORT void acir_write_vk_ultra_honk(uint8_t const* acir_vec, bool const* recursive, uint8_t** out); WASM_EXPORT void acir_proof_as_fields_ultra_honk(uint8_t const* proof_buf, fr::vec_out_buf out); diff --git a/barretenberg/cpp/src/barretenberg/ecc/curves/bn254/fq.test.cpp b/barretenberg/cpp/src/barretenberg/ecc/curves/bn254/fq.test.cpp index 343156b5c18..7281257356d 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/curves/bn254/fq.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ecc/curves/bn254/fq.test.cpp @@ -1,4 +1,6 @@ #include "fq.hpp" +#include "barretenberg/numeric/random/engine.hpp" +#include "barretenberg/numeric/uint256/uint256.hpp" #include "barretenberg/serialize/test_helper.hpp" #include @@ -526,4 +528,17 @@ TEST(fq, NegAndSelfNeg0CmpRegression) a_neg = 0; a_neg.self_neg(); EXPECT_EQ((a == a_neg), true); +} + +// This test shows that ((lo|hi)% modulus) in uint512_t is equivalent to (lo + 2^256 * hi) in field elements so we +// don't have to use the slow API (uint512_t' modulo operation) +TEST(fq, EquivalentRandomness) +{ + auto& engine = numeric::get_debug_randomness(); + uint512_t random_uint512 = engine.get_random_uint512(); + auto random_lo = fq(random_uint512.lo); + auto random_hi = fq(random_uint512.hi); + uint512_t q(fq::modulus); + constexpr auto pow_2_256 = fq(uint256_t(1) << 128).sqr(); + EXPECT_EQ(random_lo + pow_2_256 * random_hi, fq((random_uint512 % q).lo)); } \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/ecc/curves/bn254/fr.test.cpp b/barretenberg/cpp/src/barretenberg/ecc/curves/bn254/fr.test.cpp index 7d2d89edd04..803fe96d79a 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/curves/bn254/fr.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ecc/curves/bn254/fr.test.cpp @@ -357,10 +357,7 @@ TEST(fr, BatchInvert) } for (size_t i = 0; i < n; ++i) { - EXPECT_EQ(coeffs[i].data[0], 0UL); - EXPECT_EQ(coeffs[i].data[1], 0UL); - EXPECT_EQ(coeffs[i].data[2], 0UL); - EXPECT_EQ(coeffs[i].data[3], 0UL); + EXPECT_TRUE(coeffs[i].is_zero()); } } @@ -378,4 +375,16 @@ TEST(fr, Uint256Conversions) static_assert(a == c); EXPECT_EQ(a, c); +} +// This test shows that ((lo|hi)% modulus) in uint512_t is equivalent to (lo + 2^256 * hi) in field elements so we +// don't have to use the slow API (uint512_t's modulo operation) +TEST(fr, EquivalentRandomness) +{ + auto& engine = numeric::get_debug_randomness(); + uint512_t random_uint512 = engine.get_random_uint512(); + auto random_lo = fr(random_uint512.lo); + auto random_hi = fr(random_uint512.hi); + uint512_t r(fr::modulus); + constexpr auto pow_2_256 = fr(uint256_t(1) << 128).sqr(); + EXPECT_EQ(random_lo + pow_2_256 * random_hi, fr((random_uint512 % r).lo)); } \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/ecc/curves/secp256k1/secp256k1.test.cpp b/barretenberg/cpp/src/barretenberg/ecc/curves/secp256k1/secp256k1.test.cpp index 60d0f24af2a..0132643ced5 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/curves/secp256k1/secp256k1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ecc/curves/secp256k1/secp256k1.test.cpp @@ -134,6 +134,17 @@ TEST(secp256k1, TestSqr) } } +TEST(secp256k1, SqrtRandom) +{ + size_t n = 1; + for (size_t i = 0; i < n; ++i) { + secp256k1::fq input = secp256k1::fq::random_element().sqr(); + auto [is_sqr, root] = input.sqrt(); + secp256k1::fq root_test = root.sqr(); + EXPECT_EQ(root_test, input); + } +} + TEST(secp256k1, TestArithmetic) { secp256k1::fq a = secp256k1::fq::random_element(); diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp index 17f0113101d..af1643bdc1b 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp @@ -330,8 +330,10 @@ template struct alignas(32) field { * * @return if the element is a quadratic remainder, if it's not */ - constexpr std::pair sqrt() const noexcept; - + constexpr std::pair sqrt() const noexcept + requires((Params_::modulus_0 & 0x3UL) == 0x3UL); + constexpr std::pair sqrt() const noexcept + requires((Params_::modulus_0 & 0x3UL) != 0x3UL); BB_INLINE constexpr void self_neg() & noexcept; BB_INLINE constexpr void self_to_montgomery_form() & noexcept; diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_impl.hpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_impl.hpp index 7f92fd299c1..0ed0c481dec 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/fields/field_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_impl.hpp @@ -10,6 +10,7 @@ #include #include "./field_declarations.hpp" +#include "barretenberg/numeric/uint256/uint256.hpp" namespace bb { @@ -452,102 +453,187 @@ template void field::batch_invert(std::span coeffs) noexcept } } +/** + * @brief Implements an optimised variant of Tonelli-Shanks via lookup tables. + * Algorithm taken from https://cr.yp.to/papers/sqroot-20011123-retypeset20220327.pdf + * "FASTER SQUARE ROOTS IN ANNOYING FINITE FIELDS" by D. Bernstein + * Page 5 "Accelerated Discrete Logarithm" + * @tparam T + * @return constexpr field + */ template constexpr field field::tonelli_shanks_sqrt() const noexcept { BB_OP_COUNT_TRACK_NAME("fr::tonelli_shanks_sqrt"); // Tonelli-shanks algorithm begins by finding a field element Q and integer S, // such that (p - 1) = Q.2^{s} - - // We can compute the square root of a, by considering a^{(Q + 1) / 2} = R - // Once we have found such an R, we have - // R^{2} = a^{Q + 1} = a^{Q}a - // If a^{Q} = 1, we have found our square root. - // Otherwise, we have a^{Q} = t, where t is a 2^{s-1}'th root of unity. - // This is because t^{2^{s-1}} = a^{Q.2^{s-1}}. - // We know that (p - 1) = Q.w^{s}, therefore t^{2^{s-1}} = a^{(p - 1) / 2} - // From Euler's criterion, if a is a quadratic residue, a^{(p - 1) / 2} = 1 - // i.e. t^{2^{s-1}} = 1 - - // To proceed with computing our square root, we want to transform t into a smaller subgroup, - // specifically, the (s-2)'th roots of unity. - // We do this by finding some value b,such that - // (t.b^2)^{2^{s-2}} = 1 and R' = R.b - // Finding such a b is trivial, because from Euler's criterion, we know that, - // for any quadratic non-residue z, z^{(p - 1) / 2} = -1 - // i.e. z^{Q.2^{s-1}} = -1 - // => z^Q is a 2^{s-1}'th root of -1 - // => z^{Q^2} is a 2^{s-2}'th root of -1 - // Since t^{2^{s-1}} = 1, we know that t^{2^{s - 2}} = -1 - // => t.z^{Q^2} is a 2^{s - 2}'th root of unity. - - // We can iteratively transform t into ever smaller subgroups, until t = 1. - // At each iteration, we need to find a new value for b, which we can obtain - // by repeatedly squaring z^{Q} - constexpr uint256_t Q = (modulus - 1) >> static_cast(primitive_root_log_size() - 1); - constexpr uint256_t Q_minus_one_over_two = (Q - 1) >> 2; - - // __to_montgomery_form(Q_minus_one_over_two, Q_minus_one_over_two); - field z = coset_generator(0); // the generator is a non-residue - field b = pow(Q_minus_one_over_two); - field r = operator*(b); // r = a^{(Q + 1) / 2} - field t = r * b; // t = a^{(Q - 1) / 2 + (Q + 1) / 2} = a^{Q} + // We can determine s by counting the least significant set bit of `p - 1` + // We pick elements `r, g` such that g = r^Q and r is not a square. + // (the coset generators are all nonresidues and satisfy this condition) + // + // To find the square root of `u`, consider `v = u^(Q - 1 / 2)` + // There exists an integer `e` where uv^2 = g^e (see Theorem 3.1 in paper). + // If `u` is a square, `e` is even and (uvg^{−e/2})^2 = u^2v^2g^e = u^{Q+1}g^{-e} = u + // + // The goal of the algorithm is two fold: + // 1. find `e` given `u` + // 2. compute `sqrt(u) = uvg^{−e/2}` + constexpr uint256_t Q = (modulus - 1) >> static_cast(primitive_root_log_size()); + constexpr uint256_t Q_minus_one_over_two = (Q - 1) >> 1; + field v = pow(Q_minus_one_over_two); + field uv = operator*(v); // uv = u^{(Q + 1) / 2} + // uvv = g^e for some unknown e. Goal is to find e. + field uvv = uv * v; // uvv = u^{(Q - 1) / 2 + (Q + 1) / 2} = u^{Q} // check if t is a square with euler's criterion // if not, we don't have a quadratic residue and a has no square root! - field check = t; + field check = uvv; for (size_t i = 0; i < primitive_root_log_size() - 1; ++i) { check.self_sqr(); } - if (check != one()) { - return zero(); + if (check != 1) { + return 0; } - field t1 = z.pow(Q_minus_one_over_two); - field t2 = t1 * z; - field c = t2 * t1; // z^Q - size_t m = primitive_root_log_size(); + constexpr field g = coset_generator(0).pow(Q); + constexpr field g_inv = coset_generator(0).pow(modulus - 1 - Q); + constexpr size_t root_bits = primitive_root_log_size(); + constexpr size_t table_bits = 6; + constexpr size_t num_tables = root_bits / table_bits + (root_bits % table_bits != 0 ? 1 : 0); + constexpr size_t num_offset_tables = num_tables - 1; + constexpr size_t table_size = static_cast(1UL) << table_bits; + + using GTable = std::array; + constexpr auto get_g_table = [&](const field& h) { + GTable result; + result[0] = 1; + for (size_t i = 1; i < table_size; ++i) { + result[i] = result[i - 1] * h; + } + return result; + }; + constexpr std::array g_tables = [&]() { + field working_base = g_inv; + std::array result; + for (size_t i = 0; i < num_tables; ++i) { + result[i] = get_g_table(working_base); + for (size_t j = 0; j < table_bits; ++j) { + working_base.self_sqr(); + } + } + return result; + }(); + constexpr std::array offset_g_tables = [&]() { + field working_base = g_inv; + for (size_t i = 0; i < root_bits % table_bits; ++i) { + working_base.self_sqr(); + } + std::array result; + for (size_t i = 0; i < num_offset_tables; ++i) { + result[i] = get_g_table(working_base); + for (size_t j = 0; j < table_bits; ++j) { + working_base.self_sqr(); + } + } + return result; + }(); + + constexpr GTable root_table_a = get_g_table(g.pow(1UL << ((num_tables - 1) * table_bits))); + constexpr GTable root_table_b = get_g_table(g.pow(1UL << (root_bits - table_bits))); + // compute uvv^{2^table_bits}, uvv^{2^{table_bits*2}}, ..., uvv^{2^{table_bits*num_tables}} + std::array uvv_powers; + field base = uvv; + for (size_t i = 0; i < num_tables - 1; ++i) { + uvv_powers[i] = base; + for (size_t j = 0; j < table_bits; ++j) { + base.self_sqr(); + } + } + uvv_powers[num_tables - 1] = base; + std::array e_slices; + for (size_t i = 0; i < num_tables; ++i) { + size_t table_index = num_tables - 1 - i; + field target = uvv_powers[table_index]; + for (size_t j = 0; j < i; ++j) { + size_t e_idx = num_tables - 1 - (i - 1) + j; + size_t g_idx = num_tables - 2 - j; + + field g_lookup; + if (j != i - 1) { + g_lookup = offset_g_tables[g_idx - 1][e_slices[e_idx]]; // e1 + } else { + g_lookup = g_tables[g_idx][e_slices[e_idx]]; + } + target *= g_lookup; + } + size_t count = 0; + + if (i == 0) { + for (auto& x : root_table_a) { + if (x == target) { + break; + } + count += 1; + } + } else { + for (auto& x : root_table_b) { + if (x == target) { + break; + } + count += 1; + } + } - while (t != one()) { - size_t i = 0; - field t2m = t; + ASSERT(count != table_size); + e_slices[table_index] = count; + } - // find the smallest value of m, such that t^{2^m} = 1 - while (t2m != one()) { - t2m.self_sqr(); - i += 1; + // We want to compute g^{-e/2} which requires computing `e/2` via our slice representation + for (size_t i = 0; i < num_tables; ++i) { + auto& e_slice = e_slices[num_tables - 1 - i]; + // e_slices[num_tables - 1] is always even. + // From theorem 3.1 (https://cr.yp.to/papers/sqroot-20011123-retypeset20220327.pdf) + // if slice is odd, propagate the downshifted bit into previous slice value + if ((e_slice & 1UL) == 1UL) { + size_t borrow_value = (i == 1) ? 1UL << ((root_bits % table_bits) - 1) : (1UL << (table_bits - 1)); + e_slices[num_tables - i] += borrow_value; } + e_slice >>= 1; + } - size_t j = m - i - 1; - b = c; - while (j > 0) { - b.self_sqr(); - --j; - } // b = z^2^(m-i-1) - - c = b.sqr(); - t = t * c; - r = r * b; - m = i; + field g_pow_minus_e_over_2 = 1; + for (size_t i = 0; i < num_tables; ++i) { + if (i == 0) { + g_pow_minus_e_over_2 *= g_tables[i][e_slices[num_tables - 1 - i]]; + } else { + g_pow_minus_e_over_2 *= offset_g_tables[i - 1][e_slices[num_tables - 1 - i]]; + } } - return r; + return uv * g_pow_minus_e_over_2; } -template constexpr std::pair> field::sqrt() const noexcept +template +constexpr std::pair> field::sqrt() const noexcept + requires((T::modulus_0 & 0x3UL) == 0x3UL) { BB_OP_COUNT_TRACK_NAME("fr::sqrt"); - field root; - if constexpr ((T::modulus_0 & 0x3UL) == 0x3UL) { - constexpr uint256_t sqrt_exponent = (modulus + uint256_t(1)) >> 2; - root = pow(sqrt_exponent); - } else { - root = tonelli_shanks_sqrt(); - } + constexpr uint256_t sqrt_exponent = (modulus + uint256_t(1)) >> 2; + field root = pow(sqrt_exponent); if ((root * root) == (*this)) { return std::pair(true, root); } return std::pair(false, field::zero()); +} -} // namespace bb; +template +constexpr std::pair> field::sqrt() const noexcept + requires((T::modulus_0 & 0x3UL) != 0x3UL) +{ + field root = tonelli_shanks_sqrt(); + if ((root * root) == (*this)) { + return std::pair(true, root); + } + return std::pair(false, field::zero()); +} template constexpr field field::operator/(const field& other) const noexcept { @@ -602,11 +688,12 @@ template field field::random_element(numeric::RNG* engine) noexc if (engine == nullptr) { engine = &numeric::get_randomness(); } - - uint512_t source = engine->get_random_uint512(); - uint512_t q(modulus); - uint512_t reduced = source % q; - return field(reduced.lo); + constexpr field pow_2_256 = field(uint256_t(1) << 128).sqr(); + field lo; + field hi; + lo = engine->get_random_uint256(); + hi = engine->get_random_uint256(); + return lo + (pow_2_256 * hi); } template constexpr size_t field::primitive_root_log_size() noexcept @@ -634,8 +721,8 @@ constexpr std::array, field::COSET_GENERATOR_SIZE> field::compute size_t count = 1; while (count < n) { - // work_variable contains a new field element, and we need to test that, for all previous vector elements, - // result[i] / work_variable is not a member of our subgroup + // work_variable contains a new field element, and we need to test that, for all previous vector + // elements, result[i] / work_variable is not a member of our subgroup field work_inverse = work_variable.invert(); bool valid = true; for (size_t j = 0; j < count; ++j) { @@ -674,8 +761,9 @@ template void field::msgpack_pack(auto& packer) const // The field is first converted from Montgomery form, similar to how the old format did it. auto adjusted = from_montgomery_form(); - // The data is then converted to big endian format using htonll, which stands for "host to network long long". - // This is necessary because the data will be written to a raw msgpack buffer, which requires big endian format. + // The data is then converted to big endian format using htonll, which stands for "host to network long + // long". This is necessary because the data will be written to a raw msgpack buffer, which requires big + // endian format. uint64_t bin_data[4] = { htonll(adjusted.data[3]), htonll(adjusted.data[2]), htonll(adjusted.data[1]), htonll(adjusted.data[0]) }; @@ -693,8 +781,8 @@ template void field::msgpack_unpack(auto o) // The binary data is first extracted from the msgpack object. std::array raw_data = o; - // The binary data is then read as big endian uint64_t's. This is done by casting the raw data to uint64_t* and then - // using ntohll ("network to host long long") to correct the endianness to the host's endianness. + // The binary data is then read as big endian uint64_t's. This is done by casting the raw data to uint64_t* + // and then using ntohll ("network to host long long") to correct the endianness to the host's endianness. uint64_t* cast_data = (uint64_t*)&raw_data[0]; // NOLINT uint64_t reversed[] = { ntohll(cast_data[3]), ntohll(cast_data[2]), ntohll(cast_data[1]), ntohll(cast_data[0]) }; diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_builder_types.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_builder_types.hpp index 2db0d13abf2..a8158fdeed9 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_builder_types.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_builder_types.hpp @@ -33,6 +33,17 @@ template struct VMOperation { return res; } bool operator==(const VMOperation& other) const = default; + + std::array get_base_point_standard_form() const + { + uint256_t x(base_point.x); + uint256_t y(base_point.y); + if (base_point.is_point_at_infinity()) { + x = 0; + y = 0; + } + return { x, y }; + } }; template struct ScalarMul { uint32_t pc; diff --git a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp index 73c2ad839fb..2ddcdfb2038 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp @@ -57,11 +57,11 @@ class GoblinMockCircuits { PROFILE_THIS(); if (large) { // Results in circuit size 2^19 - stdlib::generate_sha256_test_circuit(builder, 12); + stdlib::generate_sha256_test_circuit(builder, 11); stdlib::generate_ecdsa_verification_test_circuit(builder, 10); stdlib::generate_merkle_membership_test_circuit(builder, 12); } else { // Results in circuit size 2^17 - stdlib::generate_sha256_test_circuit(builder, 9); + stdlib::generate_sha256_test_circuit(builder, 8); stdlib::generate_ecdsa_verification_test_circuit(builder, 2); stdlib::generate_merkle_membership_test_circuit(builder, 10); } @@ -192,7 +192,7 @@ class GoblinMockCircuits { // Add operations representing general kernel logic e.g. state updates. Note: these are structured to make // the kernel "full" within the dyadic size 2^17 - const size_t NUM_MERKLE_CHECKS = 20; + const size_t NUM_MERKLE_CHECKS = 19; const size_t NUM_ECDSA_VERIFICATIONS = 1; const size_t NUM_SHA_HASHES = 1; stdlib::generate_merkle_membership_test_circuit(builder, NUM_MERKLE_CHECKS); diff --git a/barretenberg/cpp/src/barretenberg/numeric/random/engine.cpp b/barretenberg/cpp/src/barretenberg/numeric/random/engine.cpp index 8af63177345..6288e007562 100644 --- a/barretenberg/cpp/src/barretenberg/numeric/random/engine.cpp +++ b/barretenberg/cpp/src/barretenberg/numeric/random/engine.cpp @@ -1,17 +1,76 @@ #include "engine.hpp" #include "barretenberg/common/assert.hpp" #include +#include #include #include +#include namespace bb::numeric { namespace { -auto generate_random_data() + +#if defined(__wasm__) || defined(__APPLE__) + +// In wasm and on mac os the API we are using can only give 256 bytes per call, so there is no point in creating a +// larger buffer +constexpr size_t RANDOM_BUFFER_SIZE = 256; +constexpr size_t BYTES_PER_GETENTROPY_READ = 256; + +#else + +// When working on native we allocate 1M of memory to sample randomness from urandom +constexpr size_t RANDOM_BUFFER_SIZE = 1UL << 20; + +#endif +struct RandomBufferWrapper { + // Buffer with randomness sampled from a CSPRNG + uint8_t buffer[RANDOM_BUFFER_SIZE]; + // Offset into the unused part of the buffer + ssize_t offset = -1; +}; +thread_local RandomBufferWrapper random_buffer_wrapper; +/** + * @brief Generate an array of random unsigned ints sampled from a CSPRNG + * + * @tparam size_in_unsigned_ints Size of the array + * @return std::array + */ +template std::array generate_random_data() { - std::array random_data; - std::random_device source; - std::generate(std::begin(random_data), std::end(random_data), std::ref(source)); + static_assert(size_in_unsigned_ints > 0); + static_assert(size_in_unsigned_ints <= 32); + std::array random_data; + constexpr size_t random_data_buffer_size = sizeof(random_data); + + // if the buffer is not initialized or doesn't contain enough bytes, sample randomness + // We could preserve the leftover bytes, but it's a bit messy + if (random_buffer_wrapper.offset == -1 || + (static_cast(random_buffer_wrapper.offset) + random_data_buffer_size) > RANDOM_BUFFER_SIZE) { + size_t bytes_left = RANDOM_BUFFER_SIZE; + uint8_t* current_offset = random_buffer_wrapper.buffer; + // Sample until we fill the buffer + while (bytes_left != 0) { +#if defined(__wasm__) || defined(__APPLE__) + // Sample through a "syscall" on wasm. We can't request more than 256, it fails and results in an infinite + // loop + ssize_t read_bytes = + getentropy(current_offset, BYTES_PER_GETENTROPY_READ) == -1 ? -1 : BYTES_PER_GETENTROPY_READ; +#else + // Sample from urandom on native + auto read_bytes = getrandom(current_offset, bytes_left, 0); +#endif + // If we read something, update the leftover + if (read_bytes != -1) { + current_offset += read_bytes; + bytes_left -= static_cast(read_bytes); + } + } + random_buffer_wrapper.offset = 0; + } + + memcpy(&random_data, random_buffer_wrapper.buffer + random_buffer_wrapper.offset, random_data_buffer_size); + random_buffer_wrapper.offset += static_cast(random_data_buffer_size); return random_data; } } // namespace @@ -20,28 +79,28 @@ class RandomEngine : public RNG { public: uint8_t get_random_uint8() override { - auto buf = generate_random_data(); + auto buf = generate_random_data<1>(); uint32_t out = buf[0]; return static_cast(out); } uint16_t get_random_uint16() override { - auto buf = generate_random_data(); + auto buf = generate_random_data<1>(); uint32_t out = buf[0]; return static_cast(out); } uint32_t get_random_uint32() override { - auto buf = generate_random_data(); + auto buf = generate_random_data<1>(); uint32_t out = buf[0]; return static_cast(out); } uint64_t get_random_uint64() override { - auto buf = generate_random_data(); + auto buf = generate_random_data<2>(); auto lo = static_cast(buf[0]); auto hi = static_cast(buf[1]); return (lo + (hi << 32ULL)); @@ -49,20 +108,26 @@ class RandomEngine : public RNG { uint128_t get_random_uint128() override { - auto big = get_random_uint256(); - auto lo = static_cast(big.data[0]); - auto hi = static_cast(big.data[1]); + const auto get64 = [](const std::array& buffer, const size_t offset) { + auto lo = static_cast(buffer[0 + offset]); + auto hi = static_cast(buffer[1 + offset]); + return (lo + (hi << 32ULL)); + }; + auto buf = generate_random_data<4>(); + auto lo = static_cast(get64(buf, 0)); + auto hi = static_cast(get64(buf, 2)); + return (lo + (hi << static_cast(64ULL))); } uint256_t get_random_uint256() override { - const auto get64 = [](const std::array& buffer, const size_t offset) { + const auto get64 = [](const std::array& buffer, const size_t offset) { auto lo = static_cast(buffer[0 + offset]); auto hi = static_cast(buffer[1 + offset]); return (lo + (hi << 32ULL)); }; - auto buf = generate_random_data(); + auto buf = generate_random_data<8>(); uint64_t lolo = get64(buf, 0); uint64_t lohi = get64(buf, 2); uint64_t hilo = get64(buf, 4); diff --git a/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp b/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp index e46237a4306..bf20e3800ca 100644 --- a/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp @@ -593,22 +593,20 @@ TYPED_TEST(ultra_plonk_composer, non_native_field_multiplication) const auto split_into_limbs = [&](const uint512_t& input) { constexpr size_t NUM_BITS = 68; - std::array limbs; + std::array limbs; limbs[0] = input.slice(0, NUM_BITS).lo; limbs[1] = input.slice(NUM_BITS * 1, NUM_BITS * 2).lo; limbs[2] = input.slice(NUM_BITS * 2, NUM_BITS * 3).lo; limbs[3] = input.slice(NUM_BITS * 3, NUM_BITS * 4).lo; - limbs[4] = fr(input.lo); return limbs; }; - const auto get_limb_witness_indices = [&](const std::array& limbs) { - std::array limb_indices; + const auto get_limb_witness_indices = [&](const std::array& limbs) { + std::array limb_indices; limb_indices[0] = builder.add_variable(limbs[0]); limb_indices[1] = builder.add_variable(limbs[1]); limb_indices[2] = builder.add_variable(limbs[2]); limb_indices[3] = builder.add_variable(limbs[3]); - limb_indices[4] = builder.add_variable(limbs[4]); return limb_indices; }; const uint512_t BINARY_BASIS_MODULUS = uint512_t(1) << (68 * 4); diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/mega_arithmetization.hpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/mega_arithmetization.hpp index b4703c9179f..67a77dfa715 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/mega_arithmetization.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/mega_arithmetization.hpp @@ -74,14 +74,14 @@ template class MegaArith { { this->ecc_op = 1 << 10; this->pub_inputs = 1 << 7; - this->busread = 1 << 7; - this->arithmetic = 201000; + this->arithmetic = 198000; this->delta_range = 90000; this->elliptic = 9000; - this->aux = 137000; - this->poseidon2_external = 2500; - this->poseidon2_internal = 11500; + this->aux = 136000; this->lookup = 72000; + this->busread = 1 << 7; + this->poseidon2_external = 2500; + this->poseidon2_internal = 14000; } }; @@ -96,8 +96,8 @@ template class MegaArith { this->delta_range = 25000; this->elliptic = 80000; this->aux = 100000; - this->poseidon2_external = 30000; - this->poseidon2_internal = 150000; + this->poseidon2_external = 30128; + this->poseidon2_internal = 172000; this->lookup = 200000; } }; @@ -228,6 +228,7 @@ template class MegaArith { { for (auto block : this->get()) { if (block.size() > block.get_fixed_size()) { + info("WARNING: Num gates in circuit block exceeds the specified fixed size - execution trace will " "not be constructed correctly!"); summarize(); @@ -247,4 +248,4 @@ using MegaArithmetization = MegaArith; template concept HasAdditionalSelectors = IsAnyOf>; -} // namespace bb \ No newline at end of file +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp index 342286270cb..b0a8a27a6e8 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp @@ -134,6 +134,15 @@ std::shared_ptr ProtogalaxyVerifier combination = linear_combination(to_combine, lagranges); } + // generate recursive folding challenges to ensure manifest matches with recursive verifier + // (in recursive verifier we use random challenges to convert Flavor::NUM_FOLDED_ENTITIES muls + // into one large multiscalar multiplication) + std::array args; + for (size_t idx = 0; idx < Flavor::NUM_FOLDED_ENTITIES; ++idx) { + args[idx] = "accumulator_combination_challenges" + std::to_string(idx); + } + transcript->template get_challenges(args); + return next_accumulator; } diff --git a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_extra_relations.cpp b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_extra_relations.cpp index 471fa8b7765..b0055bf1107 100644 --- a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_extra_relations.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_extra_relations.cpp @@ -1,9 +1,12 @@ +#include "barretenberg/relations/translator_vm/translator_extra_relations.hpp" #include "barretenberg/relations/translator_vm/translator_extra_relations_impl.hpp" #include "barretenberg/translator_vm/translator_flavor.hpp" namespace bb { template class TranslatorOpcodeConstraintRelationImpl; template class TranslatorAccumulatorTransferRelationImpl; +template class TranslatorZeroConstraintsRelationImpl; DEFINE_ZK_SUMCHECK_RELATION_CLASS(TranslatorOpcodeConstraintRelationImpl, TranslatorFlavor); DEFINE_ZK_SUMCHECK_RELATION_CLASS(TranslatorAccumulatorTransferRelationImpl, TranslatorFlavor); +DEFINE_ZK_SUMCHECK_RELATION_CLASS(TranslatorZeroConstraintsRelationImpl, TranslatorFlavor); } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_extra_relations.hpp b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_extra_relations.hpp index c30ed383a4c..d114a4cabd8 100644 --- a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_extra_relations.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_extra_relations.hpp @@ -122,9 +122,185 @@ polynomials, const FF& scaling_factor); }; +template class TranslatorZeroConstraintsRelationImpl { + public: + using FF = FF_; + + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 3; // degree((some lagrange)(A)) = 2 + + static constexpr size_t ZK_RELATION_LENGTH = 5; + static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ + 3, // p_x_low_limbs_range_constraint_0 is zero outside of the minicircuit + 3, // p_x_low_limbs_range_constraint_1 is zero outside of the minicircuit + 3, // p_x_low_limbs_range_constraint_2 is zero outside of the minicircuit + 3, // p_x_low_limbs_range_constraint_3 is zero outside of the minicircuit + 3, // p_x_low_limbs_range_constraint_4 is zero outside of the minicircuit + 3, // p_x_high_limbs_range_constraint_0 is zero outside of the minicircuit + 3, // p_x_high_limbs_range_constraint_1 is zero outside of the minicircuit + 3, // p_x_high_limbs_range_constraint_2 is zero outside of the minicircuit + 3, // p_x_high_limbs_range_constraint_3 is zero outside of the minicircuit + 3, // p_x_high_limbs_range_constraint_4 is zero outside of the minicircuit + 3, // p_y_low_limbs_range_constraint_0 is zero outside of the minicircuit + 3, // p_y_low_limbs_range_constraint_1 is zero outside of the minicircuit + 3, // p_y_low_limbs_range_constraint_2 is zero outside of the minicircuit + 3, // p_y_low_limbs_range_constraint_3 is zero outside of the minicircuit + 3, // p_y_low_limbs_range_constraint_4 is zero outside of the minicircuit + 3, // p_y_high_limbs_range_constraint_0 is zero outside of the minicircuit + 3, // p_y_high_limbs_range_constraint_1 is zero outside of the minicircuit + 3, // p_y_high_limbs_range_constraint_2 is zero outside of the minicircuit + 3, // p_y_high_limbs_range_constraint_3 is zero outside of the minicircuit + 3, // p_y_high_limbs_range_constraint_4 is zero outside of the minicircuit + 3, // z_low_limbs_range_constraint_0 is zero outside of the minicircuit + 3, // z_low_limbs_range_constraint_1 is zero outside of the minicircuit + 3, // z_low_limbs_range_constraint_2 is zero outside of the minicircuit + 3, // z_low_limbs_range_constraint_3 is zero outside of the minicircuit + 3, // z_low_limbs_range_constraint_4 is zero outside of the minicircuit + 3, // z_high_limbs_range_constraint_0 is zero outside of the minicircuit + 3, // z_high_limbs_range_constraint_1 is zero outside of the minicircuit + 3, // z_high_limbs_range_constraint_2 is zero outside of the minicircuit + 3, // z_high_limbs_range_constraint_3 is zero outside of the minicircuit + 3, // z_high_limbs_range_constraint_4 is zero outside of the minicircuit + 3, // accumulator_low_limbs_range_constraint_0 is zero outside of the minicircuit + 3, // accumulator_low_limbs_range_constraint_1 is zero outside of the minicircuit + 3, // accumulator_low_limbs_range_constraint_2 is zero outside of the minicircuit + 3, // accumulator_low_limbs_range_constraint_3 is zero outside of the minicircuit + 3, // accumulator_low_limbs_range_constraint_4 is zero outside of the minicircuit + 3, // accumulator_high_limbs_range_constraint_0 is zero outside of the minicircuit + 3, // accumulator_high_limbs_range_constraint_1 is zero outside of the minicircuit + 3, // accumulator_high_limbs_range_constraint_2 is zero outside of the minicircuit + 3, // accumulator_high_limbs_range_constraint_3 is zero outside of the minicircuit + 3, // accumulator_high_limbs_range_constraint_4 is zero outside of the minicircuit + 3, // quotient_low_limbs_range_constraint_0 is zero outside of the minicircuit + 3, // quotient_low_limbs_range_constraint_1 is zero outside of the minicircuit + 3, // quotient_low_limbs_range_constraint_2 is zero outside of the minicircuit + 3, // quotient_low_limbs_range_constraint_3 is zero outside of the minicircuit + 3, // quotient_low_limbs_range_constraint_4 is zero outside of the minicircuit + 3, // quotient_high_limbs_range_constraint_0 is zero outside of the minicircuit + 3, // quotient_high_limbs_range_constraint_1 is zero outside of the minicircuit + 3, // quotient_high_limbs_range_constraint_2 is zero outside of the minicircuit + 3, // quotient_high_limbs_range_constraint_3 is zero outside of the minicircuit + 3, // quotient_high_limbs_range_constraint_4 is zero outside of the minicircuit + 3, // relation_wide_limbs_range_constraint_0 is zero outside of the minicircuit + 3, // relation_wide_limbs_range_constraint_1 is zero outside of the minicircuit + 3, // relation_wide_limbs_range_constraint_2 is zero outside of the minicircuit + 3, // relation_wide_limbs_range_constraint_3 is zero outside of the minicircuit + 3, // p_x_low_limbs_range_constraint_tail is zero outside of the minicircuit + 3, // p_x_high_limbs_range_constraint_tail is zero outside of the minicircuit + 3, // p_y_low_limbs_range_constraint_tail is zero outside of the minicircuit + 3, // p_y_high_limbs_range_constraint_tail is zero outside of the minicircuit + 3, // z_low_limbs_range_constraint_tail is zero outside of the minicircuit + 3, // z_high_limbs_range_constraint_tail is zero outside of the minicircuit + 3, // accumulator_low_limbs_range_constraint_tail is zero outside of the minicircuit + 3, // accumulator_high_limbs_range_constraint_tail is zero outside of the minicircuit + 3, // quotient_low_limbs_range_constraint_tail is zero outside of the minicircuit + 3, // quotient_high_limbs_range_constraint_tail is zero outside of the minicircuit + + }; + /** + * @brief For ZK-Flavors: Upper bound on the degrees of subrelations considered as polynomials only in witness +polynomials, + * i.e. all selectors and public polynomials are treated as constants. The subrelation witness degree does not + * exceed the subrelation partial degree given by SUBRELATION_PARTIAL_LENGTH - 1. + */ + static constexpr std::array SUBRELATION_WITNESS_DEGREES{ + 2, // p_x_low_limbs_range_constraint_0 is zero outside of the minicircuit + 2, // p_x_low_limbs_range_constraint_1 is zero outside of the minicircuit + 2, // p_x_low_limbs_range_constraint_2 is zero outside of the minicircuit + 2, // p_x_low_limbs_range_constraint_3 is zero outside of the minicircuit + 2, // p_x_low_limbs_range_constraint_4 is zero outside of the minicircuit + 2, // p_x_high_limbs_range_constraint_0 is zero outside of the minicircuit + 2, // p_x_high_limbs_range_constraint_1 is zero outside of the minicircuit + 2, // p_x_high_limbs_range_constraint_2 is zero outside of the minicircuit + 2, // p_x_high_limbs_range_constraint_3 is zero outside of the minicircuit + 2, // p_x_high_limbs_range_constraint_4 is zero outside of the minicircuit + 2, // p_y_low_limbs_range_constraint_0 is zero outside of the minicircuit + 2, // p_y_low_limbs_range_constraint_1 is zero outside of the minicircuit + 2, // p_y_low_limbs_range_constraint_2 is zero outside of the minicircuit + 2, // p_y_low_limbs_range_constraint_3 is zero outside of the minicircuit + 2, // p_y_low_limbs_range_constraint_4 is zero outside of the minicircuit + 2, // p_y_high_limbs_range_constraint_0 is zero outside of the minicircuit + 2, // p_y_high_limbs_range_constraint_1 is zero outside of the minicircuit + 2, // p_y_high_limbs_range_constraint_2 is zero outside of the minicircuit + 2, // p_y_high_limbs_range_constraint_3 is zero outside of the minicircuit + 2, // p_y_high_limbs_range_constraint_4 is zero outside of the minicircuit + 2, // z_low_limbs_range_constraint_0 is zero outside of the minicircuit + 2, // z_low_limbs_range_constraint_1 is zero outside of the minicircuit + 2, // z_low_limbs_range_constraint_2 is zero outside of the minicircuit + 2, // z_low_limbs_range_constraint_3 is zero outside of the minicircuit + 2, // z_low_limbs_range_constraint_4 is zero outside of the minicircuit + 2, // z_high_limbs_range_constraint_0 is zero outside of the minicircuit + 2, // z_high_limbs_range_constraint_1 is zero outside of the minicircuit + 2, // z_high_limbs_range_constraint_2 is zero outside of the minicircuit + 2, // z_high_limbs_range_constraint_3 is zero outside of the minicircuit + 2, // z_high_limbs_range_constraint_4 is zero outside of the minicircuit + 2, // accumulator_low_limbs_range_constraint_0 is zero outside of the minicircuit + 2, // accumulator_low_limbs_range_constraint_1 is zero outside of the minicircuit + 2, // accumulator_low_limbs_range_constraint_2 is zero outside of the minicircuit + 2, // accumulator_low_limbs_range_constraint_3 is zero outside of the minicircuit + 2, // accumulator_low_limbs_range_constraint_4 is zero outside of the minicircuit + 2, // accumulator_high_limbs_range_constraint_0 is zero outside of the minicircuit + 2, // accumulator_high_limbs_range_constraint_1 is zero outside of the minicircuit + 2, // accumulator_high_limbs_range_constraint_2 is zero outside of the minicircuit + 2, // accumulator_high_limbs_range_constraint_3 is zero outside of the minicircuit + 2, // accumulator_high_limbs_range_constraint_4 is zero outside of the minicircuit + 2, // quotient_low_limbs_range_constraint_0 is zero outside of the minicircuit + 2, // quotient_low_limbs_range_constraint_1 is zero outside of the minicircuit + 2, // quotient_low_limbs_range_constraint_2 is zero outside of the minicircuit + 2, // quotient_low_limbs_range_constraint_3 is zero outside of the minicircuit + 2, // quotient_low_limbs_range_constraint_4 is zero outside of the minicircuit + 2, // quotient_high_limbs_range_constraint_0 is zero outside of the minicircuit + 2, // quotient_high_limbs_range_constraint_1 is zero outside of the minicircuit + 2, // quotient_high_limbs_range_constraint_2 is zero outside of the minicircuit + 2, // quotient_high_limbs_range_constraint_3 is zero outside of the minicircuit + 2, // quotient_high_limbs_range_constraint_4 is zero outside of the minicircuit + 2, // relation_wide_limbs_range_constraint_0 is zero outside of the minicircuit + 2, // relation_wide_limbs_range_constraint_1 is zero outside of the minicircuit + 2, // relation_wide_limbs_range_constraint_2 is zero outside of the minicircuit + 2, // relation_wide_limbs_range_constraint_3 is zero outside of the minicircuit + 2, // p_x_low_limbs_range_constraint_tail is zero outside of the minicircuit + 2, // p_x_high_limbs_range_constraint_tail is zero outside of the minicircuit + 2, // p_y_low_limbs_range_constraint_tail is zero outside of the minicircuit + 2, // p_y_high_limbs_range_constraint_tail is zero outside of the minicircuit + 2, // z_low_limbs_range_constraint_tail is zero outside of the minicircuit + 2, // z_high_limbs_range_constraint_tail is zero outside of the minicircuit + 2, // accumulator_low_limbs_range_constraint_tail is zero outside of the minicircuit + 2, // accumulator_high_limbs_range_constraint_tail is zero outside of the minicircuit + 2, // quotient_low_limbs_range_constraint_tail is zero outside of the minicircuit + 2, // quotient_high_limbs_range_constraint_tail is zero outside of the minicircuit + + }; + /** + * @brief Might return true if the contribution from all subrelations for the provided inputs is identically zero + * + * + */ + template inline static bool skip(const AllEntities& in) + { + static constexpr auto minus_one = -FF(1); + return (in.lagrange_even_in_minicircuit + in.lagrange_second_to_last_in_minicircuit + minus_one).is_zero(); + } + /** + * @brief Relation enforcing all the range-constraint polynomials to be zero after the minicircuit + * @details This relation ensures that while we are out of the minicircuit the range constraint polynomials are zero + * + * @param evals transformed to `evals + C(in(X)...)*scaling_factor` + * @param in an std::array containing the fully extended Univariate edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ + template + static void accumulate(ContainerOverSubrelations& accumulators, + const AllEntities& in, + const Parameters& params, + const FF& scaling_factor); +}; + template using TranslatorOpcodeConstraintRelation = Relation>; template using TranslatorAccumulatorTransferRelation = Relation>; +template using TranslatorZeroConstraintsRelation = Relation>; + } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_extra_relations_impl.hpp b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_extra_relations_impl.hpp index 63d6522238e..cddcb3af380 100644 --- a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_extra_relations_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_extra_relations_impl.hpp @@ -145,4 +145,291 @@ void TranslatorAccumulatorTransferRelationImpl::accumulate(ContainerOverSubr tmp_12 *= scaling_factor; std::get<11>(accumulators) += tmp_12; }; + +/** + * @brief Relation enforcing all the range-constraint polynomials to be zero after the minicircuit + * @details This relation ensures that while we are out of the minicircuit the range constraint polynomials are zero + * + * @param evals transformed to `evals + C(in(X)...)*scaling_factor` + * @param in an std::array containing the fully extended Univariate edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ +template +template +void TranslatorZeroConstraintsRelationImpl::accumulate(ContainerOverSubrelations& accumulators, + const AllEntities& in, + const Parameters&, + const FF& scaling_factor) +{ + using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; + using View = typename Accumulator::View; + + // Minus one + static auto minus_one = -FF(1); + + auto lagrange_even_in_minicircuit = View(in.lagrange_even_in_minicircuit); + auto lagrange_odd_in_minicircuit = View(in.lagrange_odd_in_minicircuit); + + auto p_x_low_limbs_range_constraint_0 = View(in.p_x_low_limbs_range_constraint_0); + auto p_x_low_limbs_range_constraint_1 = View(in.p_x_low_limbs_range_constraint_1); + auto p_x_low_limbs_range_constraint_2 = View(in.p_x_low_limbs_range_constraint_2); + auto p_x_low_limbs_range_constraint_3 = View(in.p_x_low_limbs_range_constraint_3); + auto p_x_low_limbs_range_constraint_4 = View(in.p_x_low_limbs_range_constraint_4); + auto p_x_high_limbs_range_constraint_0 = View(in.p_x_high_limbs_range_constraint_0); + auto p_x_high_limbs_range_constraint_1 = View(in.p_x_high_limbs_range_constraint_1); + auto p_x_high_limbs_range_constraint_2 = View(in.p_x_high_limbs_range_constraint_2); + auto p_x_high_limbs_range_constraint_3 = View(in.p_x_high_limbs_range_constraint_3); + auto p_x_high_limbs_range_constraint_4 = View(in.p_x_high_limbs_range_constraint_4); + auto p_y_low_limbs_range_constraint_0 = View(in.p_y_low_limbs_range_constraint_0); + auto p_y_low_limbs_range_constraint_1 = View(in.p_y_low_limbs_range_constraint_1); + auto p_y_low_limbs_range_constraint_2 = View(in.p_y_low_limbs_range_constraint_2); + auto p_y_low_limbs_range_constraint_3 = View(in.p_y_low_limbs_range_constraint_3); + auto p_y_low_limbs_range_constraint_4 = View(in.p_y_low_limbs_range_constraint_4); + auto p_y_high_limbs_range_constraint_0 = View(in.p_y_high_limbs_range_constraint_0); + auto p_y_high_limbs_range_constraint_1 = View(in.p_y_high_limbs_range_constraint_1); + auto p_y_high_limbs_range_constraint_2 = View(in.p_y_high_limbs_range_constraint_2); + auto p_y_high_limbs_range_constraint_3 = View(in.p_y_high_limbs_range_constraint_3); + auto p_y_high_limbs_range_constraint_4 = View(in.p_y_high_limbs_range_constraint_4); + auto z_low_limbs_range_constraint_0 = View(in.z_low_limbs_range_constraint_0); + auto z_low_limbs_range_constraint_1 = View(in.z_low_limbs_range_constraint_1); + auto z_low_limbs_range_constraint_2 = View(in.z_low_limbs_range_constraint_2); + auto z_low_limbs_range_constraint_3 = View(in.z_low_limbs_range_constraint_3); + auto z_low_limbs_range_constraint_4 = View(in.z_low_limbs_range_constraint_4); + auto z_high_limbs_range_constraint_0 = View(in.z_high_limbs_range_constraint_0); + auto z_high_limbs_range_constraint_1 = View(in.z_high_limbs_range_constraint_1); + auto z_high_limbs_range_constraint_2 = View(in.z_high_limbs_range_constraint_2); + auto z_high_limbs_range_constraint_3 = View(in.z_high_limbs_range_constraint_3); + auto z_high_limbs_range_constraint_4 = View(in.z_high_limbs_range_constraint_4); + auto accumulator_low_limbs_range_constraint_0 = View(in.accumulator_low_limbs_range_constraint_0); + auto accumulator_low_limbs_range_constraint_1 = View(in.accumulator_low_limbs_range_constraint_1); + auto accumulator_low_limbs_range_constraint_2 = View(in.accumulator_low_limbs_range_constraint_2); + auto accumulator_low_limbs_range_constraint_3 = View(in.accumulator_low_limbs_range_constraint_3); + auto accumulator_low_limbs_range_constraint_4 = View(in.accumulator_low_limbs_range_constraint_4); + auto accumulator_high_limbs_range_constraint_0 = View(in.accumulator_high_limbs_range_constraint_0); + auto accumulator_high_limbs_range_constraint_1 = View(in.accumulator_high_limbs_range_constraint_1); + auto accumulator_high_limbs_range_constraint_2 = View(in.accumulator_high_limbs_range_constraint_2); + auto accumulator_high_limbs_range_constraint_3 = View(in.accumulator_high_limbs_range_constraint_3); + auto accumulator_high_limbs_range_constraint_4 = View(in.accumulator_high_limbs_range_constraint_4); + auto quotient_low_limbs_range_constraint_0 = View(in.quotient_low_limbs_range_constraint_0); + auto quotient_low_limbs_range_constraint_1 = View(in.quotient_low_limbs_range_constraint_1); + auto quotient_low_limbs_range_constraint_2 = View(in.quotient_low_limbs_range_constraint_2); + auto quotient_low_limbs_range_constraint_3 = View(in.quotient_low_limbs_range_constraint_3); + auto quotient_low_limbs_range_constraint_4 = View(in.quotient_low_limbs_range_constraint_4); + auto quotient_high_limbs_range_constraint_0 = View(in.quotient_high_limbs_range_constraint_0); + auto quotient_high_limbs_range_constraint_1 = View(in.quotient_high_limbs_range_constraint_1); + auto quotient_high_limbs_range_constraint_2 = View(in.quotient_high_limbs_range_constraint_2); + auto quotient_high_limbs_range_constraint_3 = View(in.quotient_high_limbs_range_constraint_3); + auto quotient_high_limbs_range_constraint_4 = View(in.quotient_high_limbs_range_constraint_4); + auto relation_wide_limbs_range_constraint_0 = View(in.relation_wide_limbs_range_constraint_0); + auto relation_wide_limbs_range_constraint_1 = View(in.relation_wide_limbs_range_constraint_1); + auto relation_wide_limbs_range_constraint_2 = View(in.relation_wide_limbs_range_constraint_2); + auto relation_wide_limbs_range_constraint_3 = View(in.relation_wide_limbs_range_constraint_3); + auto p_x_low_limbs_range_constraint_tail = View(in.p_x_low_limbs_range_constraint_tail); + auto p_x_high_limbs_range_constraint_tail = View(in.p_x_high_limbs_range_constraint_tail); + auto p_y_low_limbs_range_constraint_tail = View(in.p_y_low_limbs_range_constraint_tail); + auto p_y_high_limbs_range_constraint_tail = View(in.p_y_high_limbs_range_constraint_tail); + auto z_low_limbs_range_constraint_tail = View(in.z_low_limbs_range_constraint_tail); + auto z_high_limbs_range_constraint_tail = View(in.z_high_limbs_range_constraint_tail); + auto accumulator_low_limbs_range_constraint_tail = View(in.accumulator_low_limbs_range_constraint_tail); + auto accumulator_high_limbs_range_constraint_tail = View(in.accumulator_high_limbs_range_constraint_tail); + auto quotient_low_limbs_range_constraint_tail = View(in.quotient_low_limbs_range_constraint_tail); + auto quotient_high_limbs_range_constraint_tail = View(in.quotient_high_limbs_range_constraint_tail); + + // 0 in the minicircuit, -1 outside + auto not_in_minicircuit_by_scaling = + (lagrange_odd_in_minicircuit + lagrange_even_in_minicircuit + minus_one) * scaling_factor; + + // Contribution 0, ensure p_x_low_limbs_range_constraint_0 is 0 outside of minicircuit + std::get<0>(accumulators) += p_x_low_limbs_range_constraint_0 * not_in_minicircuit_by_scaling; + + // Contribution 1, ensure p_x_low_limbs_range_constraint_1 is 0 outside of minicircuit + std::get<1>(accumulators) += p_x_low_limbs_range_constraint_1 * not_in_minicircuit_by_scaling; + + // Contribution 2, ensure p_x_low_limbs_range_constraint_2 is 0 outside of minicircuit + std::get<2>(accumulators) += p_x_low_limbs_range_constraint_2 * not_in_minicircuit_by_scaling; + + // Contribution 3, ensure p_x_low_limbs_range_constraint_3 is 0 outside of minicircuit + std::get<3>(accumulators) += p_x_low_limbs_range_constraint_3 * not_in_minicircuit_by_scaling; + + // Contribution 4, ensure p_x_low_limbs_range_constraint_4 is 0 outside of minicircuit + std::get<4>(accumulators) += p_x_low_limbs_range_constraint_4 * not_in_minicircuit_by_scaling; + + // Contribution 5, ensure p_x_high_limbs_range_constraint_0 is 0 outside of minicircuit + std::get<5>(accumulators) += p_x_high_limbs_range_constraint_0 * not_in_minicircuit_by_scaling; + + // Contribution 6, ensure p_x_high_limbs_range_constraint_1 is 0 outside of minicircuit + std::get<6>(accumulators) += p_x_high_limbs_range_constraint_1 * not_in_minicircuit_by_scaling; + + // Contribution 7, ensure p_x_high_limbs_range_constraint_2 is 0 outside of minicircuit + std::get<7>(accumulators) += p_x_high_limbs_range_constraint_2 * not_in_minicircuit_by_scaling; + + // Contribution 8, ensure p_x_high_limbs_range_constraint_3 is 0 outside of minicircuit + std::get<8>(accumulators) += p_x_high_limbs_range_constraint_3 * not_in_minicircuit_by_scaling; + + // Contribution 9, ensure p_x_high_limbs_range_constraint_4 is 0 outside of minicircuit + std::get<9>(accumulators) += p_x_high_limbs_range_constraint_4 * not_in_minicircuit_by_scaling; + + // Contribution 10, ensure p_y_low_limbs_range_constraint_0 is 0 outside of minicircuit + std::get<10>(accumulators) += p_y_low_limbs_range_constraint_0 * not_in_minicircuit_by_scaling; + + // Contribution 11, ensure p_y_low_limbs_range_constraint_1 is 0 outside of minicircuit + std::get<11>(accumulators) += p_y_low_limbs_range_constraint_1 * not_in_minicircuit_by_scaling; + + // Contribution 12, ensure p_y_low_limbs_range_constraint_2 is 0 outside of minicircuit + std::get<12>(accumulators) += p_y_low_limbs_range_constraint_2 * not_in_minicircuit_by_scaling; + + // Contribution 13, ensure p_y_low_limbs_range_constraint_3 is 0 outside of minicircuit + std::get<13>(accumulators) += p_y_low_limbs_range_constraint_3 * not_in_minicircuit_by_scaling; + + // Contribution 14, ensure p_y_low_limbs_range_constraint_4 is 0 outside of minicircuit + std::get<14>(accumulators) += p_y_low_limbs_range_constraint_4 * not_in_minicircuit_by_scaling; + + // Contribution 15, ensure p_y_high_limbs_range_constraint_0 is 0 outside of minicircuit + std::get<15>(accumulators) += p_y_high_limbs_range_constraint_0 * not_in_minicircuit_by_scaling; + + // Contribution 16, ensure p_y_high_limbs_range_constraint_1 is 0 outside of minicircuit + std::get<16>(accumulators) += p_y_high_limbs_range_constraint_1 * not_in_minicircuit_by_scaling; + + // Contribution 17, ensure p_y_high_limbs_range_constraint_2 is 0 outside of minicircuit + std::get<17>(accumulators) += p_y_high_limbs_range_constraint_2 * not_in_minicircuit_by_scaling; + + // Contribution 18, ensure p_y_high_limbs_range_constraint_3 is 0 outside of minicircuit + std::get<18>(accumulators) += p_y_high_limbs_range_constraint_3 * not_in_minicircuit_by_scaling; + + // Contribution 19, ensure p_y_high_limbs_range_constraint_4 is 0 outside of minicircuit + std::get<19>(accumulators) += p_y_high_limbs_range_constraint_4 * not_in_minicircuit_by_scaling; + + // Contribution 20, ensure z_low_limbs_range_constraint_0 is 0 outside of minicircuit + std::get<20>(accumulators) += z_low_limbs_range_constraint_0 * not_in_minicircuit_by_scaling; + + // Contribution 21, ensure z_low_limbs_range_constraint_1 is 0 outside of minicircuit + std::get<21>(accumulators) += z_low_limbs_range_constraint_1 * not_in_minicircuit_by_scaling; + + // Contribution 22, ensure z_low_limbs_range_constraint_2 is 0 outside of minicircuit + std::get<22>(accumulators) += z_low_limbs_range_constraint_2 * not_in_minicircuit_by_scaling; + + // Contribution 23, ensure z_low_limbs_range_constraint_3 is 0 outside of minicircuit + std::get<23>(accumulators) += z_low_limbs_range_constraint_3 * not_in_minicircuit_by_scaling; + + // Contribution 24, ensure z_low_limbs_range_constraint_4 is 0 outside of minicircuit + std::get<24>(accumulators) += z_low_limbs_range_constraint_4 * not_in_minicircuit_by_scaling; + + // Contribution 25, ensure z_high_limbs_range_constraint_0 is 0 outside of minicircuit + std::get<25>(accumulators) += z_high_limbs_range_constraint_0 * not_in_minicircuit_by_scaling; + + // Contribution 26, ensure z_high_limbs_range_constraint_1 is 0 outside of minicircuit + std::get<26>(accumulators) += z_high_limbs_range_constraint_1 * not_in_minicircuit_by_scaling; + + // Contribution 27, ensure z_high_limbs_range_constraint_2 is 0 outside of minicircuit + std::get<27>(accumulators) += z_high_limbs_range_constraint_2 * not_in_minicircuit_by_scaling; + + // Contribution 28, ensure z_high_limbs_range_constraint_3 is 0 outside of minicircuit + std::get<28>(accumulators) += z_high_limbs_range_constraint_3 * not_in_minicircuit_by_scaling; + + // Contribution 29, ensure z_high_limbs_range_constraint_4 is 0 outside of minicircuit + std::get<29>(accumulators) += z_high_limbs_range_constraint_4 * not_in_minicircuit_by_scaling; + + // Contribution 30, ensure accumulator_low_limbs_range_constraint_0 is 0 outside of minicircuit + std::get<30>(accumulators) += accumulator_low_limbs_range_constraint_0 * not_in_minicircuit_by_scaling; + + // Contribution 31, ensure accumulator_low_limbs_range_constraint_1 is 0 outside of minicircuit + std::get<31>(accumulators) += accumulator_low_limbs_range_constraint_1 * not_in_minicircuit_by_scaling; + + // Contribution 32, ensure accumulator_low_limbs_range_constraint_2 is 0 outside of minicircuit + std::get<32>(accumulators) += accumulator_low_limbs_range_constraint_2 * not_in_minicircuit_by_scaling; + + // Contribution 33, ensure accumulator_low_limbs_range_constraint_3 is 0 outside of minicircuit + std::get<33>(accumulators) += accumulator_low_limbs_range_constraint_3 * not_in_minicircuit_by_scaling; + + // Contribution 34, ensure accumulator_low_limbs_range_constraint_4 is 0 outside of minicircuit + std::get<34>(accumulators) += accumulator_low_limbs_range_constraint_4 * not_in_minicircuit_by_scaling; + + // Contribution 35, ensure accumulator_high_limbs_range_constraint_0 is 0 outside of minicircuit + std::get<35>(accumulators) += accumulator_high_limbs_range_constraint_0 * not_in_minicircuit_by_scaling; + + // Contribution 36, ensure accumulator_high_limbs_range_constraint_1 is 0 outside of minicircuit + std::get<36>(accumulators) += accumulator_high_limbs_range_constraint_1 * not_in_minicircuit_by_scaling; + + // Contribution 37, ensure accumulator_high_limbs_range_constraint_2 is 0 outside of minicircuit + std::get<37>(accumulators) += accumulator_high_limbs_range_constraint_2 * not_in_minicircuit_by_scaling; + + // Contribution 38, ensure accumulator_high_limbs_range_constraint_3 is 0 outside of minicircuit + std::get<38>(accumulators) += accumulator_high_limbs_range_constraint_3 * not_in_minicircuit_by_scaling; + + // Contribution 39, ensure accumulator_high_limbs_range_constraint_4 is 0 outside of minicircuit + std::get<39>(accumulators) += accumulator_high_limbs_range_constraint_4 * not_in_minicircuit_by_scaling; + + // Contribution 40, ensure quotient_low_limbs_range_constraint_0 is 0 outside of minicircuit + std::get<40>(accumulators) += quotient_low_limbs_range_constraint_0 * not_in_minicircuit_by_scaling; + + // Contribution 41, ensure quotient_low_limbs_range_constraint_1 is 0 outside of minicircuit + std::get<41>(accumulators) += quotient_low_limbs_range_constraint_1 * not_in_minicircuit_by_scaling; + + // Contribution 42, ensure quotient_low_limbs_range_constraint_2 is 0 outside of minicircuit + std::get<42>(accumulators) += quotient_low_limbs_range_constraint_2 * not_in_minicircuit_by_scaling; + + // Contribution 43, ensure quotient_low_limbs_range_constraint_3 is 0 outside of minicircuit + std::get<43>(accumulators) += quotient_low_limbs_range_constraint_3 * not_in_minicircuit_by_scaling; + + // Contribution 44, ensure quotient_low_limbs_range_constraint_4 is 0 outside of minicircuit + std::get<44>(accumulators) += quotient_low_limbs_range_constraint_4 * not_in_minicircuit_by_scaling; + + // Contribution 45, ensure quotient_high_limbs_range_constraint_0 is 0 outside of minicircuit + std::get<45>(accumulators) += quotient_high_limbs_range_constraint_0 * not_in_minicircuit_by_scaling; + + // Contribution 46, ensure quotient_high_limbs_range_constraint_1 is 0 outside of minicircuit + std::get<46>(accumulators) += quotient_high_limbs_range_constraint_1 * not_in_minicircuit_by_scaling; + + // Contribution 47, ensure quotient_high_limbs_range_constraint_2 is 0 outside of minicircuit + std::get<47>(accumulators) += quotient_high_limbs_range_constraint_2 * not_in_minicircuit_by_scaling; + + // Contribution 48, ensure quotient_high_limbs_range_constraint_3 is 0 outside of minicircuit + std::get<48>(accumulators) += quotient_high_limbs_range_constraint_3 * not_in_minicircuit_by_scaling; + + // Contribution 49, ensure quotient_high_limbs_range_constraint_4 is 0 outside of minicircuit + std::get<49>(accumulators) += quotient_high_limbs_range_constraint_4 * not_in_minicircuit_by_scaling; + + // Contribution 50, ensure relation_wide_limbs_range_constraint_0 is 0 outside of minicircuit + std::get<50>(accumulators) += relation_wide_limbs_range_constraint_0 * not_in_minicircuit_by_scaling; + + // Contribution 51, ensure relation_wide_limbs_range_constraint_1 is 0 outside of minicircuit + std::get<51>(accumulators) += relation_wide_limbs_range_constraint_1 * not_in_minicircuit_by_scaling; + + // Contribution 52, ensure relation_wide_limbs_range_constraint_2 is 0 outside of minicircuit + std::get<52>(accumulators) += relation_wide_limbs_range_constraint_2 * not_in_minicircuit_by_scaling; + + // Contribution 53, ensure relation_wide_limbs_range_constraint_3 is 0 outside of minicircuit + std::get<53>(accumulators) += relation_wide_limbs_range_constraint_3 * not_in_minicircuit_by_scaling; + + // Contribution 54, ensure p_x_low_limbs_range_constraint_tail is 0 outside of minicircuit + std::get<54>(accumulators) += p_x_low_limbs_range_constraint_tail * not_in_minicircuit_by_scaling; + + // Contribution 55, ensure p_x_high_limbs_range_constraint_tail is 0 outside of minicircuit + std::get<55>(accumulators) += p_x_high_limbs_range_constraint_tail * not_in_minicircuit_by_scaling; + + // Contribution 56, ensure p_y_low_limbs_range_constraint_tail is 0 outside of minicircuit + std::get<56>(accumulators) += p_y_low_limbs_range_constraint_tail * not_in_minicircuit_by_scaling; + + // Contribution 57, ensure p_y_high_limbs_range_constraint_tail is 0 outside of minicircuit + std::get<57>(accumulators) += p_y_high_limbs_range_constraint_tail * not_in_minicircuit_by_scaling; + + // Contribution 58, ensure z_low_limbs_range_constraint_tail is 0 outside of minicircuit + std::get<58>(accumulators) += z_low_limbs_range_constraint_tail * not_in_minicircuit_by_scaling; + + // Contribution 59, ensure z_high_limbs_range_constraint_tail is 0 outside of minicircuit + std::get<59>(accumulators) += z_high_limbs_range_constraint_tail * not_in_minicircuit_by_scaling; + + // Contribution 60, ensure accumulator_low_limbs_range_constraint_tail is 0 outside of minicircuit + std::get<60>(accumulators) += accumulator_low_limbs_range_constraint_tail * not_in_minicircuit_by_scaling; + + // Contribution 61, ensure accumulator_high_limbs_range_constraint_tail is 0 outside of minicircuit + std::get<61>(accumulators) += accumulator_high_limbs_range_constraint_tail * not_in_minicircuit_by_scaling; + + // Contribution 62, ensure quotient_low_limbs_range_constraint_tail is 0 outside of minicircuit + std::get<62>(accumulators) += quotient_low_limbs_range_constraint_tail * not_in_minicircuit_by_scaling; + + // Contribution 63, ensure quotient_high_limbs_range_constraint_tail is 0 outside of minicircuit + std::get<63>(accumulators) += quotient_high_limbs_range_constraint_tail * not_in_minicircuit_by_scaling; +}; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_relation_consistency.test.cpp b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_relation_consistency.test.cpp index 4a7b8cdcde9..ec94e7cfc07 100644 --- a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_relation_consistency.test.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_relation_consistency.test.cpp @@ -11,6 +11,7 @@ * satisfied in general by random inputs) only that the two implementations are equivalent. * */ +#include "barretenberg/relations/translator_vm/translator_extra_relations.hpp" #include "barretenberg/translator_vm/translator_flavor.hpp" #include @@ -792,6 +793,230 @@ TEST_F(TranslatorRelationConsistency, AccumulatorTransferRelation) run_test(/*random_inputs=*/true); }; +TEST_F(TranslatorRelationConsistency, ZeroConstraintsRelation) +{ + const auto run_test = [](bool random_inputs) { + using Relation = TranslatorZeroConstraintsRelation; + using RelationValues = typename Relation::SumcheckArrayOfValuesOverSubrelations; + + const InputElements input_elements = random_inputs ? get_random_input() : get_special_input(); + + // Get all the wires + const auto& p_x_low_limbs_range_constraint_0 = input_elements.p_x_low_limbs_range_constraint_0; + const auto& p_x_low_limbs_range_constraint_1 = input_elements.p_x_low_limbs_range_constraint_1; + const auto& p_x_low_limbs_range_constraint_2 = input_elements.p_x_low_limbs_range_constraint_2; + const auto& p_x_low_limbs_range_constraint_3 = input_elements.p_x_low_limbs_range_constraint_3; + const auto& p_x_low_limbs_range_constraint_4 = input_elements.p_x_low_limbs_range_constraint_4; + const auto& p_x_low_limbs_range_constraint_tail = input_elements.p_x_low_limbs_range_constraint_tail; + const auto& p_x_high_limbs_range_constraint_0 = input_elements.p_x_high_limbs_range_constraint_0; + const auto& p_x_high_limbs_range_constraint_1 = input_elements.p_x_high_limbs_range_constraint_1; + const auto& p_x_high_limbs_range_constraint_2 = input_elements.p_x_high_limbs_range_constraint_2; + const auto& p_x_high_limbs_range_constraint_3 = input_elements.p_x_high_limbs_range_constraint_3; + const auto& p_x_high_limbs_range_constraint_4 = input_elements.p_x_high_limbs_range_constraint_4; + const auto& p_x_high_limbs_range_constraint_tail = input_elements.p_x_high_limbs_range_constraint_tail; + const auto& p_y_low_limbs_range_constraint_0 = input_elements.p_y_low_limbs_range_constraint_0; + const auto& p_y_low_limbs_range_constraint_1 = input_elements.p_y_low_limbs_range_constraint_1; + const auto& p_y_low_limbs_range_constraint_2 = input_elements.p_y_low_limbs_range_constraint_2; + const auto& p_y_low_limbs_range_constraint_3 = input_elements.p_y_low_limbs_range_constraint_3; + const auto& p_y_low_limbs_range_constraint_4 = input_elements.p_y_low_limbs_range_constraint_4; + const auto& p_y_low_limbs_range_constraint_tail = input_elements.p_y_low_limbs_range_constraint_tail; + const auto& p_y_high_limbs_range_constraint_0 = input_elements.p_y_high_limbs_range_constraint_0; + const auto& p_y_high_limbs_range_constraint_1 = input_elements.p_y_high_limbs_range_constraint_1; + const auto& p_y_high_limbs_range_constraint_2 = input_elements.p_y_high_limbs_range_constraint_2; + const auto& p_y_high_limbs_range_constraint_3 = input_elements.p_y_high_limbs_range_constraint_3; + const auto& p_y_high_limbs_range_constraint_4 = input_elements.p_y_high_limbs_range_constraint_4; + const auto& p_y_high_limbs_range_constraint_tail = input_elements.p_y_high_limbs_range_constraint_tail; + const auto& z_low_limbs_range_constraint_0 = input_elements.z_low_limbs_range_constraint_0; + const auto& z_low_limbs_range_constraint_1 = input_elements.z_low_limbs_range_constraint_1; + const auto& z_low_limbs_range_constraint_2 = input_elements.z_low_limbs_range_constraint_2; + const auto& z_low_limbs_range_constraint_3 = input_elements.z_low_limbs_range_constraint_3; + const auto& z_low_limbs_range_constraint_4 = input_elements.z_low_limbs_range_constraint_4; + const auto& z_low_limbs_range_constraint_tail = input_elements.z_low_limbs_range_constraint_tail; + const auto& z_high_limbs_range_constraint_0 = input_elements.z_high_limbs_range_constraint_0; + const auto& z_high_limbs_range_constraint_1 = input_elements.z_high_limbs_range_constraint_1; + const auto& z_high_limbs_range_constraint_2 = input_elements.z_high_limbs_range_constraint_2; + const auto& z_high_limbs_range_constraint_3 = input_elements.z_high_limbs_range_constraint_3; + const auto& z_high_limbs_range_constraint_4 = input_elements.z_high_limbs_range_constraint_4; + const auto& z_high_limbs_range_constraint_tail = input_elements.z_high_limbs_range_constraint_tail; + const auto& accumulator_low_limbs_range_constraint_0 = input_elements.accumulator_low_limbs_range_constraint_0; + const auto& accumulator_low_limbs_range_constraint_1 = input_elements.accumulator_low_limbs_range_constraint_1; + const auto& accumulator_low_limbs_range_constraint_2 = input_elements.accumulator_low_limbs_range_constraint_2; + const auto& accumulator_low_limbs_range_constraint_3 = input_elements.accumulator_low_limbs_range_constraint_3; + const auto& accumulator_low_limbs_range_constraint_4 = input_elements.accumulator_low_limbs_range_constraint_4; + const auto& accumulator_low_limbs_range_constraint_tail = + input_elements.accumulator_low_limbs_range_constraint_tail; + const auto& accumulator_high_limbs_range_constraint_0 = + input_elements.accumulator_high_limbs_range_constraint_0; + const auto& accumulator_high_limbs_range_constraint_1 = + input_elements.accumulator_high_limbs_range_constraint_1; + const auto& accumulator_high_limbs_range_constraint_2 = + input_elements.accumulator_high_limbs_range_constraint_2; + const auto& accumulator_high_limbs_range_constraint_3 = + input_elements.accumulator_high_limbs_range_constraint_3; + const auto& accumulator_high_limbs_range_constraint_4 = + input_elements.accumulator_high_limbs_range_constraint_4; + const auto& accumulator_high_limbs_range_constraint_tail = + input_elements.accumulator_high_limbs_range_constraint_tail; + const auto& quotient_low_limbs_range_constraint_0 = input_elements.quotient_low_limbs_range_constraint_0; + const auto& quotient_low_limbs_range_constraint_1 = input_elements.quotient_low_limbs_range_constraint_1; + const auto& quotient_low_limbs_range_constraint_2 = input_elements.quotient_low_limbs_range_constraint_2; + const auto& quotient_low_limbs_range_constraint_3 = input_elements.quotient_low_limbs_range_constraint_3; + const auto& quotient_low_limbs_range_constraint_4 = input_elements.quotient_low_limbs_range_constraint_4; + const auto& quotient_low_limbs_range_constraint_tail = input_elements.quotient_low_limbs_range_constraint_tail; + const auto& quotient_high_limbs_range_constraint_0 = input_elements.quotient_high_limbs_range_constraint_0; + const auto& quotient_high_limbs_range_constraint_1 = input_elements.quotient_high_limbs_range_constraint_1; + const auto& quotient_high_limbs_range_constraint_2 = input_elements.quotient_high_limbs_range_constraint_2; + const auto& quotient_high_limbs_range_constraint_3 = input_elements.quotient_high_limbs_range_constraint_3; + const auto& quotient_high_limbs_range_constraint_4 = input_elements.quotient_high_limbs_range_constraint_4; + const auto& quotient_high_limbs_range_constraint_tail = + input_elements.quotient_high_limbs_range_constraint_tail; + const auto& relation_wide_limbs_range_constraint_0 = input_elements.relation_wide_limbs_range_constraint_0; + const auto& relation_wide_limbs_range_constraint_1 = input_elements.relation_wide_limbs_range_constraint_1; + const auto& relation_wide_limbs_range_constraint_2 = input_elements.relation_wide_limbs_range_constraint_2; + const auto& relation_wide_limbs_range_constraint_3 = input_elements.relation_wide_limbs_range_constraint_3; + + const auto& lagrange_odd_in_minicircuit = input_elements.lagrange_odd_in_minicircuit; + const auto& lagrange_even_in_minicircuit = input_elements.lagrange_even_in_minicircuit; + + RelationValues expected_values; + + const auto parameters = RelationParameters::get_random(); + + expected_values[0] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_x_low_limbs_range_constraint_0; + expected_values[1] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_x_low_limbs_range_constraint_1; + expected_values[2] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_x_low_limbs_range_constraint_2; + expected_values[3] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_x_low_limbs_range_constraint_3; + expected_values[4] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_x_low_limbs_range_constraint_4; + expected_values[5] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_x_high_limbs_range_constraint_0; + expected_values[6] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_x_high_limbs_range_constraint_1; + expected_values[7] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_x_high_limbs_range_constraint_2; + expected_values[8] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_x_high_limbs_range_constraint_3; + expected_values[9] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_x_high_limbs_range_constraint_4; + expected_values[10] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_y_low_limbs_range_constraint_0; + expected_values[11] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_y_low_limbs_range_constraint_1; + expected_values[12] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_y_low_limbs_range_constraint_2; + expected_values[13] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_y_low_limbs_range_constraint_3; + expected_values[14] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_y_low_limbs_range_constraint_4; + expected_values[15] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_y_high_limbs_range_constraint_0; + expected_values[16] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_y_high_limbs_range_constraint_1; + expected_values[17] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_y_high_limbs_range_constraint_2; + expected_values[18] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_y_high_limbs_range_constraint_3; + expected_values[19] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_y_high_limbs_range_constraint_4; + expected_values[20] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * z_low_limbs_range_constraint_0; + expected_values[21] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * z_low_limbs_range_constraint_1; + expected_values[22] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * z_low_limbs_range_constraint_2; + expected_values[23] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * z_low_limbs_range_constraint_3; + expected_values[24] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * z_low_limbs_range_constraint_4; + expected_values[25] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * z_high_limbs_range_constraint_0; + expected_values[26] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * z_high_limbs_range_constraint_1; + expected_values[27] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * z_high_limbs_range_constraint_2; + expected_values[28] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * z_high_limbs_range_constraint_3; + expected_values[29] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * z_high_limbs_range_constraint_4; + expected_values[30] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * accumulator_low_limbs_range_constraint_0; + expected_values[31] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * accumulator_low_limbs_range_constraint_1; + expected_values[32] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * accumulator_low_limbs_range_constraint_2; + expected_values[33] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * accumulator_low_limbs_range_constraint_3; + expected_values[34] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * accumulator_low_limbs_range_constraint_4; + expected_values[35] = (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * + accumulator_high_limbs_range_constraint_0; + expected_values[36] = (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * + accumulator_high_limbs_range_constraint_1; + expected_values[37] = (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * + accumulator_high_limbs_range_constraint_2; + expected_values[38] = (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * + accumulator_high_limbs_range_constraint_3; + expected_values[39] = (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * + accumulator_high_limbs_range_constraint_4; + expected_values[40] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * quotient_low_limbs_range_constraint_0; + expected_values[41] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * quotient_low_limbs_range_constraint_1; + expected_values[42] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * quotient_low_limbs_range_constraint_2; + expected_values[43] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * quotient_low_limbs_range_constraint_3; + expected_values[44] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * quotient_low_limbs_range_constraint_4; + expected_values[45] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * quotient_high_limbs_range_constraint_0; + expected_values[46] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * quotient_high_limbs_range_constraint_1; + expected_values[47] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * quotient_high_limbs_range_constraint_2; + expected_values[48] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * quotient_high_limbs_range_constraint_3; + expected_values[49] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * quotient_high_limbs_range_constraint_4; + expected_values[50] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * relation_wide_limbs_range_constraint_0; + expected_values[51] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * relation_wide_limbs_range_constraint_1; + expected_values[52] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * relation_wide_limbs_range_constraint_2; + expected_values[53] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * relation_wide_limbs_range_constraint_3; + expected_values[54] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_x_low_limbs_range_constraint_tail; + expected_values[55] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_x_high_limbs_range_constraint_tail; + expected_values[56] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_y_low_limbs_range_constraint_tail; + expected_values[57] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * p_y_high_limbs_range_constraint_tail; + expected_values[58] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * z_low_limbs_range_constraint_tail; + expected_values[59] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * z_high_limbs_range_constraint_tail; + expected_values[60] = (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * + accumulator_low_limbs_range_constraint_tail; + expected_values[61] = (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * + accumulator_high_limbs_range_constraint_tail; + expected_values[62] = + (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * quotient_low_limbs_range_constraint_tail; + expected_values[63] = (lagrange_even_in_minicircuit + lagrange_odd_in_minicircuit - 1) * + quotient_high_limbs_range_constraint_tail; + + validate_relation_execution(expected_values, input_elements, parameters); + }; + run_test(/*random_inputs=*/false); + run_test(/*random_inputs=*/true); +}; + TEST_F(TranslatorRelationConsistency, NonNativeFieldRelation) { const auto run_test = [](bool random_inputs) { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp index 02c69d8f770..aefc1a2316b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp @@ -82,8 +82,9 @@ bool_t ecdsa_verify_signature(const stdlib::byte_array& messag // Read more about this at: https://www.derpturkey.com/inherent-malleability-of-ecdsa-signatures/amp/ s.assert_less_than((Fr::modulus + 1) / 2); - Fr u1 = z / s; - Fr u2 = r / s; + // We already checked that s is nonzero + Fr u1 = z.div_without_denominator_check(s); + Fr u2 = r.div_without_denominator_check(s); public_key.validate_on_curve(); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256_plookup.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256_plookup.cpp index 3fa8f939c25..c64afbeb10d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256_plookup.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256_plookup.cpp @@ -279,11 +279,11 @@ std::array, 8> sha256_block(const std::array, sparse_value a = sparse_value(h_init[0]); auto b = map_into_maj_sparse_form(h_init[1]); auto c = map_into_maj_sparse_form(h_init[2]); - auto d = map_into_maj_sparse_form(h_init[3]); + sparse_value d = sparse_value(h_init[3]); sparse_value e = sparse_value(h_init[4]); auto f = map_into_choose_sparse_form(h_init[5]); auto g = map_into_choose_sparse_form(h_init[6]); - auto h = map_into_choose_sparse_form(h_init[7]); + sparse_value h = sparse_value(h_init[7]); /** * Extend witness diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp index bf1ac000ac9..aa2c057b8b9 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp @@ -70,8 +70,8 @@ UltraRecursiveVerifier_::AggregationObject UltraRecursiveVerifier_public_inputs[key->recursive_proof_public_input_indices[idx]]; idx++; } - base_field_vals[j] = - typename Curve::BaseField(bigfield_limbs[0], bigfield_limbs[1], bigfield_limbs[2], bigfield_limbs[3]); + base_field_vals[j] = Curve::BaseField::construct_from_limbs( + bigfield_limbs[0], bigfield_limbs[1], bigfield_limbs[2], bigfield_limbs[3]); } nested_pairing_points[i] = typename Curve::Group(base_field_vals[0], base_field_vals[1]); } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp index 52bbdd10dab..ced1a04cdb7 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp @@ -109,11 +109,11 @@ aggregation_state convert_witness_indices_to_agg_obj(Builder& builder, { std::array aggregation_elements; for (size_t i = 0; i < 4; ++i) { - aggregation_elements[i] = - typename Curve::BaseField(Curve::ScalarField::from_witness_index(&builder, witness_indices[4 * i]), - Curve::ScalarField::from_witness_index(&builder, witness_indices[4 * i + 1]), - Curve::ScalarField::from_witness_index(&builder, witness_indices[4 * i + 2]), - Curve::ScalarField::from_witness_index(&builder, witness_indices[4 * i + 3])); + aggregation_elements[i] = Curve::BaseField::construct_from_limbs( + Curve::ScalarField::from_witness_index(&builder, witness_indices[4 * i]), + Curve::ScalarField::from_witness_index(&builder, witness_indices[4 * i + 1]), + Curve::ScalarField::from_witness_index(&builder, witness_indices[4 * i + 2]), + Curve::ScalarField::from_witness_index(&builder, witness_indices[4 * i + 3])); aggregation_elements[i].assert_is_in_field(); } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/verifier/verifier.hpp b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/verifier/verifier.hpp index df0bffaf55e..b1fa8239519 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/verifier/verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/verifier/verifier.hpp @@ -345,7 +345,7 @@ aggregation_state verify_proof_(typename Curve::Builder* context, l1.create_range_constraint(fq_ct::NUM_LIMB_BITS, "l1"); l2.create_range_constraint(fq_ct::NUM_LIMB_BITS, "l2"); l3.create_range_constraint(fq_ct::NUM_LAST_LIMB_BITS, "l3"); - return fq_ct(l0, l1, l2, l3, false); + return fq_ct::unsafe_construct_from_limbs(l0, l1, l2, l3, false); }; fr_ct recursion_separator_challenge = transcript.get_challenge_field_element("separator", 2); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp index 0eb5d90a7fe..dd3011149d8 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp @@ -25,15 +25,14 @@ template class bigfield { struct Limb { Limb() {} - Limb(const field_t& input, const uint256_t max = uint256_t(0)) + Limb(const field_t& input, const uint256_t& max = DEFAULT_MAXIMUM_LIMB) : element(input) { - if (input.witness_index == IS_CONSTANT) { - maximum_value = uint256_t(input.additive_constant) + 1; - } else if (max != uint256_t(0)) { - maximum_value = max; + if (input.is_constant()) { + maximum_value = uint256_t(input.additive_constant); + ASSERT(maximum_value <= max); } else { - maximum_value = DEFAULT_MAXIMUM_LIMB; + maximum_value = max; } } friend std::ostream& operator<<(std::ostream& os, const Limb& a) @@ -95,40 +94,104 @@ template class bigfield { : bigfield(nullptr, uint256_t(value)) {} - // we assume the limbs have already been normalized! - bigfield(const field_t& a, - const field_t& b, - const field_t& c, - const field_t& d, - const bool can_overflow = false) + /** + * @brief Construct a bigfield element from binary limbs that are already reduced + * + * @details This API should only be used by bigfield and other stdlib members for efficiency and with extreme care. + * We need it in cases where we precompute and reduce the elements, for example, and then put them in a table + * + */ + static bigfield unsafe_construct_from_limbs(const field_t& a, + const field_t& b, + const field_t& c, + const field_t& d, + const bool can_overflow = false) { - context = a.context; - binary_basis_limbs[0] = Limb(field_t(a)); - binary_basis_limbs[1] = Limb(field_t(b)); - binary_basis_limbs[2] = Limb(field_t(c)); - binary_basis_limbs[3] = + ASSERT(a.is_constant() == b.is_constant() && b.is_constant() == c.is_constant() && + c.is_constant() == d.is_constant()); + bigfield result; + result.context = a.context; + result.binary_basis_limbs[0] = Limb(field_t(a)); + result.binary_basis_limbs[1] = Limb(field_t(b)); + result.binary_basis_limbs[2] = Limb(field_t(c)); + result.binary_basis_limbs[3] = Limb(field_t(d), can_overflow ? DEFAULT_MAXIMUM_LIMB : DEFAULT_MAXIMUM_MOST_SIGNIFICANT_LIMB); - prime_basis_limb = - (binary_basis_limbs[3].element * shift_3) - .add_two(binary_basis_limbs[2].element * shift_2, binary_basis_limbs[1].element * shift_1); - prime_basis_limb += (binary_basis_limbs[0].element); + result.prime_basis_limb = (result.binary_basis_limbs[3].element * shift_3) + .add_two(result.binary_basis_limbs[2].element * shift_2, + result.binary_basis_limbs[1].element * shift_1); + result.prime_basis_limb += (result.binary_basis_limbs[0].element); + return result; }; - // we assume the limbs have already been normalized! - bigfield(const field_t& a, - const field_t& b, - const field_t& c, - const field_t& d, - const field_t& prime_limb, - const bool can_overflow = false) + /** + * @brief Construct a bigfield element from binary limbs that are already reduced and ensure they are range + * constrained + * + */ + static bigfield construct_from_limbs(const field_t& a, + const field_t& b, + const field_t& c, + const field_t& d, + const bool can_overflow = false) { - context = a.context; - binary_basis_limbs[0] = Limb(field_t(a)); - binary_basis_limbs[1] = Limb(field_t(b)); - binary_basis_limbs[2] = Limb(field_t(c)); - binary_basis_limbs[3] = + ASSERT(a.is_constant() == b.is_constant() && b.is_constant() == c.is_constant() && + c.is_constant() == d.is_constant()); + bigfield result; + auto ctx = a.context; + result.context = a.context; + result.binary_basis_limbs[0] = Limb(field_t(a)); + result.binary_basis_limbs[1] = Limb(field_t(b)); + result.binary_basis_limbs[2] = Limb(field_t(c)); + result.binary_basis_limbs[3] = Limb(field_t(d), can_overflow ? DEFAULT_MAXIMUM_LIMB : DEFAULT_MAXIMUM_MOST_SIGNIFICANT_LIMB); - prime_basis_limb = prime_limb; + result.prime_basis_limb = (result.binary_basis_limbs[3].element * shift_3) + .add_two(result.binary_basis_limbs[2].element * shift_2, + result.binary_basis_limbs[1].element * shift_1); + result.prime_basis_limb += (result.binary_basis_limbs[0].element); + const size_t num_last_limb_bits = (can_overflow) ? NUM_LIMB_BITS : NUM_LAST_LIMB_BITS; + if constexpr (HasPlookup) { + ctx->range_constrain_two_limbs(result.binary_basis_limbs[0].element.get_normalized_witness_index(), + result.binary_basis_limbs[1].element.get_normalized_witness_index(), + static_cast(NUM_LIMB_BITS), + static_cast(NUM_LIMB_BITS)); + ctx->range_constrain_two_limbs(result.binary_basis_limbs[2].element.get_normalized_witness_index(), + result.binary_basis_limbs[3].element.get_normalized_witness_index(), + static_cast(NUM_LIMB_BITS), + static_cast(num_last_limb_bits)); + + } else { + a.create_range_constraint(NUM_LIMB_BITS); + b.create_range_constraint(NUM_LIMB_BITS); + c.create_range_constraint(NUM_LIMB_BITS); + d.create_range_constraint(num_last_limb_bits); + } + return result; + }; + /** + * @brief Construct a bigfield element from binary limbs and a prime basis limb that are already reduced + * + * @details This API should only be used by bigfield and other stdlib members for efficiency and with extreme care. + * We need it in cases where we precompute and reduce the elements, for example, and then put them in a table + * + */ + static bigfield unsafe_construct_from_limbs(const field_t& a, + const field_t& b, + const field_t& c, + const field_t& d, + const field_t& prime_limb, + const bool can_overflow = false) + { + ASSERT(a.is_constant() == b.is_constant() && b.is_constant() == c.is_constant() && + c.is_constant() == d.is_constant() && d.is_constant() == prime_limb.is_constant()); + bigfield result; + result.context = a.context; + result.binary_basis_limbs[0] = Limb(field_t(a)); + result.binary_basis_limbs[1] = Limb(field_t(b)); + result.binary_basis_limbs[2] = Limb(field_t(c)); + result.binary_basis_limbs[3] = + Limb(field_t(d), can_overflow ? DEFAULT_MAXIMUM_LIMB : DEFAULT_MAXIMUM_MOST_SIGNIFICANT_LIMB); + result.prime_basis_limb = prime_limb; + return result; }; bigfield(const byte_array& bytes); @@ -155,6 +218,9 @@ template class bigfield { static constexpr uint512_t modulus_u512 = uint512_t(modulus); static constexpr uint64_t NUM_LIMB_BITS = NUM_LIMB_BITS_IN_FIELD_SIMULATION; static constexpr uint64_t NUM_LAST_LIMB_BITS = modulus_u512.get_msb() + 1 - (NUM_LIMB_BITS * 3); + // The quotient reduction checks currently only support >=250 bit moduli and moduli >256 have never been tested + // (Check zkSecurity audit report issue #12 for explanation) + static_assert(modulus_u512.get_msb() + 1 >= 250 && modulus_u512.get_msb() + 1 <= 256); static constexpr uint1024_t DEFAULT_MAXIMUM_REMAINDER = (uint1024_t(1) << (NUM_LIMB_BITS * 3 + NUM_LAST_LIMB_BITS)) - uint1024_t(1); static constexpr uint256_t DEFAULT_MAXIMUM_LIMB = (uint256_t(1) << NUM_LIMB_BITS) - uint256_t(1); @@ -163,6 +229,10 @@ template class bigfield { static constexpr uint64_t LOG2_BINARY_MODULUS = NUM_LIMB_BITS * NUM_LIMBS; static constexpr bool is_composite = true; // false only when fr is native + // This limits the size of all vectors that are being used to 16 (we don't really need more) + static constexpr size_t MAXIMUM_SUMMAND_COUNT_LOG = 4; + static constexpr size_t MAXIMUM_SUMMAND_COUNT = 1 << MAXIMUM_SUMMAND_COUNT_LOG; + static constexpr uint256_t prime_basis_maximum_limb = uint256_t(modulus_u512.slice(NUM_LIMB_BITS * (NUM_LIMBS - 1), NUM_LIMB_BITS* NUM_LIMBS)); static constexpr Basis prime_basis{ uint512_t(bb::fr::modulus), bb::fr::modulus.get_msb() + 1 }; @@ -191,6 +261,8 @@ template class bigfield { byte_array to_byte_array() const { byte_array result(get_context()); + // Prevents aliases + assert_is_in_field(); field_t lo = binary_basis_limbs[0].element + (binary_basis_limbs[1].element * shift_1); field_t hi = binary_basis_limbs[2].element + (binary_basis_limbs[3].element * shift_1); // n.b. this only works if NUM_LIMB_BITS * 2 is divisible by 8 @@ -285,6 +357,7 @@ template class bigfield { bool check_for_zero); static bigfield div_without_denominator_check(const std::vector& numerators, const bigfield& denominator); + bigfield div_without_denominator_check(const bigfield& denominator); static bigfield div_check_denominator_nonzero(const std::vector& numerators, const bigfield& denominator); bigfield conditional_negate(const bool_t& predicate) const; @@ -392,14 +465,25 @@ template class bigfield { prime_basis_limb.tag); } - static constexpr uint512_t get_maximum_unreduced_value(const size_t num_products = 1) + static constexpr uint512_t get_maximum_unreduced_value() { - // return (uint512_t(1) << 256); - uint1024_t maximum_product = uint1024_t(binary_basis.modulus) * uint1024_t(prime_basis.modulus) / - uint1024_t(static_cast(num_products)); + // This = `T * n = 2^272 * |BN(Fr)|` So this equals n*2^t + + uint1024_t maximum_product = uint1024_t(binary_basis.modulus) * uint1024_t(prime_basis.modulus); // TODO: compute square root (the following is a lower bound, so good for the CRT use) + // We use -1 to stay safer, because it provides additional space to avoid the overflow, but get_msb() by itself + // should be enough + uint64_t maximum_product_bits = maximum_product.get_msb() - 1; + return (uint512_t(1) << (maximum_product_bits >> 1)); + } + + // If we encounter this maximum value of a bigfield we stop execution + static constexpr uint512_t get_prohibited_maximum_value() + { + uint1024_t maximum_product = uint1024_t(binary_basis.modulus) * uint1024_t(prime_basis.modulus); uint64_t maximum_product_bits = maximum_product.get_msb() - 1; - return (uint512_t(1) << (maximum_product_bits >> 1)) - uint512_t(1); + const size_t arbitrary_secure_margin = 20; + return (uint512_t(1) << ((maximum_product_bits >> 1) + arbitrary_secure_margin)) - uint512_t(1); } static constexpr uint1024_t get_maximum_crt_product() @@ -501,11 +585,24 @@ template class bigfield { static constexpr uint64_t MAX_ADDITION_LOG = 10; // the rationale of the expression is we should not overflow Fr when applying any bigfield operation (e.g. *) and // starting with this max limb size - static constexpr uint64_t MAX_UNREDUCED_LIMB_SIZE = (bb::fr::modulus.get_msb() + 1) / 2 - MAX_ADDITION_LOG; - static constexpr uint256_t get_maximum_unreduced_limb_value() { return uint256_t(1) << MAX_UNREDUCED_LIMB_SIZE; } + static constexpr uint64_t MAXIMUM_SIZE_THAT_WOULDNT_OVERFLOW = + (bb::fr::modulus.get_msb() - MAX_ADDITION_LOG - NUM_LIMB_BITS) / 2; + + // If the logarithm of the maximum value of a limb is more than this, we need to reduce + static constexpr uint64_t MAX_UNREDUCED_LIMB_BITS = + NUM_LIMB_BITS + 10; // We allowa an element to be added to itself 10 times. There is no actual usecase - static_assert(MAX_UNREDUCED_LIMB_SIZE < (NUM_LIMB_BITS * 2)); + static constexpr uint64_t PROHIBITED_LIMB_BITS = + MAX_UNREDUCED_LIMB_BITS + + 5; // Shouldn't be reachable through addition, reduction should happen earlier. If we detect this, we stop + + static constexpr uint256_t get_maximum_unreduced_limb_value() { return uint256_t(1) << MAX_UNREDUCED_LIMB_BITS; } + + // If we encounter this maximum value of a limb we stop execution + static constexpr uint256_t get_prohibited_maximum_limb_value() { return uint256_t(1) << PROHIBITED_LIMB_BITS; } + + static_assert(PROHIBITED_LIMB_BITS < MAXIMUM_SIZE_THAT_WOULDNT_OVERFLOW); private: static std::pair compute_quotient_remainder_values(const bigfield& a, @@ -560,7 +657,9 @@ template class bigfield { const bigfield& right, const bigfield& quotient, const bigfield& remainder); - void reduction_check(const size_t num_products = 1) const; + void reduction_check() const; + + void sanity_check() const; }; // namespace stdlib diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.test.cpp index b2194698bf8..81949bb40e9 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.test.cpp @@ -41,6 +41,35 @@ template class stdlib_bigfield : public testing::Test { typedef typename bn254::witness_ct witness_ct; public: + static void test_add_to_lower_limb_regression() + { + auto builder = Builder(); + fq_ct constant = fq_ct(1); + fq_ct var = fq_ct::create_from_u512_as_witness(&builder, 1); + fr_ct small_var = witness_ct(&builder, fr(1)); + fq_ct mixed = fq_ct(1).add_to_lower_limb(small_var, 1); + fq_ct r; + + r = mixed + mixed; + r = mixed - mixed; + r = mixed + var; + r = mixed + constant; + r = mixed - var; + r = mixed - constant; + r = var - mixed; + + r = var * constant; + r = constant / var; + r = constant * constant; + r = constant / constant; + + r = mixed * var; + r = mixed / var; + r = mixed * mixed; + r = mixed * constant; + bool result = CircuitChecker::check(builder); + EXPECT_EQ(result, true); + } // The bug happens when we are applying the CRT formula to a*b < r, which can happen when using the division // operator static void test_division_formula_bug() @@ -254,7 +283,7 @@ template class stdlib_bigfield : public testing::Test { { auto builder = Builder(); size_t num_repetitions = 1; - const size_t number_of_madds = 32; + const size_t number_of_madds = 16; for (size_t i = 0; i < num_repetitions; ++i) { fq mul_left_values[number_of_madds]; fq mul_right_values[number_of_madds]; @@ -973,10 +1002,10 @@ template class stdlib_bigfield : public testing::Test { witness_ct(&builder, fr(uint256_t(inputs[3]).slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4)))); - fq_ct two(witness_ct(&builder, fr(2)), - witness_ct(&builder, fr(0)), - witness_ct(&builder, fr(0)), - witness_ct(&builder, fr(0))); + fq_ct two = fq_ct::unsafe_construct_from_limbs(witness_ct(&builder, fr(2)), + witness_ct(&builder, fr(0)), + witness_ct(&builder, fr(0)), + witness_ct(&builder, fr(0))); fq_ct t0 = a + a; fq_ct t1 = a * two; @@ -998,31 +1027,219 @@ template class stdlib_bigfield : public testing::Test { fq base_val(engine.get_random_uint256()); uint32_t exponent_val = engine.get_random_uint32(); - fq expected = base_val.pow(exponent_val); + // Set the high bit + exponent_val |= static_cast(1) << 31; + fq_ct base_constant(&builder, static_cast(base_val)); + auto base_witness = fq_ct::from_witness(&builder, static_cast(base_val)); + // This also tests for the case where the exponent is zero + for (size_t i = 0; i <= 32; i += 4) { + auto current_exponent_val = exponent_val >> i; + fq expected = base_val.pow(current_exponent_val); + + // Check for constant bigfield element with constant exponent + fq_ct result_constant_base = base_constant.pow(current_exponent_val); + EXPECT_EQ(fq(result_constant_base.get_value()), expected); + + // Check for witness exponent with constant base + fr_ct witness_exponent = witness_ct(&builder, current_exponent_val); + auto result_witness_exponent = base_constant.pow(witness_exponent); + EXPECT_EQ(fq(result_witness_exponent.get_value()), expected); + + // Check for witness base with constant exponent + fq_ct result_witness_base = base_witness.pow(current_exponent_val); + EXPECT_EQ(fq(result_witness_base.get_value()), expected); + + // Check for all witness + base_witness.set_origin_tag(submitted_value_origin_tag); + witness_exponent.set_origin_tag(challenge_origin_tag); + + fq_ct result_all_witness = base_witness.pow(witness_exponent); + EXPECT_EQ(fq(result_all_witness.get_value()), expected); + EXPECT_EQ(result_all_witness.get_origin_tag(), first_two_merged_tag); + } + + bool check_result = CircuitChecker::check(builder); + EXPECT_EQ(check_result, true); + } + + static void test_pow_one() + { + Builder builder; + + fq base_val(engine.get_random_uint256()); + + uint32_t current_exponent_val = 1; + fq_ct base_constant(&builder, static_cast(base_val)); + auto base_witness = fq_ct::from_witness(&builder, static_cast(base_val)); + fq expected = base_val.pow(current_exponent_val); + + // Check for constant bigfield element with constant exponent + fq_ct result_constant_base = base_constant.pow(current_exponent_val); + EXPECT_EQ(fq(result_constant_base.get_value()), expected); - fq_ct base(&builder, static_cast(base_val)); - fq_ct result = base.pow(exponent_val); - EXPECT_EQ(fq(result.get_value()), expected); + // Check for witness exponent with constant base + fr_ct witness_exponent = witness_ct(&builder, current_exponent_val); + auto result_witness_exponent = base_constant.pow(witness_exponent); + EXPECT_EQ(fq(result_witness_exponent.get_value()), expected); - fr_ct exponent = witness_ct(&builder, exponent_val); - base.set_origin_tag(submitted_value_origin_tag); - exponent.set_origin_tag(challenge_origin_tag); - result = base.pow(exponent); - EXPECT_EQ(fq(result.get_value()), expected); + // Check for witness base with constant exponent + fq_ct result_witness_base = base_witness.pow(current_exponent_val); + EXPECT_EQ(fq(result_witness_base.get_value()), expected); - EXPECT_EQ(result.get_origin_tag(), first_two_merged_tag); + // Check for all witness + fq_ct result_all_witness = base_witness.pow(witness_exponent); + EXPECT_EQ(fq(result_all_witness.get_value()), expected); - info("num gates = ", builder.get_estimated_num_finalized_gates()); bool check_result = CircuitChecker::check(builder); EXPECT_EQ(check_result, true); } + + static void test_nonnormalized_field_bug_regression() + { + auto builder = Builder(); + fr_ct zero = witness_ct::create_constant_witness(&builder, fr::zero()); + uint256_t two_to_68 = uint256_t(1) << fq_ct::NUM_LIMB_BITS; + // construct bigfield where the low limb has a non-trivial `additive_constant` + fq_ct z(zero + two_to_68, zero); + // assert invariant for every limb: actual value <= maximum value + // Failed in the past for for StandardCircuitBuilder + for (auto zi : z.binary_basis_limbs) { + EXPECT_LE(uint256_t(zi.element.get_value()), zi.maximum_value); + } + } + + static void test_msub_div_ctx_crash_regression() + { + auto builder = Builder(); + fq_ct witness_one = fq_ct::create_from_u512_as_witness(&builder, uint256_t(1)); + fq_ct constant_one(1); + fq_ct::msub_div({ witness_one }, { witness_one }, constant_one, { witness_one }, true); + bool result = CircuitChecker::check(builder); + EXPECT_EQ(result, true); + } + + static void test_internal_div_regression() + { + typedef stdlib::bool_t bool_t; + auto builder = Builder(); + + fq_ct w0 = fq_ct::from_witness(&builder, 1); + w0 = w0.conditional_negate(bool_t(&builder, true)); + w0 = w0.conditional_negate(bool_t(&builder, false)); + w0 = w0.conditional_negate(bool_t(&builder, true)); + w0 = w0.conditional_negate(bool_t(&builder, true)); + fq_ct w4 = w0.conditional_negate(bool_t(&builder, false)); + w4 = w4.conditional_negate(bool_t(&builder, true)); + w4 = w4.conditional_negate(bool_t(&builder, true)); + fq_ct w5 = w4 - w0; + fq_ct w6 = w5 / 1; + (void)(w6); + EXPECT_TRUE(CircuitChecker::check(builder)); + } + + static void test_internal_div_regression2() + { + auto builder = Builder(); + + fq_ct numerator = fq_ct::create_from_u512_as_witness(&builder, uint256_t(1) << (68 + 67)); + numerator.binary_basis_limbs[0].maximum_value = 0; + numerator.binary_basis_limbs[1].maximum_value = uint256_t(1) << 67; + numerator.binary_basis_limbs[2].maximum_value = 0; + numerator.binary_basis_limbs[3].maximum_value = 0; + + for (size_t i = 0; i < 9; i++) { + numerator = numerator + numerator; + } + fq_ct denominator = fq_ct::create_from_u512_as_witness(&builder, uint256_t(1)); + fq_ct result = numerator / denominator; + (void)(result); + EXPECT_TRUE(CircuitChecker::check(builder)); + } + + static void test_internal_div_regression3() + { + Builder builder; + uint256_t dlimb0_value = uint256_t("0x00000000000000000000000000000000000000000000000bef7fa109038857fc"); + uint256_t dlimb0_max = uint256_t("0x00000000000000000000000000000000000000000000000fffffffffffffffff"); + uint256_t dlimb1_value = uint256_t("0x0000000000000000000000000000000000000000000000056f10535779f56339"); + uint256_t dlimb1_max = uint256_t("0x00000000000000000000000000000000000000000000000fffffffffffffffff"); + uint256_t dlimb2_value = uint256_t("0x00000000000000000000000000000000000000000000000c741f60a1ec4e114e"); + uint256_t dlimb2_max = uint256_t("0x00000000000000000000000000000000000000000000000fffffffffffffffff"); + uint256_t dlimb3_value = uint256_t("0x000000000000000000000000000000000000000000000000000286b3cd344d8b"); + uint256_t dlimb3_max = uint256_t("0x0000000000000000000000000000000000000000000000000003ffffffffffff"); + uint256_t dlimb_prime = uint256_t("0x286b3cd344d8bc741f60a1ec4e114e56f10535779f56339bef7fa109038857fc"); + + uint256_t nlimb0_value = uint256_t("0x00000000000000000000000000000000000000000000080a84d9bea2b012417c"); + uint256_t nlimb0_max = uint256_t("0x000000000000000000000000000000000000000000000ff7c7469df4081b61fc"); + uint256_t nlimb1_value = uint256_t("0x00000000000000000000000000000000000000000000080f50ee84526e8e5ba7"); + uint256_t nlimb1_max = uint256_t("0x000000000000000000000000000000000000000000000ffef965c67ba5d5893c"); + uint256_t nlimb2_value = uint256_t("0x00000000000000000000000000000000000000000000080aba136ca8eaf6dc1b"); + uint256_t nlimb2_max = uint256_t("0x000000000000000000000000000000000000000000000ff8171d22fd607249ea"); + uint256_t nlimb3_value = uint256_t("0x00000000000000000000000000000000000000000000000001f0042419843c29"); + uint256_t nlimb3_max = uint256_t("0x00000000000000000000000000000000000000000000000003e00636264659ff"); + uint256_t nlimb_prime = uint256_t("0x000000000000000000000000000000474da776b8ee19a56b08186bdcf01240d8"); + + fq_ct w0 = fq_ct::from_witness(&builder, bb::fq(0)); + w0.binary_basis_limbs[0].element = witness_ct(&builder, dlimb0_value); + w0.binary_basis_limbs[1].element = witness_ct(&builder, dlimb1_value); + w0.binary_basis_limbs[2].element = witness_ct(&builder, dlimb2_value); + w0.binary_basis_limbs[3].element = witness_ct(&builder, dlimb3_value); + w0.binary_basis_limbs[0].maximum_value = dlimb0_max; + w0.binary_basis_limbs[1].maximum_value = dlimb1_max; + w0.binary_basis_limbs[2].maximum_value = dlimb2_max; + w0.binary_basis_limbs[3].maximum_value = dlimb3_max; + w0.prime_basis_limb = witness_ct(&builder, dlimb_prime); + + fq_ct w1 = fq_ct::from_witness(&builder, bb::fq(0)); + w1.binary_basis_limbs[0].element = witness_ct(&builder, nlimb0_value); + w1.binary_basis_limbs[1].element = witness_ct(&builder, nlimb1_value); + w1.binary_basis_limbs[2].element = witness_ct(&builder, nlimb2_value); + w1.binary_basis_limbs[3].element = witness_ct(&builder, nlimb3_value); + w1.binary_basis_limbs[0].maximum_value = nlimb0_max; + w1.binary_basis_limbs[1].maximum_value = nlimb1_max; + w1.binary_basis_limbs[2].maximum_value = nlimb2_max; + w1.binary_basis_limbs[3].maximum_value = nlimb3_max; + w1.prime_basis_limb = witness_ct(&builder, nlimb_prime); + + fq_ct w2 = w1 / w0; + (void)w2; + EXPECT_TRUE(CircuitChecker::check(builder)); + } + static void test_assert_not_equal_regression() + { + auto builder = Builder(); + fq_ct zero = fq_ct::create_from_u512_as_witness(&builder, uint256_t(0)); + fq_ct alsozero = fq_ct::create_from_u512_as_witness(&builder, fq_ct::modulus_u512); + for (size_t i = 0; i < 4; i++) { + zero.binary_basis_limbs[i].maximum_value = zero.binary_basis_limbs[i].element.get_value(); + alsozero.binary_basis_limbs[i].maximum_value = alsozero.binary_basis_limbs[i].element.get_value(); + } + zero.assert_is_not_equal(alsozero); + bool result = CircuitChecker::check(builder); + EXPECT_EQ(result, false); + } }; // Define types for which the above tests will be constructed. using CircuitTypes = testing::Types; // Define the suite of tests. TYPED_TEST_SUITE(stdlib_bigfield, CircuitTypes); + +TYPED_TEST(stdlib_bigfield, assert_not_equal_regression) +{ + TestFixture::test_assert_not_equal_regression(); +} + +TYPED_TEST(stdlib_bigfield, add_to_lower_limb_regression) +{ + TestFixture::test_add_to_lower_limb_regression(); +} TYPED_TEST(stdlib_bigfield, badmul) +{ + TestFixture::test_bad_mul(); +} + +TYPED_TEST(stdlib_bigfield, division_formula_regression) { TestFixture::test_division_formula_bug(); } @@ -1070,6 +1287,10 @@ TYPED_TEST(stdlib_bigfield, msub_div) { TestFixture::test_msub_div(); } +TYPED_TEST(stdlib_bigfield, msb_div_ctx_crash_regression) +{ + TestFixture::test_msub_div_ctx_crash_regression(); +} TYPED_TEST(stdlib_bigfield, conditional_negate) { TestFixture::test_conditional_negate(); @@ -1124,6 +1345,22 @@ TYPED_TEST(stdlib_bigfield, pow) TestFixture::test_pow(); } +TYPED_TEST(stdlib_bigfield, pow_one) +{ + TestFixture::test_pow_one(); +} +TYPED_TEST(stdlib_bigfield, nonnormalized_field_bug_regression) +{ + TestFixture::test_nonnormalized_field_bug_regression(); +} + +TYPED_TEST(stdlib_bigfield, internal_div_bug_regression) +{ + TestFixture::test_internal_div_regression(); + TestFixture::test_internal_div_regression2(); + TestFixture::test_internal_div_regression3(); +} + // // This test was disabled before the refactor to use TYPED_TEST's/ // TEST(stdlib_bigfield, DISABLED_test_div_against_constants) // { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp index 3f4513773b9..bf522e86ff2 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp @@ -1,5 +1,6 @@ #pragma once +#include "barretenberg/common/assert.hpp" #include "barretenberg/common/zip_view.hpp" #include "barretenberg/numeric/uint256/uint256.hpp" #include "barretenberg/numeric/uintx/uintx.hpp" @@ -39,6 +40,7 @@ bigfield::bigfield(const field_t& low_bits_in, const bool can_overflow, const size_t maximum_bitlength) { + ASSERT(low_bits_in.is_constant() == high_bits_in.is_constant()); ASSERT((can_overflow == true && maximum_bitlength == 0) || (can_overflow == false && (maximum_bitlength == 0 || maximum_bitlength > (3 * NUM_LIMB_BITS)))); @@ -51,12 +53,12 @@ bigfield::bigfield(const field_t& low_bits_in, field_t limb_1(context); field_t limb_2(context); field_t limb_3(context); - if (low_bits_in.witness_index != IS_CONSTANT) { + if (!low_bits_in.is_constant()) { std::vector low_accumulator; if constexpr (HasPlookup) { // MERGE NOTE: this was the if constexpr block introduced in ecebe7643 const auto limb_witnesses = - context->decompose_non_native_field_double_width_limb(low_bits_in.normalize().witness_index); + context->decompose_non_native_field_double_width_limb(low_bits_in.get_normalized_witness_index()); limb_0.witness_index = limb_witnesses[0]; limb_1.witness_index = limb_witnesses[1]; field_t::evaluate_linear_identity(low_bits_in, -limb_0, -limb_1 * shift_1, field_t(0)); @@ -73,8 +75,9 @@ bigfield::bigfield(const field_t& low_bits_in, // limb_1 = (low_bits_in - limb_0) * shift_right_1; } else { size_t mid_index; - low_accumulator = context->decompose_into_base4_accumulators( - low_bits_in.witness_index, static_cast(NUM_LIMB_BITS * 2), "bigfield: low_bits_in too large."); + low_accumulator = context->decompose_into_base4_accumulators(low_bits_in.get_normalized_witness_index(), + static_cast(NUM_LIMB_BITS * 2), + "bigfield: low_bits_in too large."); mid_index = static_cast((NUM_LIMB_BITS / 2) - 1); // Range constraint returns an array of partial sums, midpoint will happen to hold the big limb // value @@ -103,23 +106,23 @@ bigfield::bigfield(const field_t& low_bits_in, } // We create the high limb values similar to the low limb ones above const uint64_t num_high_limb_bits = NUM_LIMB_BITS + num_last_limb_bits; - if (high_bits_in.witness_index != IS_CONSTANT) { + if (!high_bits_in.is_constant()) { std::vector high_accumulator; if constexpr (HasPlookup) { const auto limb_witnesses = context->decompose_non_native_field_double_width_limb( - high_bits_in.normalize().witness_index, (size_t)num_high_limb_bits); + high_bits_in.get_normalized_witness_index(), (size_t)num_high_limb_bits); limb_2.witness_index = limb_witnesses[0]; limb_3.witness_index = limb_witnesses[1]; field_t::evaluate_linear_identity(high_bits_in, -limb_2, -limb_3 * shift_1, field_t(0)); } else { - high_accumulator = context->decompose_into_base4_accumulators(high_bits_in.witness_index, + high_accumulator = context->decompose_into_base4_accumulators(high_bits_in.get_normalized_witness_index(), static_cast(num_high_limb_bits), "bigfield: high_bits_in too large."); if constexpr (!IsSimulator) { - limb_3.witness_index = high_accumulator[static_cast((num_last_limb_bits / 2) - 1)]; + limb_3.witness_index = high_accumulator[static_cast(((num_last_limb_bits + 1) / 2) - 1)]; } limb_2 = (high_bits_in - (limb_3 * shift_1)); } @@ -171,8 +174,8 @@ bigfield::bigfield(bigfield&& other) * @param ctx * @param value * @param can_overflow Can the input value have more than log2(modulus) bits? - * @param maximum_bitlength Provide the explicit maximum bitlength if known. Otherwise bigfield max value will be either - * log2(modulus) bits iff can_overflow = false, or (4 * NUM_LIMB_BITS) iff can_overflow = true + * @param maximum_bitlength Provide the explicit maximum bitlength if known. Otherwise bigfield max value will be + * either log2(modulus) bits iff can_overflow = false, or (4 * NUM_LIMB_BITS) iff can_overflow = true * @return bigfield * * @details This method is 1 gate more efficient than constructing from 2 field_ct elements. @@ -204,19 +207,20 @@ bigfield bigfield::create_from_u512_as_witness(Builder* prime_limb.witness_index = ctx->add_variable(limb_0.get_value() + limb_1.get_value() * shift_1 + limb_2.get_value() * shift_2 + limb_3.get_value() * shift_3); // evaluate prime basis limb with addition gate that taps into the 4th wire in the next gate - ctx->create_big_add_gate({ limb_1.witness_index, - limb_2.witness_index, - limb_3.witness_index, - prime_limb.witness_index, + ctx->create_big_add_gate({ limb_1.get_normalized_witness_index(), + limb_2.get_normalized_witness_index(), + limb_3.get_normalized_witness_index(), + prime_limb.get_normalized_witness_index(), shift_1, shift_2, shift_3, -1, 0 }, true); - // TODO(https://github.com/AztecProtocol/barretenberg/issues/879): dummy necessary for preceeding big add gate + // TODO(https://github.com/AztecProtocol/barretenberg/issues/879): dummy necessary for preceeding big add + // gate ctx->create_dummy_gate( - ctx->blocks.arithmetic, ctx->zero_idx, ctx->zero_idx, ctx->zero_idx, limb_0.witness_index); + ctx->blocks.arithmetic, ctx->zero_idx, ctx->zero_idx, ctx->zero_idx, limb_0.get_normalized_witness_index()); uint64_t num_last_limb_bits = (can_overflow) ? NUM_LIMB_BITS : NUM_LAST_LIMB_BITS; @@ -235,10 +239,14 @@ bigfield bigfield::create_from_u512_as_witness(Builder* result.binary_basis_limbs[3].maximum_value = max_limb_value; } result.prime_basis_limb = prime_limb; - ctx->range_constrain_two_limbs( - limb_0.witness_index, limb_1.witness_index, (size_t)NUM_LIMB_BITS, (size_t)NUM_LIMB_BITS); - ctx->range_constrain_two_limbs( - limb_2.witness_index, limb_3.witness_index, (size_t)NUM_LIMB_BITS, (size_t)num_last_limb_bits); + ctx->range_constrain_two_limbs(limb_0.get_normalized_witness_index(), + limb_1.get_normalized_witness_index(), + (size_t)NUM_LIMB_BITS, + (size_t)NUM_LIMB_BITS); + ctx->range_constrain_two_limbs(limb_2.get_normalized_witness_index(), + limb_3.get_normalized_witness_index(), + (size_t)NUM_LIMB_BITS, + (size_t)num_last_limb_bits); return result; } else { @@ -291,7 +299,7 @@ template bigfield::bigfield(const byt const auto [limb0, limb1] = reconstruct_two_limbs(ctx, lo_8_bytes, lolo_8_bytes, lo_split_byte); const auto [limb2, limb3] = reconstruct_two_limbs(ctx, hi_8_bytes, mid_8_bytes, mid_split_byte); - const auto res = bigfield(limb0, limb1, limb2, limb3, true); + const auto res = bigfield::unsafe_construct_from_limbs(limb0, limb1, limb2, limb3, true); const auto num_last_limb_bits = 256 - (NUM_LIMB_BITS * 3); res.binary_basis_limbs[3].maximum_value = (uint64_t(1) << num_last_limb_bits); @@ -340,13 +348,14 @@ template uint512_t bigfield::get_maxi } /** - * @brief Add a field element to the lower limb. CAUTION (the element has to be constrained before using this function) + * @brief Add a field element to the lower limb. CAUTION (the element has to be constrained before using this + * function) * - * @details Sometimes we need to add a small constrained value to a bigfield element (for example, a boolean value), but - * we don't want to construct a full bigfield element for that as it would take too many gates. If the maximum value of - * the field element being added is small enough, we can simply add it to the lowest limb and increase its maximum - * value. That will create 2 additional constraints instead of 5/3 needed to add 2 bigfield elements and several needed - * to construct a bigfield element. + * @details Sometimes we need to add a small constrained value to a bigfield element (for example, a boolean value), + * but we don't want to construct a full bigfield element for that as it would take too many gates. If the maximum + * value of the field element being added is small enough, we can simply add it to the lowest limb and increase its + * maximum value. That will create 2 additional constraints instead of 5/3 needed to add 2 bigfield elements and + * several needed to construct a bigfield element. * * @tparam Builder Builder * @tparam T Field Parameters @@ -359,14 +368,33 @@ bigfield bigfield::add_to_lower_limb(const field_t::from_witness(context, binary_basis_limbs[i].element.get_value()), + binary_basis_limbs[i].maximum_value); + // Ensure it is fixed + result.binary_basis_limbs[i].element.fix_witness(); + result.context = ctx; + } + } else { + + // if this element is a witness, then all limbs will be witnesses + result = *this; + } result.binary_basis_limbs[0].maximum_value = binary_basis_limbs[0].maximum_value + other_maximum_value; result.binary_basis_limbs[0].element = binary_basis_limbs[0].element + other; @@ -411,7 +439,11 @@ bigfield bigfield::operator+(const bigfield& other) cons limbconst = limbconst || other.binary_basis_limbs[2].element.is_constant(); limbconst = limbconst || other.binary_basis_limbs[3].element.is_constant(); limbconst = limbconst || other.prime_basis_limb.is_constant(); - limbconst = limbconst || (prime_basis_limb.witness_index == other.prime_basis_limb.witness_index); + limbconst = + limbconst || (prime_basis_limb.get_witness_index() == + other.prime_basis_limb + .get_witness_index()); // We are comparing if the bigfield elements are exactly the + // same object, so we compare the unnormalized witness indices if (!limbconst) { std::pair x0{ binary_basis_limbs[0].element.witness_index, binary_basis_limbs[0].element.multiplicative_constant }; @@ -441,7 +473,6 @@ bigfield bigfield::operator+(const bigfield& other) cons uint32_t xp(prime_basis_limb.witness_index); uint32_t yp(other.prime_basis_limb.witness_index); bb::fr cp(prime_basis_limb.additive_constant + other.prime_basis_limb.additive_constant); - const auto output_witnesses = ctx->evaluate_non_native_field_addition( { x0, y0, c0 }, { x1, y1, c1 }, { x2, y2, c2 }, { x3, y3, c3 }, { xp, yp, cp }); result.binary_basis_limbs[0].element = field_t::from_witness_index(ctx, output_witnesses[0]); @@ -572,7 +603,8 @@ bigfield bigfield::operator-(const bigfield& other) cons * Step 3: Compute offset terms t0, t1, t2, t3 that we add to our result to ensure each limb is positive * * t3 represents the value we are BORROWING from constant_to_add.limb[3] - * t2, t1, t0 are the terms we will ADD to constant_to_add.limb[2], constant_to_add.limb[1], constant_to_add.limb[0] + * t2, t1, t0 are the terms we will ADD to constant_to_add.limb[2], constant_to_add.limb[1], + *constant_to_add.limb[0] * * i.e. The net value we add to `constant_to_add` is 0. We must ensure that: * t3 = t0 + (t1 << NUM_LIMB_BITS) + (t2 << NUM_LIMB_BITS * 2) @@ -587,8 +619,8 @@ bigfield bigfield::operator-(const bigfield& other) cons uint256_t t3(uint256_t(1) << (limb_2_borrow_shift - NUM_LIMB_BITS)); /** - * Compute the limbs of `constant_to_add`, including our offset terms t0, t1, t2, t3 that ensure each result limb is - *positive + * Compute the limbs of `constant_to_add`, including our offset terms t0, t1, t2, t3 that ensure each result + *limb is positive **/ uint256_t to_add_0 = uint256_t(constant_to_add.slice(0, NUM_LIMB_BITS)) + t0; uint256_t to_add_1 = uint256_t(constant_to_add.slice(NUM_LIMB_BITS, NUM_LIMB_BITS * 2)) + t1; @@ -624,7 +656,11 @@ bigfield bigfield::operator-(const bigfield& other) cons limbconst = limbconst || other.binary_basis_limbs[2].element.is_constant(); limbconst = limbconst || other.binary_basis_limbs[3].element.is_constant(); limbconst = limbconst || other.prime_basis_limb.is_constant(); - limbconst = limbconst || (prime_basis_limb.witness_index == other.prime_basis_limb.witness_index); + limbconst = + limbconst || + (prime_basis_limb.witness_index == + other.prime_basis_limb.witness_index); // We are checking if this is and identical element, so we + // need to compare the actual indices, not normalized ones if (!limbconst) { std::pair x0{ result.binary_basis_limbs[0].element.witness_index, binary_basis_limbs[0].element.multiplicative_constant }; @@ -713,10 +749,11 @@ bigfield bigfield::operator*(const bigfield& other) cons return remainder; } else { // when writing a*b = q*p + r we wish to enforce r<2^s for smallest s such that p<2^s - // hence the second constructor call is with can_overflow=false. This will allow using r in more additions mod - // 2^t without needing to apply the mod, where t=4*NUM_LIMB_BITS + // hence the second constructor call is with can_overflow=false. This will allow using r in more additions + // mod 2^t without needing to apply the mod, where t=4*NUM_LIMB_BITS - // Check if the product overflows CRT or the quotient can't be contained in a range proof and reduce accordingly + // Check if the product overflows CRT or the quotient can't be contained in a range proof and reduce + // accordingly auto [reduction_required, num_quotient_bits] = get_quotient_reduction_info({ get_maximum_value() }, { other.get_maximum_value() }, {}); if (reduction_required) { @@ -739,8 +776,8 @@ bigfield bigfield::operator*(const bigfield& other) cons } /** - * Division operator. Doesn't create constraints for b!=0, which can lead to vulnerabilities. If you need a safer - *variant use div_check_denominator_nonzero. + * Division operator. Create constraints for b!=0 by default. If you need a variant + *without the zero check, use div_without_denominator_check. * * To evaluate (a / b = c mod p), we instead evaluate (c * b = a mod p). **/ @@ -748,7 +785,7 @@ template bigfield bigfield::operator/(const bigfield& other) const { - return internal_div({ *this }, other, false); + return internal_div({ *this }, other, true); } /** * @brief Create constraints for summing these terms @@ -812,7 +849,11 @@ bigfield bigfield::internal_div(const std::vector bigfield::div_without_denominator_check(const s return internal_div(numerators, denominator, false); } +template +bigfield bigfield::div_without_denominator_check(const bigfield& denominator) +{ + return internal_div({ *this }, denominator, false); +} + /** * Div method with constraints for denominator!=0. * @@ -929,6 +976,7 @@ template bigfield bigfield bigfield bigfield::sqradd(const std::vector& to_add) const { + ASSERT(to_add.size() <= MAXIMUM_SUMMAND_COUNT); reduction_check(); Builder* ctx = context; @@ -993,36 +1041,62 @@ bigfield bigfield::sqradd(const std::vector& t /** * @brief Raise a bigfield to a power of an exponent. Note that the exponent must not exceed 32 bits and is - * implicitly range constrained. The exponent is turned into a field_t witness for the underlying pow method - * to work. + * implicitly range constrained. * * @returns this ** (exponent) * * @todo TODO(https://github.com/AztecProtocol/barretenberg/issues/1014) Improve the efficiency of this function. - * @todo TODO(https://github.com/AztecProtocol/barretenberg/issues/1015) Security of this (as part of the whole class) + * @todo TODO(https://github.com/AztecProtocol/barretenberg/issues/1015) Security of this (as part of the whole + * class) */ template bigfield bigfield::pow(const size_t exponent) const { - auto* ctx = get_context() ? get_context() : nullptr; + // Just return one immediately + + if (exponent == 0) { + return bigfield(uint256_t(1)); + } + + bool accumulator_initialized = false; + bigfield accumulator; + bigfield running_power = *this; + auto shifted_exponent = exponent; - return pow(witness_t(ctx, exponent)); + // Square and multiply + while (shifted_exponent != 0) { + if (shifted_exponent & 1) { + if (!accumulator_initialized) { + accumulator = running_power; + accumulator_initialized = true; + } else { + accumulator *= running_power; + } + } + if (shifted_exponent != 0) { + running_power = running_power.sqr(); + } + shifted_exponent >>= 1; + } + return accumulator; } /** - * @brief Raise a bigfield to a power of an exponent (field_t) that must be a witness. Note that the exponent must not - * exceed 32 bits and is implicitly range constrained. + * @brief Raise a bigfield to a power of an exponent (field_t) that must be a witness. Note that the exponent must + * not exceed 32 bits and is implicitly range constrained. * * @returns this ** (exponent) * * @todo TODO(https://github.com/AztecProtocol/barretenberg/issues/1014) Improve the efficiency of this function. - * @todo TODO(https://github.com/AztecProtocol/barretenberg/issues/1015) Security of this (as part of the whole class) + * @todo TODO(https://github.com/AztecProtocol/barretenberg/issues/1015) Security of this (as part of the whole + * class) */ template bigfield bigfield::pow(const field_t& exponent) const { auto* ctx = get_context() ? get_context() : exponent.get_context(); uint256_t exponent_value = exponent.get_value(); + if constexpr (IsSimulator) { if ((exponent_value >> 32) != static_cast(0)) { ctx->failure("field_t::pow exponent accumulator incorrect"); @@ -1033,34 +1107,37 @@ bigfield bigfield::pow(const field_t& exponent) return result; } - bool exponent_constant = exponent.is_constant(); + ASSERT(exponent_value.get_msb() < 32); + // Use the constant version that perfoms only the necessary multiplications if the exponent is constant + if (exponent.is_constant()) { + return this->pow(static_cast(exponent_value)); + } std::vector> exponent_bits(32); + // Collect individual bits as bool_t's for (size_t i = 0; i < exponent_bits.size(); ++i) { uint256_t value_bit = exponent_value & 1; bool_t bit; - bit = exponent_constant ? bool_t(ctx, value_bit.data[0]) : witness_t(ctx, value_bit.data[0]); + bit = bool_t(witness_t(ctx, value_bit.data[0])); exponent_bits[31 - i] = (bit); exponent_value >>= 1; } - if (!exponent_constant) { - field_t exponent_accumulator(ctx, 0); - for (const auto& bit : exponent_bits) { - exponent_accumulator += exponent_accumulator; - exponent_accumulator += bit; - } - exponent.assert_equal(exponent_accumulator, "field_t::pow exponent accumulator incorrect"); + field_t exponent_accumulator(ctx, 0); + + // Reconstruct the exponent from bits + for (const auto& bit : exponent_bits) { + exponent_accumulator += exponent_accumulator; + exponent_accumulator += field_t(bit); } + + // Ensure it's equal to the original + exponent.assert_equal(exponent_accumulator, "field_t::pow exponent accumulator incorrect"); bigfield accumulator(ctx, 1); - bigfield mul_coefficient = *this - 1; + bigfield one(1); + // Compute the power with a square-and-multiply algorithm for (size_t digit_idx = 0; digit_idx < 32; ++digit_idx) { accumulator *= accumulator; - const bigfield bit(field_t(exponent_bits[digit_idx]), - field_t(witness_t(ctx, 0)), - field_t(witness_t(ctx, 0)), - field_t(witness_t(ctx, 0)), - /*can_overflow=*/true); - accumulator *= (mul_coefficient * bit + 1); + accumulator *= one.conditional_select(*this, exponent_bits[digit_idx]); } accumulator.self_reduce(); accumulator.set_origin_tag(OriginTag(get_origin_tag(), exponent.tag)); @@ -1078,6 +1155,7 @@ bigfield bigfield::pow(const field_t& exponent) template bigfield bigfield::madd(const bigfield& to_mul, const std::vector& to_add) const { + ASSERT(to_add.size() <= MAXIMUM_SUMMAND_COUNT); Builder* ctx = context ? context : to_mul.context; reduction_check(); to_mul.reduction_check(); @@ -1147,6 +1225,10 @@ void bigfield::perform_reductions_for_mult_madd(std::vector& mul_right, const std::vector& to_add) { + ASSERT(mul_left.size() == mul_right.size()); + ASSERT(to_add.size() <= MAXIMUM_SUMMAND_COUNT); + ASSERT(mul_left.size() <= MAXIMUM_SUMMAND_COUNT); + const size_t number_of_products = mul_left.size(); // Get the maximum values of elements std::vector max_values_left; @@ -1221,19 +1303,20 @@ void bigfield::perform_reductions_for_mult_madd(std::vector& left_element, std::tuple& right_element) { return std::get<0>(left_element) > std::get<0>(right_element); }; - // Sort the vector, larger values first - std::sort(maximum_value_updates.begin(), maximum_value_updates.end(), compare_update_tuples); // Now we loop through, reducing 1 element each time. This is costly in code, but allows us to use fewer // gates while (reduction_required) { + // Compute the possible reduction updates + compute_updates(maximum_value_updates, mul_left, mul_right, number_of_products); + + // Sort the vector, larger values first + std::sort(maximum_value_updates.begin(), maximum_value_updates.end(), compare_update_tuples); // We choose the largest update auto [update_size, largest_update_product_index, multiplicand_index] = maximum_value_updates[0]; @@ -1247,19 +1330,14 @@ void bigfield::perform_reductions_for_mult_madd(std::vector( - get_quotient_reduction_info(max_values_left, max_values_right, to_add, { DEFAULT_MAXIMUM_REMAINDER })); - - compute_updates(maximum_value_updates, mul_left, mul_right, number_of_products); - for (size_t i = 0; i < number_of_products; i++) { max_values_left[i] = mul_left[i].get_maximum_value(); max_values_right[i] = mul_right[i].get_maximum_value(); } - - // Sort the vector - std::sort(maximum_value_updates.begin(), maximum_value_updates.end(), compare_update_tuples); + reduction_required = std::get<0>( + get_quotient_reduction_info(max_values_left, max_values_right, to_add, { DEFAULT_MAXIMUM_REMAINDER })); } + // Now we have reduced everything exactly to the point of no overflow. There is probably a way to use even // fewer reductions, but for now this will suffice. } @@ -1281,6 +1359,8 @@ bigfield bigfield::mult_madd(const std::vector bool fix_remainder_to_zero) { ASSERT(mul_left.size() == mul_right.size()); + ASSERT(mul_left.size() <= MAXIMUM_SUMMAND_COUNT); + ASSERT(to_add.size() <= MAXIMUM_SUMMAND_COUNT); std::vector mutable_mul_left(mul_left); std::vector mutable_mul_right(mul_right); @@ -1381,7 +1461,8 @@ bigfield bigfield::mult_madd(const std::vector } } - // Now that we know that there is at least 1 non-constant multiplication, we can start estimating reductions, etc + // Now that we know that there is at least 1 non-constant multiplication, we can start estimating reductions, + // etc // Compute the constant term we're adding const auto [_, constant_part_remainder_1024] = (sum_of_constant_products + add_right_constant_sum).divmod(modulus); @@ -1409,8 +1490,8 @@ bigfield bigfield::mult_madd(const std::vector // Check that we can actually reduce the products enough, this assert will probably never get triggered ASSERT((worst_case_product_sum + add_right_maximum) < get_maximum_crt_product()); - // We've collapsed all constants, checked if we can compute the sum of products in the worst case, time to check if - // we need to reduce something + // We've collapsed all constants, checked if we can compute the sum of products in the worst case, time to check + // if we need to reduce something perform_reductions_for_mult_madd(new_input_left, new_input_right, new_to_add); uint1024_t sum_of_products_final(0); for (size_t i = 0; i < final_number_of_products; i++) { @@ -1464,6 +1545,7 @@ bigfield bigfield::dual_madd(const bigfield& left_a, const bigfield& right_b, const std::vector& to_add) { + ASSERT(to_add.size() <= MAXIMUM_SUMMAND_COUNT); left_a.reduction_check(); right_a.reduction_check(); left_b.reduction_check(); @@ -1500,12 +1582,6 @@ bigfield bigfield::msub_div(const std::vector& const std::vector& to_sub, bool enable_divisor_nz_check) { - Builder* ctx = divisor.context; - - const size_t num_multiplications = mul_left.size(); - native product_native = 0; - bool products_constant = true; - // Check the basics ASSERT(mul_left.size() == mul_right.size()); ASSERT(divisor.get_value() != 0); @@ -1517,6 +1593,35 @@ bigfield bigfield::msub_div(const std::vector& for (auto& element : to_sub) { new_tag = OriginTag(new_tag, element.get_origin_tag()); } + // Gett he context + Builder* ctx = divisor.context; + if (ctx == NULL) { + for (auto& el : mul_left) { + if (el.context != NULL) { + ctx = el.context; + break; + } + } + } + if (ctx == NULL) { + for (auto& el : mul_right) { + if (el.context != NULL) { + ctx = el.context; + break; + } + } + } + if (ctx == NULL) { + for (auto& el : to_sub) { + if (el.context != NULL) { + ctx = el.context; + break; + } + } + } + const size_t num_multiplications = mul_left.size(); + native product_native = 0; + bool products_constant = true; // This check is optional, because it is heavy and often we don't need it at all if (enable_divisor_nz_check) { @@ -1550,8 +1655,10 @@ bigfield bigfield::msub_div(const std::vector& if (sub_constant && products_constant && divisor.is_constant()) { auto result = bigfield(ctx, uint256_t(result_value.lo.lo)); result.set_origin_tag(new_tag); + return result; } + ASSERT(ctx != NULL); // Create the result witness bigfield result = create_from_u512_as_witness(ctx, result_value.lo); @@ -1578,6 +1685,7 @@ bigfield bigfield::conditional_negate(const bool_t bigfield::conditional_negate(const bool_t>(predicate).madd(-(binary_basis_limbs[3].element * two) + to_add_3, binary_basis_limbs[3].element); - uint256_t max_limb_0 = binary_basis_limbs[0].maximum_value + to_add_0_u256 + t0; - uint256_t max_limb_1 = binary_basis_limbs[1].maximum_value + to_add_1_u256 + t1; - uint256_t max_limb_2 = binary_basis_limbs[2].maximum_value + to_add_2_u256 + t2; - uint256_t max_limb_3 = binary_basis_limbs[3].maximum_value + to_add_3_u256 - t3; + uint256_t maximum_negated_limb_0 = to_add_0_u256 + t0; + uint256_t maximum_negated_limb_1 = to_add_1_u256 + t1; + uint256_t maximum_negated_limb_2 = to_add_2_u256 + t2; + uint256_t maximum_negated_limb_3 = to_add_3_u256; + + uint256_t max_limb_0 = binary_basis_limbs[0].maximum_value > maximum_negated_limb_0 + ? binary_basis_limbs[0].maximum_value + : maximum_negated_limb_0; + uint256_t max_limb_1 = binary_basis_limbs[1].maximum_value > maximum_negated_limb_1 + ? binary_basis_limbs[1].maximum_value + : maximum_negated_limb_1; + uint256_t max_limb_2 = binary_basis_limbs[2].maximum_value > maximum_negated_limb_2 + ? binary_basis_limbs[2].maximum_value + : maximum_negated_limb_2; + uint256_t max_limb_3 = binary_basis_limbs[3].maximum_value > maximum_negated_limb_3 + ? binary_basis_limbs[3].maximum_value + : maximum_negated_limb_3; bigfield result(ctx); result.binary_basis_limbs[0] = Limb(limb_0, max_limb_0); @@ -1717,7 +1838,8 @@ bigfield bigfield::conditional_select(const bigfield& ot * This allows us to evaluate `operator==` using only 1 bigfield multiplication operation. * We can check the product equals 0 or 1 by directly evaluating the binary basis/prime basis limbs of Y. * i.e. if `r == 1` then `(a - b)*X` should have 0 for all limb values - * if `r == 0` then `(a - b)*X` should have 1 in the least significant binary basis limb and 0 elsewhere + * if `r == 0` then `(a - b)*X` should have 1 in the least significant binary basis limb and 0 + * elsewhere * @tparam Builder * @tparam T * @param other @@ -1732,15 +1854,15 @@ template bool_t bigfield::op if (!ctx) { // TODO(https://github.com/AztecProtocol/barretenberg/issues/660): null context _should_ mean that both are // constant, but we check with an assertion to be sure. - ASSERT(is_constant() == other.is_constant()); + ASSERT(is_constant() && other.is_constant()); return is_equal_raw; } bool_t is_equal = witness_t(ctx, is_equal_raw); bigfield diff = (*this) - other; - // TODO(https://github.com/AztecProtocol/barretenberg/issues/999): get native values efficiently (i.e. if u512 value - // fits in a u256, subtract off modulus until u256 fits into finite field) + // TODO(https://github.com/AztecProtocol/barretenberg/issues/999): get native values efficiently (i.e. if u512 + // value fits in a u256, subtract off modulus until u256 fits into finite field) native diff_native = native((diff.get_value() % modulus_u512).lo); native inverse_native = is_equal_raw ? 0 : diff_native.invert(); @@ -1772,10 +1894,8 @@ template bool_t bigfield::op * This prevents our field arithmetic from overflowing the native modulus boundary, whilst ensuring we can * still use the chinese remainder theorem to validate field multiplications with a reduced number of range checks * - * @param num_products The number of products a*b in the parent function that calls the reduction check. Needed to - *limit overflow **/ -template void bigfield::reduction_check(const size_t num_products) const +template void bigfield::reduction_check() const { if (is_constant()) { // this seems not a reduction check, but actually computing the reduction @@ -1807,12 +1927,32 @@ template void bigfield::reduction_che bool limb_overflow_test_1 = binary_basis_limbs[1].maximum_value > maximum_limb_value; bool limb_overflow_test_2 = binary_basis_limbs[2].maximum_value > maximum_limb_value; bool limb_overflow_test_3 = binary_basis_limbs[3].maximum_value > maximum_limb_value; - if (get_maximum_value() > get_maximum_unreduced_value(num_products) || limb_overflow_test_0 || - limb_overflow_test_1 || limb_overflow_test_2 || limb_overflow_test_3) { + if (get_maximum_value() > get_maximum_unreduced_value() || limb_overflow_test_0 || limb_overflow_test_1 || + limb_overflow_test_2 || limb_overflow_test_3) { self_reduce(); } } +/** + * SANITY CHECK on a value that is about to interact with another value + * + * @details ASSERTs that the value of all limbs is less than or equal to the prohibited maximum value. Checks that the + *maximum value of the whole element is also less than a prohibited maximum value + * + **/ +template void bigfield::sanity_check() const +{ + + uint256_t maximum_limb_value = get_prohibited_maximum_limb_value(); + bool limb_overflow_test_0 = binary_basis_limbs[0].maximum_value > maximum_limb_value; + bool limb_overflow_test_1 = binary_basis_limbs[1].maximum_value > maximum_limb_value; + bool limb_overflow_test_2 = binary_basis_limbs[2].maximum_value > maximum_limb_value; + bool limb_overflow_test_3 = binary_basis_limbs[3].maximum_value > maximum_limb_value; + ASSERT(!(get_maximum_value() > get_prohibited_maximum_value() || limb_overflow_test_0 || limb_overflow_test_1 || + limb_overflow_test_2 || limb_overflow_test_3)); +} + +// Underneath performs assert_less_than(modulus) // create a version with mod 2^t element part in [0,p-1] // After reducing to size 2^s, we check (p-1)-a is non-negative as integer. // We perform subtraction using carries on blocks of size 2^b. The operations inside the blocks are done mod r @@ -1821,70 +1961,11 @@ template void bigfield::reduction_che // non-negative at the end of subtraction, we know the subtraction result is positive as integers and a

void bigfield::assert_is_in_field() const { - // Warning: this assumes we have run circuit construction at least once in debug mode where large non reduced - // constants are allowed via ASSERT - if (is_constant()) { - return; - } - - self_reduce(); // this method in particular enforces limb vals are <2^b - needed for logic described above - uint256_t value = get_value().lo; - // TODO:make formal assert that modulus<=256 bits - constexpr uint256_t modulus_minus_one = modulus_u512.lo - 1; - - constexpr uint256_t modulus_minus_one_0 = modulus_minus_one.slice(0, NUM_LIMB_BITS); - constexpr uint256_t modulus_minus_one_1 = modulus_minus_one.slice(NUM_LIMB_BITS, NUM_LIMB_BITS * 2); - constexpr uint256_t modulus_minus_one_2 = modulus_minus_one.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 3); - constexpr uint256_t modulus_minus_one_3 = modulus_minus_one.slice(NUM_LIMB_BITS * 3, NUM_LIMB_BITS * 4); - - bool borrow_0_value = value.slice(0, NUM_LIMB_BITS) > modulus_minus_one_0; - bool borrow_1_value = - (value.slice(NUM_LIMB_BITS, NUM_LIMB_BITS * 2) + uint256_t(borrow_0_value)) > (modulus_minus_one_1); - bool borrow_2_value = - (value.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 3) + uint256_t(borrow_1_value)) > (modulus_minus_one_2); - - field_t modulus_0(context, modulus_minus_one_0); - field_t modulus_1(context, modulus_minus_one_1); - field_t modulus_2(context, modulus_minus_one_2); - field_t modulus_3(context, modulus_minus_one_3); - bool_t borrow_0(witness_t(context, borrow_0_value)); - bool_t borrow_1(witness_t(context, borrow_1_value)); - bool_t borrow_2(witness_t(context, borrow_2_value)); - // The way we use borrows here ensures that we are checking that modulus - binary_basis > 0. - // We check that the result in each limb is > 0. - // If the modulus part in this limb is smaller, we simply borrow the value from the higher limb. - // The prover can rearrange the borrows the way they like. The important thing is that the borrows are - // constrained. - field_t r0 = modulus_0 - binary_basis_limbs[0].element + static_cast>(borrow_0) * shift_1; - field_t r1 = modulus_1 - binary_basis_limbs[1].element + - static_cast>(borrow_1) * shift_1 - static_cast>(borrow_0); - field_t r2 = modulus_2 - binary_basis_limbs[2].element + - static_cast>(borrow_2) * shift_1 - static_cast>(borrow_1); - field_t r3 = modulus_3 - binary_basis_limbs[3].element - static_cast>(borrow_2); - r0 = r0.normalize(); - r1 = r1.normalize(); - r2 = r2.normalize(); - r3 = r3.normalize(); - if constexpr (HasPlookup) { - context->decompose_into_default_range(r0.witness_index, static_cast(NUM_LIMB_BITS)); - context->decompose_into_default_range(r1.witness_index, static_cast(NUM_LIMB_BITS)); - context->decompose_into_default_range(r2.witness_index, static_cast(NUM_LIMB_BITS)); - context->decompose_into_default_range(r3.witness_index, static_cast(NUM_LIMB_BITS)); - } else { - context->decompose_into_base4_accumulators( - r0.witness_index, static_cast(NUM_LIMB_BITS), "bigfield: assert_is_in_field range constraint 1."); - context->decompose_into_base4_accumulators( - r1.witness_index, static_cast(NUM_LIMB_BITS), "bigfield: assert_is_in_field range constraint 2."); - context->decompose_into_base4_accumulators( - r2.witness_index, static_cast(NUM_LIMB_BITS), "bigfield: assert_is_in_field range constraint 3."); - context->decompose_into_base4_accumulators( - r3.witness_index, static_cast(NUM_LIMB_BITS), "bigfield: assert_is_in_field range constraint 4."); - } + assert_less_than(modulus); } template void bigfield::assert_less_than(const uint256_t upper_limit) const { - // TODO(kesha): Merge this with assert_is_in_field // Warning: this assumes we have run circuit construction at least once in debug mode where large non reduced // constants are allowed via ASSERT if constexpr (IsSimulator) { @@ -1900,8 +1981,8 @@ template void bigfield::assert_less_t } ASSERT(upper_limit != 0); - // The circuit checks that limit - this >= 0, so if we are doing a less_than comparison, we need to subtract 1 from - // the limit + // The circuit checks that limit - this >= 0, so if we are doing a less_than comparison, we need to subtract 1 + // from the limit uint256_t strict_upper_limit = upper_limit - uint256_t(1); self_reduce(); // this method in particular enforces limb vals are <2^b - needed for logic described above uint256_t value = get_value().lo; @@ -1940,25 +2021,32 @@ template void bigfield::assert_less_t r1 = r1.normalize(); r2 = r2.normalize(); r3 = r3.normalize(); - if constexpr (Builder::CIRCUIT_TYPE == CircuitType::ULTRA) { - context->decompose_into_default_range(r0.witness_index, static_cast(NUM_LIMB_BITS)); - context->decompose_into_default_range(r1.witness_index, static_cast(NUM_LIMB_BITS)); - context->decompose_into_default_range(r2.witness_index, static_cast(NUM_LIMB_BITS)); - context->decompose_into_default_range(r3.witness_index, static_cast(NUM_LIMB_BITS)); + if constexpr (HasPlookup) { + context->decompose_into_default_range(r0.get_normalized_witness_index(), static_cast(NUM_LIMB_BITS)); + context->decompose_into_default_range(r1.get_normalized_witness_index(), static_cast(NUM_LIMB_BITS)); + context->decompose_into_default_range(r2.get_normalized_witness_index(), static_cast(NUM_LIMB_BITS)); + context->decompose_into_default_range(r3.get_normalized_witness_index(), static_cast(NUM_LIMB_BITS)); } else { - context->decompose_into_base4_accumulators( - r0.witness_index, static_cast(NUM_LIMB_BITS), "bigfield: assert_less_than range constraint 1."); - context->decompose_into_base4_accumulators( - r1.witness_index, static_cast(NUM_LIMB_BITS), "bigfield: assert_less_than range constraint 2."); - context->decompose_into_base4_accumulators( - r2.witness_index, static_cast(NUM_LIMB_BITS), "bigfield: assert_less_than range constraint 3."); - context->decompose_into_base4_accumulators( - r3.witness_index, static_cast(NUM_LIMB_BITS), "bigfield: assert_less_than range constraint 4."); + context->decompose_into_base4_accumulators(r0.get_normalized_witness_index(), + static_cast(NUM_LIMB_BITS), + "bigfield: assert_less_than range constraint 1."); + context->decompose_into_base4_accumulators(r1.get_normalized_witness_index(), + static_cast(NUM_LIMB_BITS), + "bigfield: assert_less_than range constraint 2."); + context->decompose_into_base4_accumulators(r2.get_normalized_witness_index(), + static_cast(NUM_LIMB_BITS), + "bigfield: assert_less_than range constraint 3."); + context->decompose_into_base4_accumulators(r3.get_normalized_witness_index(), + static_cast(NUM_LIMB_BITS), + "bigfield: assert_less_than range constraint 4."); } } // check elements are equal mod p by proving their integer difference is a multiple of p. // This relies on the minus operator for a-b increasing a by a multiple of p large enough so diff is non-negative +// When one of the elements is a constant and another is a witness we check equality of limbs, so if the witness +// bigfield element is in an unreduced form, it needs to be reduced first. We don't have automatice reduced form +// detection for now, so it is up to the circuit writer to detect this template void bigfield::assert_equal(const bigfield& other) const { Builder* ctx = this->context ? this->context : other.context; @@ -1970,6 +2058,7 @@ template void bigfield::assert_equal( if (is_constant() && other.is_constant()) { std::cerr << "bigfield: calling assert equal on 2 CONSTANT bigfield elements...is this intended?" << std::endl; + ASSERT(get_value() == other.get_value()); // We expect constants to be less than the target modulus return; } else if (other.is_constant()) { // TODO(https://github.com/AztecProtocol/barretenberg/issues/998): Something is fishy here @@ -1990,28 +2079,6 @@ template void bigfield::assert_equal( other.assert_equal(*this); return; } else { - if (is_constant() && other.is_constant()) { - std::cerr << "bigfield: calling assert equal on 2 CONSTANT bigfield elements...is this intended?" - << std::endl; - return; - } else if (other.is_constant()) { - // evaluate a strict equality - make sure *this is reduced first, or an honest prover - // might not be able to satisfy these constraints. - field_t t0 = (binary_basis_limbs[0].element - other.binary_basis_limbs[0].element); - field_t t1 = (binary_basis_limbs[1].element - other.binary_basis_limbs[1].element); - field_t t2 = (binary_basis_limbs[2].element - other.binary_basis_limbs[2].element); - field_t t3 = (binary_basis_limbs[3].element - other.binary_basis_limbs[3].element); - field_t t4 = (prime_basis_limb - other.prime_basis_limb); - t0.assert_is_zero(); - t1.assert_is_zero(); - t2.assert_is_zero(); - t3.assert_is_zero(); - t4.assert_is_zero(); - return; - } else if (is_constant()) { - other.assert_equal(*this); - return; - } bigfield diff = *this - other; const uint512_t diff_val = diff.get_value(); @@ -2047,7 +2114,7 @@ template void bigfield::assert_is_not const auto get_overload_count = [target_modulus = modulus_u512](const uint512_t& maximum_value) { uint512_t target = target_modulus; size_t overload_count = 0; - while (target < maximum_value) { + while (target <= maximum_value) { ++overload_count; target += target_modulus; } @@ -2101,13 +2168,15 @@ template void bigfield::self_reduce() if ((maximum_quotient_bits & 1ULL) == 1ULL) { ++maximum_quotient_bits; } - // TODO: implicit assumption here - NUM_LIMB_BITS large enough for all the quotient + + ASSERT(maximum_quotient_bits <= NUM_LIMB_BITS); uint32_t quotient_limb_index = context->add_variable(bb::fr(quotient_value.lo)); field_t quotient_limb = field_t::from_witness_index(context, quotient_limb_index); if constexpr (HasPlookup) { - context->decompose_into_default_range(quotient_limb.witness_index, static_cast(maximum_quotient_bits)); + context->decompose_into_default_range(quotient_limb.get_normalized_witness_index(), + static_cast(maximum_quotient_bits)); } else { - context->decompose_into_base4_accumulators(quotient_limb.witness_index, + context->decompose_into_base4_accumulators(quotient_limb.get_normalized_witness_index(), static_cast(maximum_quotient_bits), "bigfield: quotient_limb too large."); } @@ -2154,7 +2223,21 @@ void bigfield::unsafe_evaluate_multiply_add(const bigfield& input_le const std::vector& input_remainders) { + ASSERT(to_add.size() <= MAXIMUM_SUMMAND_COUNT); + ASSERT(input_remainders.size() <= MAXIMUM_SUMMAND_COUNT); + // Sanity checks + input_left.sanity_check(); + input_to_mul.sanity_check(); + input_quotient.sanity_check(); + for (auto& el : to_add) { + el.sanity_check(); + } + for (auto& el : input_remainders) { + el.sanity_check(); + } + std::vector remainders(input_remainders); + bigfield left = input_left; bigfield to_mul = input_to_mul; bigfield quotient = input_quotient; @@ -2183,7 +2266,19 @@ void bigfield::unsafe_evaluate_multiply_add(const bigfield& input_le uint512_t max_r0 = left.binary_basis_limbs[0].maximum_value * to_mul.binary_basis_limbs[0].maximum_value; max_r0 += (neg_modulus_limbs_u256[0] * quotient.binary_basis_limbs[0].maximum_value); - const uint512_t max_r1 = max_b0 + max_b1; + uint512_t max_r1 = max_b0 + max_b1; + + uint256_t borrow_lo_value = 0; + for (const auto& remainder : input_remainders) { + max_r0 += remainder.binary_basis_limbs[0].maximum_value; + max_r1 += remainder.binary_basis_limbs[1].maximum_value; + + borrow_lo_value += (remainder.binary_basis_limbs[0].maximum_value + + (remainder.binary_basis_limbs[1].maximum_value << NUM_LIMB_BITS)); + } + borrow_lo_value >>= 2 * NUM_LIMB_BITS; + field_t borrow_lo(ctx, bb::fr(borrow_lo_value)); + const uint512_t max_r2 = max_c0 + max_c1 + max_c2; const uint512_t max_r3 = max_d0 + max_d1 + max_d2 + max_d3; @@ -2196,7 +2291,8 @@ void bigfield::unsafe_evaluate_multiply_add(const bigfield& input_le (to_add[i].binary_basis_limbs[3].maximum_value << NUM_LIMB_BITS); } const uint512_t max_lo = max_r0 + (max_r1 << NUM_LIMB_BITS) + max_a0; - const uint512_t max_hi = max_r2 + (max_r3 << NUM_LIMB_BITS) + max_a1; + const uint512_t max_lo_carry = max_lo >> (2 * NUM_LIMB_BITS); + const uint512_t max_hi = max_r2 + (max_r3 << NUM_LIMB_BITS) + max_a1 + max_lo_carry; uint64_t max_lo_bits = (max_lo.get_msb() + 1); uint64_t max_hi_bits = max_hi.get_msb() + 1; @@ -2207,6 +2303,15 @@ void bigfield::unsafe_evaluate_multiply_add(const bigfield& input_le ++max_hi_bits; } + uint64_t carry_lo_msb = max_lo_bits - (2 * NUM_LIMB_BITS); + uint64_t carry_hi_msb = max_hi_bits - (2 * NUM_LIMB_BITS); + + if (max_lo_bits < (2 * NUM_LIMB_BITS)) { + carry_lo_msb = 0; + } + if (max_hi_bits < (2 * NUM_LIMB_BITS)) { + carry_hi_msb = 0; + } if constexpr (HasPlookup) { // The plookup custom bigfield gate requires inputs are witnesses. // If we're using constant values, instantiate them as circuit variables @@ -2278,38 +2383,34 @@ void bigfield::unsafe_evaluate_multiply_add(const bigfield& input_le bb::non_native_field_witnesses witnesses{ { - left.binary_basis_limbs[0].element.normalize().witness_index, - left.binary_basis_limbs[1].element.normalize().witness_index, - left.binary_basis_limbs[2].element.normalize().witness_index, - left.binary_basis_limbs[3].element.normalize().witness_index, - left.prime_basis_limb.witness_index, + left.binary_basis_limbs[0].element.get_normalized_witness_index(), + left.binary_basis_limbs[1].element.get_normalized_witness_index(), + left.binary_basis_limbs[2].element.get_normalized_witness_index(), + left.binary_basis_limbs[3].element.get_normalized_witness_index(), }, { - to_mul.binary_basis_limbs[0].element.normalize().witness_index, - to_mul.binary_basis_limbs[1].element.normalize().witness_index, - to_mul.binary_basis_limbs[2].element.normalize().witness_index, - to_mul.binary_basis_limbs[3].element.normalize().witness_index, - to_mul.prime_basis_limb.witness_index, + to_mul.binary_basis_limbs[0].element.get_normalized_witness_index(), + to_mul.binary_basis_limbs[1].element.get_normalized_witness_index(), + to_mul.binary_basis_limbs[2].element.get_normalized_witness_index(), + to_mul.binary_basis_limbs[3].element.get_normalized_witness_index(), }, { - quotient.binary_basis_limbs[0].element.normalize().witness_index, - quotient.binary_basis_limbs[1].element.normalize().witness_index, - quotient.binary_basis_limbs[2].element.normalize().witness_index, - quotient.binary_basis_limbs[3].element.normalize().witness_index, - quotient.prime_basis_limb.witness_index, + quotient.binary_basis_limbs[0].element.get_normalized_witness_index(), + quotient.binary_basis_limbs[1].element.get_normalized_witness_index(), + quotient.binary_basis_limbs[2].element.get_normalized_witness_index(), + quotient.binary_basis_limbs[3].element.get_normalized_witness_index(), }, { - remainder_limbs[0].normalize().witness_index, - remainder_limbs[1].normalize().witness_index, - remainder_limbs[2].normalize().witness_index, - remainder_limbs[3].normalize().witness_index, - remainder_prime_limb.witness_index, + remainder_limbs[0].get_normalized_witness_index(), + remainder_limbs[1].get_normalized_witness_index(), + remainder_limbs[2].get_normalized_witness_index(), + remainder_limbs[3].get_normalized_witness_index(), }, { neg_modulus_limbs[0], neg_modulus_limbs[1], neg_modulus_limbs[2], neg_modulus_limbs[3] }, modulus, }; // N.B. this method also evaluates the prime field component of the non-native field mul - const auto [lo_idx, hi_idx] = ctx->evaluate_non_native_field_multiplication(witnesses, false); + const auto [lo_idx, hi_idx] = ctx->evaluate_non_native_field_multiplication(witnesses); bb::fr neg_prime = -bb::fr(uint256_t(target_basis.modulus)); field_t::evaluate_polynomial_identity(left.prime_basis_limb, @@ -2317,19 +2418,19 @@ void bigfield::unsafe_evaluate_multiply_add(const bigfield& input_le quotient.prime_basis_limb * neg_prime, -remainder_prime_limb); - field_t lo = field_t::from_witness_index(ctx, lo_idx); + field_t lo = field_t::from_witness_index(ctx, lo_idx) + borrow_lo; field_t hi = field_t::from_witness_index(ctx, hi_idx); - const uint64_t carry_lo_msb = max_lo_bits - (2 * NUM_LIMB_BITS); - const uint64_t carry_hi_msb = max_hi_bits - (2 * NUM_LIMB_BITS); // if both the hi and lo output limbs have less than 70 bits, we can use our custom // limb accumulation gate (accumulates 2 field elements, each composed of 5 14-bit limbs, in 3 gates) if (carry_lo_msb <= 70 && carry_hi_msb <= 70) { - ctx->range_constrain_two_limbs( - hi.witness_index, lo.witness_index, size_t(carry_lo_msb), size_t(carry_hi_msb)); + ctx->range_constrain_two_limbs(hi.get_normalized_witness_index(), + lo.get_normalized_witness_index(), + size_t(carry_hi_msb), + size_t(carry_lo_msb)); } else { - ctx->decompose_into_default_range(hi.normalize().witness_index, carry_hi_msb); - ctx->decompose_into_default_range(lo.normalize().witness_index, carry_lo_msb); + ctx->decompose_into_default_range(hi.get_normalized_witness_index(), carry_hi_msb); + ctx->decompose_into_default_range(lo.get_normalized_witness_index(), carry_lo_msb); } } else { const field_t b0 = left.binary_basis_limbs[1].element.madd( @@ -2351,9 +2452,9 @@ void bigfield::unsafe_evaluate_multiply_add(const bigfield& input_le const field_t d3 = left.binary_basis_limbs[0].element.madd( to_mul.binary_basis_limbs[3].element, quotient.binary_basis_limbs[0].element * neg_modulus_limbs[3]); - // We wish to show that left*right - quotient*remainder = 0 mod 2^t, we do this by collecting the limb products - // into two separate variables - carry_lo and carry_hi, which are still small enough not to wrap mod r - // Their first t/2 bits will equal, respectively, the first and second t/2 bits of the expresssion + // We wish to show that left*right - quotient*remainder = 0 mod 2^t, we do this by collecting the limb + // products into two separate variables - carry_lo and carry_hi, which are still small enough not to wrap + // mod r Their first t/2 bits will equal, respectively, the first and second t/2 bits of the expresssion // Thus it will suffice to check that each of them begins with t/2 zeroes. We do this by in fact assigning // to these variables those expressions divided by 2^{t/2}. Since we have bounds on their ranage that are // smaller than r, We can range check the divisions by the original range bounds divided by 2^{t/2} @@ -2379,6 +2480,7 @@ void bigfield::unsafe_evaluate_multiply_add(const bigfield& input_le } field_t t1 = carry_lo.add_two(-remainders[0].binary_basis_limbs[2].element, -(remainders[0].binary_basis_limbs[3].element * shift_1)); + carry_lo += borrow_lo; field_t carry_hi_0 = r2 * shift_right_2; field_t carry_hi_1 = r3 * (shift_1 * shift_right_2); field_t carry_hi_2 = t1 * shift_right_2; @@ -2416,15 +2518,12 @@ void bigfield::unsafe_evaluate_multiply_add(const bigfield& input_le field_t::evaluate_polynomial_identity( left.prime_basis_limb, to_mul.prime_basis_limb, quotient.prime_basis_limb * neg_prime, linear_terms); - const uint64_t carry_lo_msb = max_lo_bits - (2 * NUM_LIMB_BITS); - const uint64_t carry_hi_msb = max_hi_bits - (2 * NUM_LIMB_BITS); - const bb::fr carry_lo_shift(uint256_t(uint256_t(1) << carry_lo_msb)); if ((carry_hi_msb + carry_lo_msb) < field_t::modulus.get_msb()) { field_t carry_combined = carry_lo + (carry_hi * carry_lo_shift); carry_combined = carry_combined.normalize(); const auto accumulators = ctx->decompose_into_base4_accumulators( - carry_combined.witness_index, + carry_combined.get_normalized_witness_index(), static_cast(carry_lo_msb + carry_hi_msb), "bigfield: carry_combined too large in unsafe_evaluate_multiply_add."); field_t accumulator_midpoint = @@ -2433,10 +2532,10 @@ void bigfield::unsafe_evaluate_multiply_add(const bigfield& input_le } else { carry_lo = carry_lo.normalize(); carry_hi = carry_hi.normalize(); - ctx->decompose_into_base4_accumulators(carry_lo.witness_index, + ctx->decompose_into_base4_accumulators(carry_lo.get_normalized_witness_index(), static_cast(carry_lo_msb), "bigfield: carry_lo too large in unsafe_evaluate_multiply_add."); - ctx->decompose_into_base4_accumulators(carry_hi.witness_index, + ctx->decompose_into_base4_accumulators(carry_hi.get_normalized_witness_index(), static_cast(carry_hi_msb), "bigfield: carry_hi too large in unsafe_evaluate_multiply_add."); } @@ -2471,7 +2570,26 @@ void bigfield::unsafe_evaluate_multiple_multiply_add(const std::vect const bigfield& input_quotient, const std::vector& input_remainders) { + ASSERT(input_left.size() == input_right.size()); + ASSERT(input_left.size() <= MAXIMUM_SUMMAND_COUNT); + ASSERT(to_add.size() <= MAXIMUM_SUMMAND_COUNT); + ASSERT(input_remainders.size() <= MAXIMUM_SUMMAND_COUNT); + ASSERT(input_left.size() == input_right.size() && input_left.size() < 1024); + // Sanity checks + for (auto& el : input_left) { + el.sanity_check(); + } + for (auto& el : input_right) { + el.sanity_check(); + } + for (auto& el : to_add) { + el.sanity_check(); + } + input_quotient.sanity_check(); + for (auto& el : input_remainders) { + el.sanity_check(); + } std::vector remainders(input_remainders); std::vector left(input_left); std::vector right(input_right); @@ -2527,7 +2645,19 @@ void bigfield::unsafe_evaluate_multiple_multiply_add(const std::vect // max_r3 = terms from 2^3t - 2^5t uint512_t max_r0 = (neg_modulus_limbs_u256[0] * quotient.binary_basis_limbs[0].maximum_value); max_r0 += (neg_modulus_limbs_u256[0] * quotient.binary_basis_limbs[0].maximum_value); - const uint512_t max_r1 = max_b0 + max_b1; + uint512_t max_r1 = max_b0 + max_b1; + + uint256_t borrow_lo_value(0); + for (const auto& remainder : input_remainders) { + max_r0 += remainder.binary_basis_limbs[0].maximum_value; + max_r1 += remainder.binary_basis_limbs[1].maximum_value; + + borrow_lo_value += remainder.binary_basis_limbs[0].maximum_value + + (remainder.binary_basis_limbs[1].maximum_value << NUM_LIMB_BITS); + } + borrow_lo_value >>= 2 * NUM_LIMB_BITS; + field_t borrow_lo(ctx, bb::fr(borrow_lo_value)); + const uint512_t max_r2 = max_c0 + max_c1 + max_c2; const uint512_t max_r3 = max_d0 + max_d1 + max_d2 + max_d3; @@ -2554,6 +2684,8 @@ void bigfield::unsafe_evaluate_multiple_multiply_add(const std::vect max_hi += product_hi; } + const uint512_t max_lo_carry = max_lo >> (2 * NUM_LIMB_BITS); + max_hi += max_lo_carry; // Compute the maximum number of bits in `max_lo` and `max_hi` - this defines the range constraint values we // will need to apply to validate our product uint64_t max_lo_bits = (max_lo.get_msb() + 1); @@ -2618,32 +2750,28 @@ void bigfield::unsafe_evaluate_multiple_multiply_add(const std::vect if (i > 0) { bb::non_native_field_witnesses mul_witnesses = { { - left[i].binary_basis_limbs[0].element.normalize().witness_index, - left[i].binary_basis_limbs[1].element.normalize().witness_index, - left[i].binary_basis_limbs[2].element.normalize().witness_index, - left[i].binary_basis_limbs[3].element.normalize().witness_index, - left[i].prime_basis_limb.witness_index, + left[i].binary_basis_limbs[0].element.get_normalized_witness_index(), + left[i].binary_basis_limbs[1].element.get_normalized_witness_index(), + left[i].binary_basis_limbs[2].element.get_normalized_witness_index(), + left[i].binary_basis_limbs[3].element.get_normalized_witness_index(), }, { - right[i].binary_basis_limbs[0].element.normalize().witness_index, - right[i].binary_basis_limbs[1].element.normalize().witness_index, - right[i].binary_basis_limbs[2].element.normalize().witness_index, - right[i].binary_basis_limbs[3].element.normalize().witness_index, - right[i].prime_basis_limb.witness_index, + right[i].binary_basis_limbs[0].element.get_normalized_witness_index(), + right[i].binary_basis_limbs[1].element.get_normalized_witness_index(), + right[i].binary_basis_limbs[2].element.get_normalized_witness_index(), + right[i].binary_basis_limbs[3].element.get_normalized_witness_index(), }, { ctx->zero_idx, ctx->zero_idx, ctx->zero_idx, ctx->zero_idx, - ctx->zero_idx, }, { ctx->zero_idx, ctx->zero_idx, ctx->zero_idx, ctx->zero_idx, - ctx->zero_idx, }, { 0, 0, 0, 0 }, modulus, @@ -2714,38 +2842,34 @@ void bigfield::unsafe_evaluate_multiple_multiply_add(const std::vect bb::non_native_field_witnesses witnesses{ { - left[0].binary_basis_limbs[0].element.normalize().witness_index, - left[0].binary_basis_limbs[1].element.normalize().witness_index, - left[0].binary_basis_limbs[2].element.normalize().witness_index, - left[0].binary_basis_limbs[3].element.normalize().witness_index, - left[0].prime_basis_limb.normalize().witness_index, + left[0].binary_basis_limbs[0].element.get_normalized_witness_index(), + left[0].binary_basis_limbs[1].element.get_normalized_witness_index(), + left[0].binary_basis_limbs[2].element.get_normalized_witness_index(), + left[0].binary_basis_limbs[3].element.get_normalized_witness_index(), }, { - right[0].binary_basis_limbs[0].element.normalize().witness_index, - right[0].binary_basis_limbs[1].element.normalize().witness_index, - right[0].binary_basis_limbs[2].element.normalize().witness_index, - right[0].binary_basis_limbs[3].element.normalize().witness_index, - right[0].prime_basis_limb.normalize().witness_index, + right[0].binary_basis_limbs[0].element.get_normalized_witness_index(), + right[0].binary_basis_limbs[1].element.get_normalized_witness_index(), + right[0].binary_basis_limbs[2].element.get_normalized_witness_index(), + right[0].binary_basis_limbs[3].element.get_normalized_witness_index(), }, { - quotient.binary_basis_limbs[0].element.normalize().witness_index, - quotient.binary_basis_limbs[1].element.normalize().witness_index, - quotient.binary_basis_limbs[2].element.normalize().witness_index, - quotient.binary_basis_limbs[3].element.normalize().witness_index, - quotient.prime_basis_limb.normalize().witness_index, + quotient.binary_basis_limbs[0].element.get_normalized_witness_index(), + quotient.binary_basis_limbs[1].element.get_normalized_witness_index(), + quotient.binary_basis_limbs[2].element.get_normalized_witness_index(), + quotient.binary_basis_limbs[3].element.get_normalized_witness_index(), }, { - remainder_limbs[0].normalize().witness_index, - remainder_limbs[1].normalize().witness_index, - remainder_limbs[2].normalize().witness_index, - remainder_limbs[3].normalize().witness_index, - remainder_prime_limb.normalize().witness_index, + remainder_limbs[0].get_normalized_witness_index(), + remainder_limbs[1].get_normalized_witness_index(), + remainder_limbs[2].get_normalized_witness_index(), + remainder_limbs[3].get_normalized_witness_index(), }, { neg_modulus_limbs[0], neg_modulus_limbs[1], neg_modulus_limbs[2], neg_modulus_limbs[3] }, modulus, }; - const auto [lo_1_idx, hi_1_idx] = ctx->evaluate_non_native_field_multiplication(witnesses, false); + const auto [lo_1_idx, hi_1_idx] = ctx->evaluate_non_native_field_multiplication(witnesses); bb::fr neg_prime = -bb::fr(uint256_t(target_basis.modulus)); @@ -2754,20 +2878,29 @@ void bigfield::unsafe_evaluate_multiple_multiply_add(const std::vect quotient.prime_basis_limb * neg_prime, -remainder_prime_limb); - field_t lo = field_t::from_witness_index(ctx, lo_1_idx); + field_t lo = field_t::from_witness_index(ctx, lo_1_idx) + borrow_lo; field_t hi = field_t::from_witness_index(ctx, hi_1_idx); - const uint64_t carry_lo_msb = max_lo_bits - (2 * NUM_LIMB_BITS); - const uint64_t carry_hi_msb = max_hi_bits - (2 * NUM_LIMB_BITS); + uint64_t carry_lo_msb = max_lo_bits - (2 * NUM_LIMB_BITS); + uint64_t carry_hi_msb = max_hi_bits - (2 * NUM_LIMB_BITS); + + if (max_lo_bits < (2 * NUM_LIMB_BITS)) { + carry_lo_msb = 0; + } + if (max_hi_bits < (2 * NUM_LIMB_BITS)) { + carry_hi_msb = 0; + } // if both the hi and lo output limbs have less than 70 bits, we can use our custom // limb accumulation gate (accumulates 2 field elements, each composed of 5 14-bit limbs, in 3 gates) if (carry_lo_msb <= 70 && carry_hi_msb <= 70) { - ctx->range_constrain_two_limbs( - hi.witness_index, lo.witness_index, (size_t)carry_lo_msb, (size_t)carry_hi_msb); + ctx->range_constrain_two_limbs(hi.get_normalized_witness_index(), + lo.get_normalized_witness_index(), + (size_t)carry_hi_msb, + (size_t)carry_lo_msb); } else { - ctx->decompose_into_default_range(hi.normalize().witness_index, carry_hi_msb); - ctx->decompose_into_default_range(lo.normalize().witness_index, carry_lo_msb); + ctx->decompose_into_default_range(hi.get_normalized_witness_index(), carry_hi_msb); + ctx->decompose_into_default_range(lo.get_normalized_witness_index(), carry_lo_msb); } /* NOTE TO AUDITOR: An extraneous block if constexpr (HasPlookup) { @@ -2913,6 +3046,7 @@ void bigfield::unsafe_evaluate_multiple_multiply_add(const std::vect field_t carry_lo = carry_lo_0.add_two(carry_lo_1, carry_lo_2); field_t t1 = carry_lo.add_two(-remainder_limbs[2], -(remainder_limbs[3] * shift_1)); + carry_lo += borrow_lo; field_t carry_hi_0 = r2 * shift_right_2; field_t carry_hi_1 = r3 * (shift_1 * shift_right_2); field_t carry_hi_2 = t1 * shift_right_2; @@ -2936,15 +3070,17 @@ void bigfield::unsafe_evaluate_multiple_multiply_add(const std::vect if constexpr (HasPlookup) { carry_lo = carry_lo.normalize(); carry_hi = carry_hi.normalize(); - ctx->decompose_into_default_range(carry_lo.witness_index, static_cast(carry_lo_msb)); - ctx->decompose_into_default_range(carry_hi.witness_index, static_cast(carry_hi_msb)); + ctx->decompose_into_default_range(carry_lo.get_normalized_witness_index(), + static_cast(carry_lo_msb)); + ctx->decompose_into_default_range(carry_hi.get_normalized_witness_index(), + static_cast(carry_hi_msb)); } else { if ((carry_hi_msb + carry_lo_msb) < field_t::modulus.get_msb()) { field_t carry_combined = carry_lo + (carry_hi * carry_lo_shift); carry_combined = carry_combined.normalize(); const auto accumulators = ctx->decompose_into_base4_accumulators( - carry_combined.witness_index, + carry_combined.get_normalized_witness_index(), static_cast(carry_lo_msb + carry_hi_msb), "bigfield: carry_combined too large in unsafe_evaluate_multiple_multiply_add."); field_t accumulator_midpoint = field_t::from_witness_index( @@ -2954,11 +3090,11 @@ void bigfield::unsafe_evaluate_multiple_multiply_add(const std::vect carry_lo = carry_lo.normalize(); carry_hi = carry_hi.normalize(); ctx->decompose_into_base4_accumulators( - carry_lo.witness_index, + carry_lo.get_normalized_witness_index(), static_cast(carry_lo_msb), "bigfield: carry_lo too large in unsafe_evaluate_multiple_multiply_add."); ctx->decompose_into_base4_accumulators( - carry_hi.witness_index, + carry_hi.get_normalized_witness_index(), static_cast(carry_hi_msb), "bigfield: carry_hi too large in unsafe_evaluate_multiple_multiply_add."); } @@ -2972,10 +3108,21 @@ void bigfield::unsafe_evaluate_square_add(const bigfield& left, const bigfield& quotient, const bigfield& remainder) { + ASSERT(to_add.size() <= MAXIMUM_SUMMAND_COUNT); + if (HasPlookup) { unsafe_evaluate_multiply_add(left, left, to_add, quotient, { remainder }); return; } + + // Sanity checks + left.sanity_check(); + remainder.sanity_check(); + quotient.sanity_check(); + for (auto& el : to_add) { + el.sanity_check(); + } + Builder* ctx = left.context == nullptr ? quotient.context : left.context; uint512_t max_b0 = (left.binary_basis_limbs[1].maximum_value * left.binary_basis_limbs[0].maximum_value); @@ -3096,15 +3243,15 @@ void bigfield::unsafe_evaluate_square_add(const bigfield& left, if constexpr (HasPlookup) { carry_lo = carry_lo.normalize(); carry_hi = carry_hi.normalize(); - ctx->decompose_into_default_range(carry_lo.witness_index, static_cast(carry_lo_msb)); - ctx->decompose_into_default_range(carry_hi.witness_index, static_cast(carry_hi_msb)); + ctx->decompose_into_default_range(carry_lo.get_normalized_witness_index(), static_cast(carry_lo_msb)); + ctx->decompose_into_default_range(carry_hi.get_normalized_witness_index(), static_cast(carry_hi_msb)); } else { if ((carry_hi_msb + carry_lo_msb) < field_t::modulus.get_msb()) { field_t carry_combined = carry_lo + (carry_hi * carry_lo_shift); carry_combined = carry_combined.normalize(); const auto accumulators = ctx->decompose_into_base4_accumulators( - carry_combined.witness_index, + carry_combined.get_normalized_witness_index(), static_cast(carry_lo_msb + carry_hi_msb), "bigfield: carry_combined too large in unsafe_evaluate_square_add."); field_t accumulator_midpoint = @@ -3113,10 +3260,10 @@ void bigfield::unsafe_evaluate_square_add(const bigfield& left, } else { carry_lo = carry_lo.normalize(); carry_hi = carry_hi.normalize(); - ctx->decompose_into_base4_accumulators(carry_lo.witness_index, + ctx->decompose_into_base4_accumulators(carry_lo.get_normalized_witness_index(), static_cast(carry_lo_msb), "bigfield: carry_lo too large in unsafe_evaluate_square_add."); - ctx->decompose_into_base4_accumulators(carry_hi.witness_index, + ctx->decompose_into_base4_accumulators(carry_hi.get_normalized_witness_index(), static_cast(carry_hi_msb), "bigfield: carry_hi too large in unsafe_evaluate_square_add"); } @@ -3127,6 +3274,8 @@ template std::pair bigfield::compute_quotient_remainder_values( const bigfield& a, const bigfield& b, const std::vector& to_add) { + ASSERT(to_add.size() <= MAXIMUM_SUMMAND_COUNT); + uint512_t add_values(0); for (const auto& add_element : to_add) { add_element.reduction_check(); @@ -3149,6 +3298,8 @@ uint512_t bigfield::compute_maximum_quotient_value(const std::vector const std::vector& to_add) { ASSERT(as.size() == bs.size()); + ASSERT(to_add.size() <= MAXIMUM_SUMMAND_COUNT); + uint512_t add_values(0); for (const auto& add_element : to_add) { add_values += add_element; @@ -3171,6 +3322,11 @@ std::pair bigfield::get_quotient_reduction_info(const const std::vector& remainders_max) { ASSERT(as_max.size() == bs_max.size()); + + ASSERT(to_add.size() <= MAXIMUM_SUMMAND_COUNT); + ASSERT(as_max.size() <= MAXIMUM_SUMMAND_COUNT); + ASSERT(remainders_max.size() <= MAXIMUM_SUMMAND_COUNT); + // Check if the product sum can overflow CRT modulus if (mul_product_overflows_crt_modulus(as_max, bs_max, to_add)) { return std::pair(true, 0); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/goblin_field.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/goblin_field.hpp index 8e18a679ce4..c26305e7d4d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/goblin_field.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/goblin_field.hpp @@ -53,9 +53,13 @@ template class goblin_field { // N.B. this method is because AggregationState expects group element coordinates to be split into 4 slices // (we could update to only use 2 for Mega but that feels complex) - goblin_field(field_ct lolo, field_ct lohi, field_ct hilo, field_ct hihi, [[maybe_unused]] bool can_overflow = false) - : limbs{ lolo + lohi * (uint256_t(1) << NUM_LIMB_BITS), hilo + hihi * (uint256_t(1) << NUM_LIMB_BITS) } - {} + static goblin_field construct_from_limbs( + field_ct lolo, field_ct lohi, field_ct hilo, field_ct hihi, [[maybe_unused]] bool can_overflow = false) + { + goblin_field result; + result.limbs = { lolo + lohi * (uint256_t(1) << NUM_LIMB_BITS), hilo + hihi * (uint256_t(1) << NUM_LIMB_BITS) }; + return result; + } void assert_equal(const goblin_field& other) const { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.hpp index dcb321b3e5c..11d37f67f4b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.hpp @@ -54,9 +54,10 @@ template class goblin_ele static goblin_element from_witness(Builder* ctx, const typename NativeGroup::affine_element& input) { goblin_element out; + // ECCVM requires points at infinity to be represented by 0-value x/y coords if (input.is_point_at_infinity()) { - Fq x = Fq::from_witness(ctx, NativeGroup::affine_one.x); - Fq y = Fq::from_witness(ctx, NativeGroup::affine_one.y); + Fq x = Fq::from_witness(ctx, bb::fq(0)); + Fq y = Fq::from_witness(ctx, bb::fq(0)); out.x = x; out.y = y; } else { @@ -92,6 +93,16 @@ template class goblin_ele return goblin_element(x_fq, y_fq); } + static goblin_element point_at_infinity(Builder* ctx) + { + Fr zero = Fr::from_witness_index(ctx, ctx->zero_idx); + Fq x_fq(zero, zero); + Fq y_fq(zero, zero); + goblin_element result(x_fq, y_fq); + result.set_point_at_infinity(true); + return result; + } + goblin_element checked_unconditional_add(const goblin_element& other) const { return goblin_element::operator+(*this, other); @@ -107,10 +118,58 @@ template class goblin_ele } goblin_element operator-(const goblin_element& other) const { - std::vector points{ *this, other }; - return batch_mul({ *this, other }, { Fr(1), -Fr(1) }); + auto builder = get_context(other); + // Check that the internal accumulator is zero + ASSERT(builder->op_queue->get_accumulator().is_point_at_infinity()); + + // Compute the result natively, and validate that result + other == *this + typename NativeGroup::affine_element result_value = typename NativeGroup::affine_element( + typename NativeGroup::element(get_value()) - typename NativeGroup::element(other.get_value())); + + ecc_op_tuple op_tuple; + op_tuple = builder->queue_ecc_add_accum(other.get_value()); + { + auto x_lo = Fr::from_witness_index(builder, op_tuple.x_lo); + auto x_hi = Fr::from_witness_index(builder, op_tuple.x_hi); + auto y_lo = Fr::from_witness_index(builder, op_tuple.y_lo); + auto y_hi = Fr::from_witness_index(builder, op_tuple.y_hi); + x_lo.assert_equal(other.x.limbs[0]); + x_hi.assert_equal(other.x.limbs[1]); + y_lo.assert_equal(other.y.limbs[0]); + y_hi.assert_equal(other.y.limbs[1]); + } + + ecc_op_tuple op_tuple2 = builder->queue_ecc_add_accum(result_value); + auto x_lo = Fr::from_witness_index(builder, op_tuple2.x_lo); + auto x_hi = Fr::from_witness_index(builder, op_tuple2.x_hi); + auto y_lo = Fr::from_witness_index(builder, op_tuple2.y_lo); + auto y_hi = Fr::from_witness_index(builder, op_tuple2.y_hi); + + Fq result_x(x_lo, x_hi); + Fq result_y(y_lo, y_hi); + goblin_element result(result_x, result_y); + + // if the output is at infinity, this is represented by x/y coordinates being zero + // because they are all 136-bit, we can do a cheap zerocheck by first summing the limbs + auto op2_is_infinity = (x_lo.add_two(x_hi, y_lo) + y_hi).is_zero(); + result.set_point_at_infinity(op2_is_infinity); + { + ecc_op_tuple op_tuple3 = builder->queue_ecc_eq(); + auto x_lo = Fr::from_witness_index(builder, op_tuple3.x_lo); + auto x_hi = Fr::from_witness_index(builder, op_tuple3.x_hi); + auto y_lo = Fr::from_witness_index(builder, op_tuple3.y_lo); + auto y_hi = Fr::from_witness_index(builder, op_tuple3.y_hi); + + x_lo.assert_equal(x.limbs[0]); + x_hi.assert_equal(x.limbs[1]); + y_lo.assert_equal(y.limbs[0]); + y_hi.assert_equal(y.limbs[1]); + } + return result; } - goblin_element operator-() const { return batch_mul({ *this }, { -Fr(1) }); } + + goblin_element operator-() const { return point_at_infinity(get_context()) - *this; } + goblin_element operator+=(const goblin_element& other) { *this = *this + other; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.test.cpp index 9dd26890fbc..6fc3ec6cbea 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin.test.cpp @@ -42,6 +42,7 @@ template class stdlib_biggroup_goblin : public testing::Test { static void test_goblin_style_batch_mul() { const size_t num_points = 5; + const size_t edge_case_points = 3; Builder builder; std::vector points; @@ -50,10 +51,16 @@ template class stdlib_biggroup_goblin : public testing::Test { points.push_back(affine_element(element::random_element())); scalars.push_back(fr::random_element()); } + points.push_back(g1::affine_point_at_infinity); + scalars.push_back(fr::random_element()); + points.push_back(g1::affine_point_at_infinity); + scalars.push_back(0); + points.push_back(element::random_element()); + scalars.push_back(0); std::vector circuit_points; std::vector circuit_scalars; - for (size_t i = 0; i < num_points; ++i) { + for (size_t i = 0; i < num_points + edge_case_points; ++i) { circuit_points.push_back(element_ct::from_witness(&builder, points[i])); circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i])); } @@ -62,7 +69,7 @@ template class stdlib_biggroup_goblin : public testing::Test { element expected_point = g1::one; expected_point.self_set_infinity(); - for (size_t i = 0; i < num_points; ++i) { + for (size_t i = 0; i < num_points + edge_case_points; ++i) { expected_point += (element(points[i]) * scalars[i]); } @@ -75,13 +82,121 @@ template class stdlib_biggroup_goblin : public testing::Test { EXPECT_CIRCUIT_CORRECTNESS(builder); } + + static void test_goblin_style_batch_mul_to_zero() + { + const size_t num_points = 5; + Builder builder; + + std::vector points; + std::vector scalars; + for (size_t i = 0; i < num_points; ++i) { + points.push_back(affine_element(element::random_element())); + scalars.push_back(fr::random_element()); + } + for (size_t i = 0; i < num_points; ++i) { + points.push_back(points[i]); + scalars.push_back(-scalars[i]); + } + std::vector circuit_points; + std::vector circuit_scalars; + for (size_t i = 0; i < num_points * 2; ++i) { + circuit_points.push_back(element_ct::from_witness(&builder, points[i])); + circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i])); + } + + element_ct result_point = element_ct::batch_mul(circuit_points, circuit_scalars); + + EXPECT_EQ(result_point.get_value(), g1::affine_point_at_infinity); + EXPECT_CIRCUIT_CORRECTNESS(builder); + } + + /** + * @brief Test goblin-style sub + * @details Check that 1) Goblin-style batch sub returns correct value (esp. when infinities involved), and 2) + * resulting circuit is correct + */ + static void test_goblin_style_sub() + { + Builder builder; + + for (size_t i = 0; i < 100; ++i) { + + affine_element lhs(element::random_element()); + affine_element rhs(element::random_element()); + + affine_element expected = affine_element(element(lhs) - element(rhs)); + + element_ct lhs_ct = element_ct::from_witness(&builder, lhs); + element_ct lhs2_ct = element_ct::from_witness(&builder, lhs); + + element_ct rhs_ct = element_ct::from_witness(&builder, rhs); + element_ct out_ct = lhs_ct - rhs_ct; + EXPECT_EQ(out_ct.get_value(), expected); + + element_ct zero_ct = lhs_ct - lhs_ct; + EXPECT_TRUE(zero_ct.get_value().is_point_at_infinity()); + + element_ct zero_ct2 = lhs_ct - lhs2_ct; + EXPECT_TRUE(zero_ct2.get_value().is_point_at_infinity()); + + element_ct out2_ct = element_ct::point_at_infinity(&builder) - rhs_ct; + EXPECT_EQ(out2_ct.get_value(), -rhs); + + element_ct out3_ct = lhs_ct - element_ct::point_at_infinity(&builder); + EXPECT_EQ(out3_ct.get_value(), lhs); + + auto lhs_infinity_ct = element_ct::point_at_infinity(&builder); + auto rhs_infinity_ct = element_ct::point_at_infinity(&builder); + element_ct out4_ct = lhs_infinity_ct - rhs_infinity_ct; + EXPECT_TRUE(out4_ct.get_value().is_point_at_infinity()); + EXPECT_TRUE(out4_ct.is_point_at_infinity().get_value()); + } + EXPECT_CIRCUIT_CORRECTNESS(builder); + } + + /** + * @brief Check goblin-style negate works as intended, including with points at infinity + */ + static void test_goblin_style_neg() + { + Builder builder; + affine_element lhs(element::random_element()); + + affine_element expected = -lhs; + + element_ct lhs_ct = element_ct::from_witness(&builder, lhs); + + element_ct result_ct = -lhs_ct; + EXPECT_EQ(result_ct.get_value(), expected); + + element_ct infinity = element_ct::point_at_infinity(&builder); + element_ct result2_ct = -infinity; + EXPECT_EQ(result2_ct.get_value(), g1::affine_point_at_infinity); + EXPECT_CIRCUIT_CORRECTNESS(builder); + } }; using TestTypes = testing::Types>; TYPED_TEST_SUITE(stdlib_biggroup_goblin, TestTypes); -HEAVY_TYPED_TEST(stdlib_biggroup_goblin, batch_mul) +TYPED_TEST(stdlib_biggroup_goblin, batch_mul) { TestFixture::test_goblin_style_batch_mul(); } + +TYPED_TEST(stdlib_biggroup_goblin, batch_mul_equals_zero) +{ + TestFixture::test_goblin_style_batch_mul_to_zero(); +} + +TYPED_TEST(stdlib_biggroup_goblin, sub) +{ + TestFixture::test_goblin_style_sub(); +} + +TYPED_TEST(stdlib_biggroup_goblin, neg) +{ + TestFixture::test_goblin_style_neg(); +} diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin_impl.hpp index 97210ebdd3d..5cd94c27c04 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_goblin_impl.hpp @@ -90,11 +90,12 @@ goblin_element goblin_element::batch_mul(const std:: Fq point_y(y_lo, y_hi); goblin_element result = goblin_element(point_x, point_y); - // NOTE: we can have an `if` statement here under the strict assumption that `return_is_infinity` - // is produced from `eq_and_reset` opcode - if (op_tuple.return_is_infinity) { - result.set_point_at_infinity(bool_ct(builder, true)); - }; + // NOTE: this used to be set as a circuit constant from `op_tuple.return_is_infinity + // I do not see how this was secure as it meant a circuit constant could change depending on witness values + // e.g. x*[P] + y*[Q] where `x = y` and `[P] = -[Q]` + // TODO(@zac-williamson) what is op_queue.return_is_infinity actually used for? I don't see its value + auto op2_is_infinity = (x_lo.add_two(x_hi, y_lo) + y_hi).is_zero(); + result.set_point_at_infinity(op2_is_infinity); return result; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_impl.hpp index aa667bf2595..03872a3a945 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_impl.hpp @@ -93,7 +93,7 @@ element element::operator+(const element& other) con // If either inputs are points at infinity, we set lambda_denominator to be 1. This ensures we never trigger a // divide by zero error. // Note: if either inputs are points at infinity we will not use the result of this computation. - Fq safe_edgecase_denominator = Fq(field_t(1), field_t(0), field_t(0), field_t(0)); + Fq safe_edgecase_denominator = Fq(1); lambda_denominator = Fq::conditional_assign( lhs_infinity || rhs_infinity || infinity_predicate, safe_edgecase_denominator, lambda_denominator); const Fq lambda = Fq::div_without_denominator_check({ lambda_numerator }, lambda_denominator); @@ -163,7 +163,7 @@ element element::operator-(const element& other) con // If either inputs are points at infinity, we set lambda_denominator to be 1. This ensures we never trigger a // divide by zero error. // (if either inputs are points at infinity we will not use the result of this computation) - Fq safe_edgecase_denominator = Fq(field_t(1), field_t(0), field_t(0), field_t(0)); + Fq safe_edgecase_denominator = Fq(1); lambda_denominator = Fq::conditional_assign( lhs_infinity || rhs_infinity || infinity_predicate, safe_edgecase_denominator, lambda_denominator); const Fq lambda = Fq::div_without_denominator_check({ lambda_numerator }, lambda_denominator); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_tables.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_tables.hpp index 821779acbf6..40cfcf24abc 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_tables.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_tables.hpp @@ -74,8 +74,9 @@ element element::read_group_element_rom_tables( const auto yhi = tables[3][index]; const auto xyprime = tables[4][index]; - Fq x_fq(xlo[0], xlo[1], xhi[0], xhi[1], xyprime[0]); - Fq y_fq(ylo[0], ylo[1], yhi[0], yhi[1], xyprime[1]); + // We assign maximum_value of each limb here, so we can use the unsafe API from element construction + Fq x_fq = Fq::unsafe_construct_from_limbs(xlo[0], xlo[1], xhi[0], xhi[1], xyprime[0]); + Fq y_fq = Fq::unsafe_construct_from_limbs(ylo[0], ylo[1], yhi[0], yhi[1], xyprime[1]); x_fq.binary_basis_limbs[0].maximum_value = limb_max[0]; x_fq.binary_basis_limbs[1].maximum_value = limb_max[1]; x_fq.binary_basis_limbs[2].maximum_value = limb_max[2]; @@ -157,8 +158,10 @@ element element::eight_bit_fixed_base_table::oper const auto yhi = plookup_read::read_pair_from_table(tags[3], index); const auto xyprime = plookup_read::read_pair_from_table(tags[4], index); - Fq x = Fq(xlo.first, xlo.second, xhi.first, xhi.second, xyprime.first); - Fq y = Fq(ylo.first, ylo.second, yhi.first, yhi.second, xyprime.second); + // All the elements are precomputed constants so they are completely reduced, so the default maximum limb values are + // appropriate + Fq x = Fq::unsafe_construct_from_limbs(xlo.first, xlo.second, xhi.first, xhi.second, xyprime.first); + Fq y = Fq::unsafe_construct_from_limbs(ylo.first, ylo.second, yhi.first, yhi.second, xyprime.second); if (use_endomorphism) { y = -y; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.hpp index 686b9a6deb1..76d4c333700 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.hpp @@ -63,6 +63,8 @@ template class bool_t { bool_t normalize() const; + uint32_t get_normalized_witness_index() const { return normalize().witness_index; } + Builder* get_context() const { return context; } void set_origin_tag(const OriginTag& new_tag) const { tag = new_tag; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp index 6639158e411..6fa48ee7957 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp @@ -249,7 +249,7 @@ template class DataBusDepot { l1.create_range_constraint(Fq::NUM_LIMB_BITS, "l1"); l2.create_range_constraint(Fq::NUM_LIMB_BITS, "l2"); l3.create_range_constraint(Fq::NUM_LAST_LIMB_BITS, "l3"); - return Fq(l0, l1, l2, l3, /*can_overflow=*/false); + return Fq::construct_from_limbs(l0, l1, l2, l3, /*can_overflow=*/false); } void assert_equality_of_commitments(const Commitment& P0, const Commitment& P1) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.cpp index 1081be69d34..51b4b726be5 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.cpp @@ -77,7 +77,15 @@ template field_t::operator bool_t() const bool mul_constant_check = (multiplicative_constant == bb::fr::one()); bool inverted_check = (additive_constant == bb::fr::one()) && (multiplicative_constant == bb::fr::neg_one()); if ((!add_constant_check || !mul_constant_check) && !inverted_check) { - normalize(); + auto normalized_element = normalize(); + bb::fr witness = context->get_variable(normalized_element.get_witness_index()); + ASSERT((witness == bb::fr::zero()) || (witness == bb::fr::one())); + bool_t result(context); + result.witness_bool = (witness == bb::fr::one()); + result.witness_inverted = false; + result.witness_index = normalized_element.get_witness_index(); + context->create_bool_gate(normalized_element.get_witness_index()); + return result; } bb::fr witness = context->get_variable(witness_index); @@ -509,6 +517,18 @@ template field_t field_t::add_two(const fie return result; } +/** + * @brief Return an new element, where the in-circuit witness contains the actual represented value (multiplicative + * constant is 1 and additive_constant is 0) + * + * @details If the element is a constant or it is already normalized, just return the element itself + * + *@todo We need to add a mechanism into the circuit builders for caching normalized variants for fields and bigfields. + *It should make the circuits smaller. https://github.com/AztecProtocol/barretenberg/issues/1052 + * + * @tparam Builder + * @return field_t + */ template field_t field_t::normalize() const { if (witness_index == IS_CONSTANT || @@ -923,6 +943,7 @@ void field_t::evaluate_linear_identity(const field_t& a, const field_t& if (a.witness_index == IS_CONSTANT && b.witness_index == IS_CONSTANT && c.witness_index == IS_CONSTANT && d.witness_index == IS_CONSTANT) { + ASSERT(a.get_value() + b.get_value() + c.get_value() + d.get_value() == 0); return; } @@ -958,6 +979,7 @@ void field_t::evaluate_polynomial_identity(const field_t& a, if (a.witness_index == IS_CONSTANT && b.witness_index == IS_CONSTANT && c.witness_index == IS_CONSTANT && d.witness_index == IS_CONSTANT) { + ASSERT((a.get_value() * b.get_value() + c.get_value() + d.get_value()).is_zero()); return; } @@ -1187,7 +1209,6 @@ std::vector> field_t::decompose_into_bits( // y_lo = (2**128 + p_lo) - sum_lo field_t y_lo = (-sum) + (p_lo + shift); y_lo += shifted_high_limb; - y_lo.normalize(); if constexpr (IsSimulator) { fr sum_lo = shift + p_lo - y_lo.get_value(); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp index 479c274ec57..77a1d4d8759 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp @@ -256,7 +256,7 @@ template class field_t { * Constants do not need to be normalized, as there is no underlying 'witness'; a constant's value is * wholly tracked by `this.additive_constant`, so we definitely don't want to set that to 0! **/ - field_t normalize() const; + [[nodiscard]] field_t normalize() const; bb::fr get_value() const; @@ -313,8 +313,26 @@ template class field_t { context->fix_witness(witness_index, get_value()); } + /** + * @brief Get the witness index of the current field element. + * + * @warning Are you sure you don't want to use + * get_normalized_witness_index? + * + * @return uint32_t + */ uint32_t get_witness_index() const { return witness_index; } + /** + * @brief Get the index of a normalized version of this element + * + * @details Most of the time when using field elements in other parts of stdlib we want to use this API instead of + * get_witness index. The reason is it will prevent some soundess vulnerabilities + * + * @return uint32_t + */ + uint32_t get_normalized_witness_index() const { return normalize().witness_index; } + std::vector> decompose_into_bits( size_t num_bits = 256, std::function(Builder* ctx, uint64_t, uint256_t)> get_bit = diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.test.cpp index 8da6527284c..546e79012ec 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.test.cpp @@ -107,6 +107,17 @@ template class stdlib_field : public testing::Test { run_test(-1, fr::modulus.get_msb() + 1, true); } + /** + * @brief Test that bool is converted correctly + * + */ + static void test_bool_conversion_regression() + { + Builder builder = Builder(); + field_ct one = field_ct(witness_ct(&builder, 1)); + bool_ct b_false = bool_ct(one * field_ct(0)); + EXPECT_FALSE(b_false.get_value()); + } /** * @brief Demonstrate current behavior of assert_equal. */ @@ -1099,6 +1110,11 @@ TYPED_TEST(stdlib_field, test_assert_equal) TestFixture::test_assert_equal(); } +TYPED_TEST(stdlib_field, test_bool_conversion_regression) +{ + TestFixture::test_bool_conversion_regression(); +} + TYPED_TEST(stdlib_field, test_div) { TestFixture::test_div(); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.cpp index 375211e12a0..9e26b77eb5b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.cpp @@ -48,6 +48,7 @@ plookup::ReadData> plookup_read::get_lookup_accumulato if (rhs_index == IS_CONSTANT) { key_b_witness = std::nullopt; } + const auto accumulator_witnesses = ctx->create_gates_from_plookup_accumulators(id, lookup_data, lhs_index, key_b_witness); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.test.cpp index 9c36c59a809..a76a95a9a1d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.test.cpp @@ -555,8 +555,8 @@ TEST(stdlib_plookup, secp256k1_generator) const auto xhi = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_XHI, index); const auto ylo = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_YLO, index); const auto yhi = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_YHI, index); - curve::fq_ct x = curve::fq_ct(xlo.first, xlo.second, xhi.first, xhi.second); - curve::fq_ct y = curve::fq_ct(ylo.first, ylo.second, yhi.first, yhi.second); + curve::fq_ct x = curve::fq_ct::unsafe_construct_from_limbs(xlo.first, xlo.second, xhi.first, xhi.second); + curve::fq_ct y = curve::fq_ct::unsafe_construct_from_limbs(ylo.first, ylo.second, yhi.first, yhi.second); const auto res = curve::g1_ct(x, y).get_value(); curve::fr scalar(i); @@ -573,8 +573,8 @@ TEST(stdlib_plookup, secp256k1_generator) const auto ylo = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_YLO, circuit_naf_values[0]); const auto yhi = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_YHI, circuit_naf_values[0]); - curve::fq_ct x = curve::fq_ct(xlo.first, xlo.second, xhi.first, xhi.second); - curve::fq_ct y = curve::fq_ct(ylo.first, ylo.second, yhi.first, yhi.second); + curve::fq_ct x = curve::fq_ct::unsafe_construct_from_limbs(xlo.first, xlo.second, xhi.first, xhi.second); + curve::fq_ct y = curve::fq_ct::unsafe_construct_from_limbs(ylo.first, ylo.second, yhi.first, yhi.second); accumulator = curve::g1_ct(x, y); } for (size_t i = 1; i < circuit_naf_values.size(); ++i) { @@ -590,8 +590,8 @@ TEST(stdlib_plookup, secp256k1_generator) const auto xhi = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_XHI, circuit_naf_values[i]); const auto ylo = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_YLO, circuit_naf_values[i]); const auto yhi = plookup_read::read_pair_from_table(MultiTableId::SECP256K1_YHI, circuit_naf_values[i]); - curve::fq_ct x = curve::fq_ct(xlo.first, xlo.second, xhi.first, xhi.second); - curve::fq_ct y = curve::fq_ct(ylo.first, ylo.second, yhi.first, yhi.second); + curve::fq_ct x = curve::fq_ct::unsafe_construct_from_limbs(xlo.first, xlo.second, xhi.first, xhi.second); + curve::fq_ct y = curve::fq_ct::unsafe_construct_from_limbs(ylo.first, ylo.second, yhi.first, yhi.second); accumulator = accumulator.montgomery_ladder(curve::g1_ct(x, y)); } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.cpp index 67199413f86..566a8d83397 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.cpp @@ -77,25 +77,98 @@ std::shared_ptr ProtogalaxyRecursiv const FF vanishing_polynomial_at_challenge = combiner_challenge * (combiner_challenge - FF(1)); const std::vector lagranges = { FF(1) - combiner_challenge, combiner_challenge }; + /* + Fold the commitments + Note: we use additional challenges to reduce the amount of elliptic curve work performed by the ECCVM + + For an accumulator commitment [P'] and an instance commitment [P] , we compute folded commitment [P''] where + [P''] = L0(gamma).[P'] + L1(gamma).[P] + For the size-2 case this becomes: + P'' = (1 - gamma).[P'] + gamma.[P] = gamma.[P - P'] + [P'] + + This requires a large number of size-1 scalar muls (about 53) + The ECCVM can perform a size-k MSM in 32 + roundup((k/4)) rows, if each scalar multiplier is <128 bits + i.e. number of ECCVM rows = 53 * 64 = painful + + To optimize, we generate challenges `c_i` for each commitment and evaluate the relation: + + [A] = \sum c_i.[P_i] + [B] = \sum c_i.[P'_i] + [C] = \sum c_i.[P''_i] + and validate + (1 - gamma).[A] + gamma.[B] == [C] + + + This reduces the relation to 3 large MSMs where each commitment requires 3 size-128bit scalar multiplications + For a flavor with 53 instance/witness commitments, this is 53 * 24 rows + + Note: there are more efficient ways to evaluate this relationship if one solely wants to reduce number of scalar + muls, however we must also consider the number of ECCVM operations being executed, as each operation incurs a + cost in the translator circuit Each ECCVM opcode produces 5 rows in the translator circuit, which is approx. + equivalent to 9 ECCVM rows. Something to pay attention to + */ + std::vector accumulator_commitments; + std::vector instance_commitments; + for (const auto& precomputed : keys_to_fold.get_precomputed_commitments()) { + ASSERT(precomputed.size() == 2); + accumulator_commitments.emplace_back(precomputed[0]); + instance_commitments.emplace_back(precomputed[1]); + } + for (const auto& witness : keys_to_fold.get_witness_commitments()) { + ASSERT(witness.size() == 2); + accumulator_commitments.emplace_back(witness[0]); + instance_commitments.emplace_back(witness[1]); + } + + // derive output commitment witnesses + std::vector output_commitments; + for (size_t i = 0; i < accumulator_commitments.size(); ++i) { + const auto lhs_scalar = (FF(1) - combiner_challenge).get_value(); + const auto rhs_scalar = combiner_challenge.get_value(); + + const auto lhs = accumulator_commitments[i].get_value(); + + const auto rhs = instance_commitments[i].get_value(); + const auto output = lhs * lhs_scalar + rhs * rhs_scalar; + output_commitments.emplace_back(Commitment::from_witness(builder, output)); + } + + std::array args; + for (size_t idx = 0; idx < Flavor::NUM_FOLDED_ENTITIES; ++idx) { + args[idx] = "accumulator_combination_challenges" + std::to_string(idx); + } + std::array folding_challenges = transcript->template get_challenges(args); + std::vector scalars(folding_challenges.begin(), folding_challenges.end()); + + Commitment accumulator_sum = Commitment::batch_mul(accumulator_commitments, + scalars, + /*max_num_bits=*/0, + /*handle_edge_cases=*/IsUltraBuilder); + + Commitment instance_sum = Commitment::batch_mul(instance_commitments, + scalars, + /*max_num_bits=*/0, + /*handle_edge_cases=*/IsUltraBuilder); + + Commitment output_sum = Commitment::batch_mul(output_commitments, + scalars, + /*max_num_bits=*/0, + /*handle_edge_cases=*/IsUltraBuilder); + + Commitment folded_sum = Commitment::batch_mul({ accumulator_sum, instance_sum }, + lagranges, + /*max_num_bits=*/0, + /*handle_edge_cases=*/IsUltraBuilder); + + output_sum.x.assert_equal(folded_sum.x); + output_sum.y.assert_equal(folded_sum.y); + // Compute next folding parameters accumulator->is_accumulator = true; accumulator->target_sum = perturbator_evaluation * lagranges[0] + vanishing_polynomial_at_challenge * combiner_quotient_at_challenge; accumulator->gate_challenges = update_gate_challenges(perturbator_challenge, accumulator->gate_challenges, deltas); - // Fold the commitments - for (auto [combination, to_combine] : - zip_view(accumulator->verification_key->get_all(), keys_to_fold.get_precomputed_commitments())) { - combination = Commitment::batch_mul( - to_combine, lagranges, /*max_num_bits=*/0, /*with_edgecases=*/IsUltraBuilder); - } - - for (auto [combination, to_combine] : - zip_view(accumulator->witness_commitments.get_all(), keys_to_fold.get_witness_commitments())) { - combination = Commitment::batch_mul( - to_combine, lagranges, /*max_num_bits=*/0, /*with_edgecases=*/IsUltraBuilder); - } - // Fold the relation parameters for (auto [combination, to_combine] : zip_view(accumulator->alphas, keys_to_fold.get_alphas())) { combination = linear_combination(to_combine, lagranges); @@ -105,6 +178,16 @@ std::shared_ptr ProtogalaxyRecursiv combination = linear_combination(to_combine, lagranges); } + auto accumulator_vkey = accumulator->verification_key->get_all(); + for (size_t i = 0; i < Flavor::NUM_PRECOMPUTED_ENTITIES; ++i) { + accumulator_vkey[i] = output_commitments[i]; + } + + auto accumulator_witnesses = accumulator->witness_commitments.get_all(); + for (size_t i = 0; i < Flavor::NUM_WITNESS_ENTITIES; ++i) { + accumulator_witnesses[i] = output_commitments[i + accumulator_vkey.size()]; + } + return accumulator; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.cpp index a94fe57d157..a4a4befd848 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.cpp @@ -137,7 +137,8 @@ bool TranslatorRecursiveVerifier_::verify_translation( typename Flavor::FF>& translation_evaluations) { const auto reconstruct_from_array = [&](const auto& arr) { - const BF reconstructed = BF(arr[0], arr[1], arr[2], arr[3]); + const BF reconstructed = BF::construct_from_limbs(arr[0], arr[1], arr[2], arr[3]); + return reconstructed; }; diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp index 12ab5e5b543..238dcc86b0e 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp @@ -40,8 +40,8 @@ template class CircuitBuilderBase { AggregationObjectPubInputIndices recursive_proof_public_input_indices; bool contains_recursive_proof = false; - // We only know from the circuit description whether a circuit should use a prover which produces - // proofs that are friendly to verify in a circuit themselves. However, a verifier does not need a full circuit + // We know from the CLI arguments during proving whether a circuit should use a prover which produces + // proofs that are friendly to verify in a circuit themselves. A verifier does not need a full circuit // description and should be able to verify a proof with just the verification key and the proof. // This field exists to later set the same field in the verification key, and make sure // that we are using the correct prover/verifier. diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_recursive_flavor.hpp index fc310b80e54..ad0d1ccb534 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_recursive_flavor.hpp @@ -54,6 +54,8 @@ template class MegaRecursiveFlavor_ { static constexpr size_t NUM_PRECOMPUTED_ENTITIES = MegaFlavor::NUM_PRECOMPUTED_ENTITIES; // The total number of witness entities not including shifts. static constexpr size_t NUM_WITNESS_ENTITIES = MegaFlavor::NUM_WITNESS_ENTITIES; + // Total number of folded polynomials, which is just all polynomials except the shifts + static constexpr size_t NUM_FOLDED_ENTITIES = NUM_PRECOMPUTED_ENTITIES + NUM_WITNESS_ENTITIES; // define the tuple of Relations that comprise the Sumcheck relation // Reuse the Relations from Mega diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/op_queue/ecc_op_queue.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/op_queue/ecc_op_queue.hpp index 93ebc0a35d9..bc390f0001c 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/op_queue/ecc_op_queue.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/op_queue/ecc_op_queue.hpp @@ -488,6 +488,13 @@ class ECCOpQueue { uint256_t x_256(point.x); uint256_t y_256(point.y); ultra_op.return_is_infinity = point.is_point_at_infinity(); + // if we have a point at infinity, set x/y to zero + // in the biggroup_goblin class we use `assert_equal` statements to validate + // the original in-circuit coordinate values are also zero + if (point.is_point_at_infinity()) { + x_256 = 0; + y_256 = 0; + } ultra_op.x_lo = Fr(x_256.slice(0, CHUNK_SIZE)); ultra_op.x_hi = Fr(x_256.slice(CHUNK_SIZE, CHUNK_SIZE * 2)); ultra_op.y_lo = Fr(y_256.slice(0, CHUNK_SIZE)); @@ -497,9 +504,15 @@ class ECCOpQueue { Fr z_1 = 0; Fr z_2 = 0; auto converted = scalar.from_montgomery_form(); - Fr::split_into_endomorphism_scalars(converted, z_1, z_2); - ultra_op.z_1 = z_1.to_montgomery_form(); - ultra_op.z_2 = z_2.to_montgomery_form(); + uint256_t converted_u256(scalar); + if (converted_u256.get_msb() <= 128) { + ultra_op.z_1 = scalar; + ultra_op.z_2 = 0; + } else { + Fr::split_into_endomorphism_scalars(converted, z_1, z_2); + ultra_op.z_1 = z_1.to_montgomery_form(); + ultra_op.z_2 = z_2.to_montgomery_form(); + } append_to_ultra_ops(ultra_op); diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.cpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.cpp index 180908807e5..9409d1c119c 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.cpp @@ -1669,7 +1669,7 @@ std::array UltraCircuitBuilder_::decompose_non_nat **/ template std::array UltraCircuitBuilder_::evaluate_non_native_field_multiplication( - const non_native_field_witnesses& input, const bool range_constrain_quotient_and_remainder) + const non_native_field_witnesses& input) { std::array a{ @@ -1697,8 +1697,6 @@ std::array UltraCircuitBuilder_::evaluate_non_nati this->get_variable(input.r[3]), }; constexpr FF LIMB_SHIFT = uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS; - constexpr FF LIMB_SHIFT_2 = uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); - constexpr FF LIMB_SHIFT_3 = uint256_t(1) << (3 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); constexpr FF LIMB_RSHIFT = FF(1) / FF(uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); constexpr FF LIMB_RSHIFT_2 = FF(1) / FF(uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS)); @@ -1722,35 +1720,6 @@ std::array UltraCircuitBuilder_::evaluate_non_nati const uint32_t hi_2_idx = this->add_variable(hi_2); const uint32_t hi_3_idx = this->add_variable(hi_3); - // Sometimes we have already applied range constraints on the quotient and remainder - if (range_constrain_quotient_and_remainder) { - // /** - // * r_prime - r_0 - r_1 * 2^b - r_2 * 2^2b - r_3 * 2^3b = 0 - // * - // * w_4_omega - w_4 + w_1(2^b) + w_2(2^2b) + w_3(2^3b) = 0 - // * - // **/ - create_big_add_gate( - { input.r[1], input.r[2], input.r[3], input.r[4], LIMB_SHIFT, LIMB_SHIFT_2, LIMB_SHIFT_3, -1, 0 }, true); - // TODO(https://github.com/AztecProtocol/barretenberg/issues/879): dummy necessary for preceeding big add gate - create_dummy_gate(blocks.arithmetic, this->zero_idx, this->zero_idx, this->zero_idx, input.r[0]); - range_constrain_two_limbs(input.r[0], input.r[1]); - range_constrain_two_limbs(input.r[2], input.r[3]); - - // /** - // * q_prime - q_0 - q_1 * 2^b - q_2 * 2^2b - q_3 * 2^3b = 0 - // * - // * w_4_omega - w_4 + w_1(2^b) + w_2(2^2b) + w_3(2^3b) = 0 - // * - // **/ - create_big_add_gate( - { input.q[1], input.q[2], input.q[3], input.q[4], LIMB_SHIFT, LIMB_SHIFT_2, LIMB_SHIFT_3, -1, 0 }, true); - // TODO(https://github.com/AztecProtocol/barretenberg/issues/879): dummy necessary for preceeding big add gate - create_dummy_gate(blocks.arithmetic, this->zero_idx, this->zero_idx, this->zero_idx, input.q[0]); - range_constrain_two_limbs(input.q[0], input.q[1]); - range_constrain_two_limbs(input.q[2], input.q[3]); - } - // TODO(https://github.com/AztecProtocol/barretenberg/issues/879): Originally this was a single arithmetic gate. // With trace sorting, we must add a dummy gate since the add gate would otherwise try to read into an aux gate that // has been sorted out of sequence. @@ -1834,12 +1803,12 @@ void UltraCircuitBuilder_::process_non_native_field_multiplicat { for (size_t i = 0; i < cached_partial_non_native_field_multiplications.size(); ++i) { auto& c = cached_partial_non_native_field_multiplications[i]; - for (size_t j = 0; j < 5; ++j) { + for (size_t j = 0; j < c.a.size(); ++j) { c.a[j] = this->real_variable_index[c.a[j]]; c.b[j] = this->real_variable_index[c.b[j]]; } } - cached_partial_non_native_field_multiplication::deduplicate(cached_partial_non_native_field_multiplications); + cached_partial_non_native_field_multiplication::deduplicate(cached_partial_non_native_field_multiplications, this); // iterate over the cached items and create constraints for (const auto& input : cached_partial_non_native_field_multiplications) { diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp index cdf460d035f..821339329fc 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp @@ -21,12 +21,11 @@ namespace bb { template struct non_native_field_witnesses { // first 4 array elements = limbs - // 5th element = prime basis limb - std::array a; - std::array b; - std::array q; - std::array r; - std::array neg_modulus; + std::array a; + std::array b; + std::array q; + std::array r; + std::array neg_modulus; FF modulus; }; @@ -196,31 +195,48 @@ class UltraCircuitBuilder_ : public CircuitBuilderBase a; - std::array b; - FF lo_0; - FF hi_0; - FF hi_1; + std::array a; + std::array b; + uint32_t lo_0; + uint32_t hi_0; + uint32_t hi_1; bool operator==(const cached_partial_non_native_field_multiplication& other) const { bool valid = true; - for (size_t i = 0; i < 5; ++i) { + for (size_t i = 0; i < 4; ++i) { valid = valid && (a[i] == other.a[i]); valid = valid && (b[i] == other.b[i]); } return valid; } - static void deduplicate(std::vector& vec) + /** + * @brief Dedupilcate cache entries which represent multiplication of the same witnesses + * + * @details While a and b witness vectors are the same, lo_0, hi_0 and hi_1 can vary, so we have to connect them + * or there is a vulnerability + * + * @param vec + * @param circuit_builder + */ + static void deduplicate(std::vector& vec, + UltraCircuitBuilder_* circuit_builder) { std::unordered_set> seen; std::vector uniqueVec; for (const auto& item : vec) { - if (seen.insert(item).second) { + auto [existing_element, not_in_set] = seen.insert(item); + // Memorize if not in set yet + if (not_in_set) { uniqueVec.push_back(item); + } else { + // If we already have a representative, we need to connect the outputs together + circuit_builder->assert_equal(item.lo_0, (*existing_element).lo_0); + circuit_builder->assert_equal(item.hi_0, (*existing_element).hi_0); + circuit_builder->assert_equal(item.hi_1, (*existing_element).hi_1); } } @@ -801,8 +817,7 @@ class UltraCircuitBuilder_ : public CircuitBuilderBase decompose_non_native_field_double_width_limb( const uint32_t limb_idx, const size_t num_limb_bits = (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS)); - std::array evaluate_non_native_field_multiplication( - const non_native_field_witnesses& input, const bool range_constrain_quotient_and_remainder = true); + std::array evaluate_non_native_field_multiplication(const non_native_field_witnesses& input); std::array queue_partial_non_native_field_multiplication(const non_native_field_witnesses& input); typedef std::pair scaled_witness; typedef std::tuple add_simple; diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/relation_correctness.test.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/relation_correctness.test.cpp index 2b9322ced97..ca13448d383 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/relation_correctness.test.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/relation_correctness.test.cpp @@ -1,8 +1,10 @@ +#include "barretenberg/common/thread.hpp" #include "barretenberg/honk/proof_system/permutation_library.hpp" #include "barretenberg/plonk_honk_shared/library/grand_product_library.hpp" #include "barretenberg/translator_vm/translator_flavor.hpp" #include +#include using namespace bb; /** @@ -311,8 +313,9 @@ TEST_F(TranslatorRelationCorrectnessTests, TranslatorExtraRelationsCorrectness) } // Fill in lagrange even polynomial - for (size_t i = 2; i < mini_circuit_size; i += 2) { - prover_polynomials.lagrange_even_in_minicircuit.at(i) = 1; + for (size_t i = 1; i < mini_circuit_size - 1; i += 2) { + prover_polynomials.lagrange_odd_in_minicircuit.at(i) = 1; + prover_polynomials.lagrange_even_in_minicircuit.at(i + 1) = 1; } constexpr size_t NUMBER_OF_POSSIBLE_OPCODES = 6; constexpr std::array possible_opcode_values = { 0, 1, 2, 3, 4, 8 }; @@ -323,6 +326,22 @@ TEST_F(TranslatorRelationCorrectnessTests, TranslatorExtraRelationsCorrectness) possible_opcode_values[static_cast(engine.get_random_uint8() % NUMBER_OF_POSSIBLE_OPCODES)]; } + std::unordered_set range_constraint_polynomial_ids; + for (auto& concatenation_group : prover_polynomial_ids.get_groups_to_be_concatenated()) { + for (auto& id : concatenation_group) { + range_constraint_polynomial_ids.insert(id); + } + } + + // Assign random values to the mini-circuit part of the range constraint polynomials + for (const auto& range_constraint_polynomial_id : range_constraint_polynomial_ids) { + parallel_for_range(mini_circuit_size - 2, [&](size_t start, size_t end) { + // We want to iterate from 1 to mini_circuit_size - 2 (inclusive) + for (size_t i = start + 1; i < end + 1; i++) { + polynomial_container[range_constraint_polynomial_id].at(i) = fr::random_element(); + } + }); + } // Initialize used lagrange polynomials prover_polynomials.lagrange_second.at(1) = 1; prover_polynomials.lagrange_second_to_last_in_minicircuit.at(mini_circuit_size - 2) = 1; @@ -352,6 +371,9 @@ TEST_F(TranslatorRelationCorrectnessTests, TranslatorExtraRelationsCorrectness) // Check that Accumulator Transfer relation is satisfied across each row of the prover polynomials check_relation>(circuit_size, prover_polynomials, params); + + // Check that Zero Constraint relation is satisfied across each row of the prover polynomials + check_relation>(circuit_size, prover_polynomials, params); } /** * @brief Test the correctness of TranslatorFlavor's Decomposition Relation diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_circuit_builder.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_circuit_builder.cpp index 9094e12a561..73a77c2c075 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_circuit_builder.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_circuit_builder.cpp @@ -577,12 +577,17 @@ TranslatorCircuitBuilder::AccumulationInput compute_witness_values_for_one_ecc_o Fr p_y_hi(0); // Split P.x and P.y into their representations in bn254 transcript - p_x_lo = Fr(uint256_t(ecc_op.base_point.x).slice(0, 2 * TranslatorCircuitBuilder::NUM_LIMB_BITS)); - p_x_hi = Fr(uint256_t(ecc_op.base_point.x) - .slice(2 * TranslatorCircuitBuilder::NUM_LIMB_BITS, 4 * TranslatorCircuitBuilder::NUM_LIMB_BITS)); - p_y_lo = Fr(uint256_t(ecc_op.base_point.y).slice(0, 2 * TranslatorCircuitBuilder::NUM_LIMB_BITS)); - p_y_hi = Fr(uint256_t(ecc_op.base_point.y) - .slice(2 * TranslatorCircuitBuilder::NUM_LIMB_BITS, 4 * TranslatorCircuitBuilder::NUM_LIMB_BITS)); + // if we have a point at infinity, set x/y to zero + // in the biggroup_goblin class we use `assert_equal` statements to validate + // the original in-circuit coordinate values are also zero + const auto [x_256, y_256] = ecc_op.get_base_point_standard_form(); + + p_x_lo = Fr(uint256_t(x_256).slice(0, 2 * TranslatorCircuitBuilder::NUM_LIMB_BITS)); + p_x_hi = Fr(uint256_t(x_256).slice(2 * TranslatorCircuitBuilder::NUM_LIMB_BITS, + 4 * TranslatorCircuitBuilder::NUM_LIMB_BITS)); + p_y_lo = Fr(uint256_t(y_256).slice(0, 2 * TranslatorCircuitBuilder::NUM_LIMB_BITS)); + p_y_hi = Fr(uint256_t(y_256).slice(2 * TranslatorCircuitBuilder::NUM_LIMB_BITS, + 4 * TranslatorCircuitBuilder::NUM_LIMB_BITS)); // Generate the full witness values return generate_witness_values(op, @@ -614,9 +619,9 @@ void TranslatorCircuitBuilder::feed_ecc_op_queue_into_circuit(std::shared_ptrget_raw_ops(); for (const auto& ecc_op : raw_ops) { op_accumulator = op_accumulator * x_inv + ecc_op.get_opcode_value(); - p_x_accumulator = p_x_accumulator * x_inv + ecc_op.base_point.x; - p_y_accumulator = p_y_accumulator * x_inv + ecc_op.base_point.y; + const auto [x_u256, y_u256] = ecc_op.get_base_point_standard_form(); + p_x_accumulator = p_x_accumulator * x_inv + x_u256; + p_y_accumulator = p_y_accumulator * x_inv + y_u256; z_1_accumulator = z_1_accumulator * x_inv + ecc_op.z1; z_2_accumulator = z_2_accumulator * x_inv + ecc_op.z2; } diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp index 5cb3f6a7003..0a3d495b890 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp @@ -90,7 +90,8 @@ class TranslatorFlavor { TranslatorOpcodeConstraintRelation, TranslatorAccumulatorTransferRelation, TranslatorDecompositionRelation, - TranslatorNonNativeFieldRelation>; + TranslatorNonNativeFieldRelation, + TranslatorZeroConstraintsRelation>; using Relations = Relations_; static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); @@ -109,7 +110,8 @@ class TranslatorFlavor { typename TranslatorOpcodeConstraintRelation::ZKSumcheckTupleOfUnivariatesOverSubrelations, typename TranslatorAccumulatorTransferRelation::ZKSumcheckTupleOfUnivariatesOverSubrelations, typename TranslatorDecompositionRelation::ZKSumcheckTupleOfUnivariatesOverSubrelations, - typename TranslatorNonNativeFieldRelation::ZKSumcheckTupleOfUnivariatesOverSubrelations>; + typename TranslatorNonNativeFieldRelation::ZKSumcheckTupleOfUnivariatesOverSubrelations, + typename TranslatorZeroConstraintsRelation::ZKSumcheckTupleOfUnivariatesOverSubrelations>; using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); /** diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp index 62dfe74c9c1..79f6406cbe6 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp @@ -756,22 +756,20 @@ TYPED_TEST(UltraHonkTests, non_native_field_multiplication) const auto split_into_limbs = [&](const uint512_t& input) { constexpr size_t NUM_BITS = 68; - std::array limbs; + std::array limbs; limbs[0] = input.slice(0, NUM_BITS).lo; limbs[1] = input.slice(NUM_BITS * 1, NUM_BITS * 2).lo; limbs[2] = input.slice(NUM_BITS * 2, NUM_BITS * 3).lo; limbs[3] = input.slice(NUM_BITS * 3, NUM_BITS * 4).lo; - limbs[4] = fr(input.lo); return limbs; }; - const auto get_limb_witness_indices = [&](const std::array& limbs) { - std::array limb_indices; + const auto get_limb_witness_indices = [&](const std::array& limbs) { + std::array limb_indices; limb_indices[0] = circuit_builder.add_variable(limbs[0]); limb_indices[1] = circuit_builder.add_variable(limbs[1]); limb_indices[2] = circuit_builder.add_variable(limbs[2]); limb_indices[3] = circuit_builder.add_variable(limbs[3]); - limb_indices[4] = circuit_builder.add_variable(limbs[4]); return limb_indices; }; const uint512_t BINARY_BASIS_MODULUS = uint512_t(1) << (68 * 4); diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp index 1f00b899d9a..4640e94585d 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp @@ -323,7 +323,7 @@ TEST_F(AvmExecutionTests, simpleInternalCall) "0D3D2518" // val 222111000 = 0xD3D2518 "0004" // dst_offset 4 + to_hex(OpCode::INTERNALCALL) + // opcode INTERNALCALL - "0004" // jmp_dest + "00000004" // jmp_dest + to_hex(OpCode::ADD_16) + // opcode ADD "00" // Indirect flag "0004" // addr a 4 @@ -351,7 +351,7 @@ TEST_F(AvmExecutionTests, simpleInternalCall) // INTERNALCALL EXPECT_THAT(instructions.at(1), AllOf(Field(&Instruction::op_code, OpCode::INTERNALCALL), - Field(&Instruction::operands, ElementsAre(VariantWith(4))))); + Field(&Instruction::operands, ElementsAre(VariantWith(4))))); // INTERNALRETURN EXPECT_EQ(instructions.at(5).op_code, OpCode::INTERNALRETURN); @@ -388,7 +388,7 @@ TEST_F(AvmExecutionTests, nestedInternalCalls) { auto internalCallInstructionHex = [](std::string const& dst_offset) { return to_hex(OpCode::INTERNALCALL) // opcode INTERNALCALL - + "00" + dst_offset; + + "000000" + dst_offset; }; auto setInstructionHex = [](std::string const& val, std::string const& dst_offset) { @@ -471,8 +471,8 @@ TEST_F(AvmExecutionTests, jumpAndCalldatacopy) "0000" // cd_offset "0001" // copy_size "000A" // dst_offset // M[10] = 13, M[11] = 156 - + to_hex(OpCode::JUMP_16) + // opcode JUMP - "0005" // jmp_dest (FDIV located at 3) + + to_hex(OpCode::JUMP_32) + // opcode JUMP + "00000005" // jmp_dest (FDIV located at 3) + to_hex(OpCode::SUB_8) + // opcode SUB "00" // Indirect flag "0B" // addr 11 @@ -507,8 +507,8 @@ TEST_F(AvmExecutionTests, jumpAndCalldatacopy) // JUMP EXPECT_THAT(instructions.at(3), - AllOf(Field(&Instruction::op_code, OpCode::JUMP_16), - Field(&Instruction::operands, ElementsAre(VariantWith(5))))); + AllOf(Field(&Instruction::op_code, OpCode::JUMP_32), + Field(&Instruction::operands, ElementsAre(VariantWith(5))))); std::vector returndata; ExecutionHints execution_hints; @@ -566,9 +566,9 @@ TEST_F(AvmExecutionTests, jumpiAndCalldatacopy) + to_hex(AvmMemoryTag::U16) + "14" // val 20 "65" // dst_offset 101 - + to_hex(OpCode::JUMPI_16) + // opcode JUMPI + + to_hex(OpCode::JUMPI_32) + // opcode JUMPI "00" // Indirect flag - "0006" // jmp_dest (MUL located at 6) + "00000006" // jmp_dest (MUL located at 6) "000A" // cond_offset 10 + to_hex(OpCode::ADD_16) + // opcode ADD "00" // Indirect flag @@ -596,9 +596,9 @@ TEST_F(AvmExecutionTests, jumpiAndCalldatacopy) // JUMPI EXPECT_THAT( instructions.at(4), - AllOf(Field(&Instruction::op_code, OpCode::JUMPI_16), + AllOf(Field(&Instruction::op_code, OpCode::JUMPI_32), Field(&Instruction::operands, - ElementsAre(VariantWith(0), VariantWith(6), VariantWith(10))))); + ElementsAre(VariantWith(0), VariantWith(6), VariantWith(10))))); std::vector returndata; ExecutionHints execution_hints; diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/deserialization.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/deserialization.cpp index 8e203151ded..a105982e1bd 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/deserialization.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/deserialization.cpp @@ -90,9 +90,9 @@ const std::unordered_map> OPCODE_WIRE_FORMAT = { OperandType::INDIRECT8, OperandType::UINT16, OperandType::UINT16, OperandType::UINT16 } }, // Machine State - Internal Control Flow - { OpCode::JUMP_16, { OperandType::UINT16 } }, - { OpCode::JUMPI_16, { OperandType::INDIRECT8, OperandType::UINT16, OperandType::UINT16 } }, - { OpCode::INTERNALCALL, { OperandType::UINT16 } }, + { OpCode::JUMP_32, { OperandType::UINT32 } }, + { OpCode::JUMPI_32, { OperandType::INDIRECT8, OperandType::UINT32, OperandType::UINT16 } }, + { OpCode::INTERNALCALL, { OperandType::UINT32 } }, { OpCode::INTERNALRETURN, {} }, // Machine State - Memory diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp index 5ab799bf4ef..cc03e9375fb 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp @@ -508,16 +508,16 @@ std::vector Execution::gen_trace(std::vector const& calldata, break; // Machine State - Internal Control Flow - case OpCode::JUMP_16: - trace_builder.op_jump(std::get(inst.operands.at(0))); + case OpCode::JUMP_32: + trace_builder.op_jump(std::get(inst.operands.at(0))); break; - case OpCode::JUMPI_16: + case OpCode::JUMPI_32: trace_builder.op_jumpi(std::get(inst.operands.at(0)), - std::get(inst.operands.at(1)), + std::get(inst.operands.at(1)), std::get(inst.operands.at(2))); break; case OpCode::INTERNALCALL: - trace_builder.op_internal_call(std::get(inst.operands.at(0))); + trace_builder.op_internal_call(std::get(inst.operands.at(0))); break; case OpCode::INTERNALRETURN: trace_builder.op_internal_return(); diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/fixed_gas.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/fixed_gas.cpp index 8fa808e4ad3..bb9eda6172e 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/fixed_gas.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/fixed_gas.cpp @@ -52,8 +52,8 @@ const std::unordered_map GAS_COST_TABLE = { { OpCode::CALLDATACOPY, make_cost(AVM_CALLDATACOPY_BASE_L2_GAS, 0, AVM_CALLDATACOPY_DYN_L2_GAS, 0) }, { OpCode::RETURNDATASIZE, make_cost(AVM_RETURNDATASIZE_BASE_L2_GAS, 0, 0, 0) }, { OpCode::RETURNDATACOPY, make_cost(AVM_RETURNDATACOPY_BASE_L2_GAS, 0, AVM_RETURNDATACOPY_DYN_L2_GAS, 0) }, - { OpCode::JUMP_16, make_cost(AVM_JUMP_BASE_L2_GAS, 0, 0, 0) }, - { OpCode::JUMPI_16, make_cost(AVM_JUMPI_BASE_L2_GAS, 0, 0, 0) }, + { OpCode::JUMP_32, make_cost(AVM_JUMP_BASE_L2_GAS, 0, 0, 0) }, + { OpCode::JUMPI_32, make_cost(AVM_JUMPI_BASE_L2_GAS, 0, 0, 0) }, { OpCode::INTERNALCALL, make_cost(AVM_INTERNALCALL_BASE_L2_GAS, 0, 0, 0) }, { OpCode::INTERNALRETURN, make_cost(AVM_INTERNALRETURN_BASE_L2_GAS, 0, 0, 0) }, { OpCode::SET_8, make_cost(AVM_SET_BASE_L2_GAS, 0, 0, 0) }, diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.cpp index d247fca4661..b77070e08bf 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.cpp @@ -95,10 +95,10 @@ std::string to_string(OpCode opcode) return "CALLDATACOPY"; // Machine State // Machine State - Internal Control Flow - case OpCode::JUMP_16: - return "JUMP_16"; - case OpCode::JUMPI_16: - return "JUMPI_16"; + case OpCode::JUMP_32: + return "JUMP_32"; + case OpCode::JUMPI_32: + return "JUMPI_32"; case OpCode::INTERNALCALL: return "INTERNALCALL"; case OpCode::INTERNALRETURN: diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.hpp index cc5318be696..d2ad53e4a26 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.hpp @@ -61,8 +61,8 @@ enum class OpCode : uint8_t { // Machine State // Machine State - Internal Control Flow - JUMP_16, - JUMPI_16, + JUMP_32, + JUMPI_32, INTERNALCALL, INTERNALRETURN, // Machine State - Memory diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp index a0aa672c22e..6eea5900967 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp @@ -1695,7 +1695,7 @@ void AvmTraceBuilder::op_jump(uint32_t jmp_dest, bool skip_gas) // Constrain gas cost if (!skip_gas) { - gas_trace_builder.constrain_gas(clk, OpCode::JUMP_16); + gas_trace_builder.constrain_gas(clk, OpCode::JUMP_32); } main_trace.push_back(Row{ @@ -1739,7 +1739,7 @@ void AvmTraceBuilder::op_jumpi(uint8_t indirect, uint32_t jmp_dest, uint32_t con uint32_t next_pc = !id_zero ? jmp_dest : pc + 1; // Constrain gas cost - gas_trace_builder.constrain_gas(clk, OpCode::JUMPI_16); + gas_trace_builder.constrain_gas(clk, OpCode::JUMPI_32); main_trace.push_back(Row{ .main_clk = clk, diff --git a/barretenberg/exports.json b/barretenberg/exports.json index d6819afe1e5..67c4854c607 100644 --- a/barretenberg/exports.json +++ b/barretenberg/exports.json @@ -505,6 +505,10 @@ "name": "constraint_system_buf", "type": "const uint8_t *" }, + { + "name": "recursive", + "type": "const bool *" + }, { "name": "honk_recursion", "type": "const bool *" @@ -563,6 +567,10 @@ { "name": "constraint_system_buf", "type": "const uint8_t *" + }, + { + "name": "recursive", + "type": "const bool *" } ], "outArgs": [], @@ -579,6 +587,10 @@ "name": "constraint_system_buf", "type": "const uint8_t *" }, + { + "name": "recursive", + "type": "const bool *" + }, { "name": "witness_buf", "type": "const uint8_t *" @@ -599,6 +611,10 @@ "name": "constraint_system_buf", "type": "const uint8_t *" }, + { + "name": "recursive", + "type": "const bool *" + }, { "name": "witness_buf", "type": "const uint8_t *" @@ -619,6 +635,10 @@ "name": "constraint_system_buf", "type": "const uint8_t *" }, + { + "name": "recursive", + "type": "const bool *" + }, { "name": "witness_buf", "type": "const uint8_t *" @@ -639,6 +659,10 @@ "name": "constraint_system_buf", "type": "const uint8_t *" }, + { + "name": "recursive", + "type": "const bool *" + }, { "name": "witness_buf", "type": "const uint8_t *" @@ -704,6 +728,10 @@ { "name": "acir_vec", "type": "const uint8_t *" + }, + { + "name": "recursive", + "type": "const bool *" } ], "outArgs": [ @@ -801,6 +829,10 @@ "name": "acir_vec", "type": "const uint8_t *" }, + { + "name": "recursive", + "type": "const bool *" + }, { "name": "witness_vec", "type": "const uint8_t *" @@ -840,6 +872,10 @@ { "name": "acir_vec", "type": "const uint8_t *" + }, + { + "name": "recursive", + "type": "const bool *" } ], "outArgs": [ @@ -882,4 +918,4 @@ ], "isAsync": false } -] +] \ No newline at end of file diff --git a/barretenberg/ts/CHANGELOG.md b/barretenberg/ts/CHANGELOG.md index 1c068c3e16e..aff114885f8 100644 --- a/barretenberg/ts/CHANGELOG.md +++ b/barretenberg/ts/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.62.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg.js-v0.61.0...barretenberg.js-v0.62.0) (2024-11-01) + + +### Features + +* Faster square roots ([#2694](https://github.com/AztecProtocol/aztec-packages/issues/2694)) ([722ec5c](https://github.com/AztecProtocol/aztec-packages/commit/722ec5c3dfdc2a5e467528ed94a25677f8800087)) + ## [0.61.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg.js-v0.60.0...barretenberg.js-v0.61.0) (2024-10-30) diff --git a/barretenberg/ts/package.json b/barretenberg/ts/package.json index 80bf1064212..160dd3f927a 100644 --- a/barretenberg/ts/package.json +++ b/barretenberg/ts/package.json @@ -1,7 +1,7 @@ { "name": "@aztec/bb.js", "packageManager": "yarn@1.22.22", - "version": "0.61.0", + "version": "0.62.0", "homepage": "https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg/ts", "license": "MIT", "type": "module", diff --git a/barretenberg/ts/src/barretenberg/backend.ts b/barretenberg/ts/src/barretenberg/backend.ts index 0919f030e5c..4657463be90 100644 --- a/barretenberg/ts/src/barretenberg/backend.ts +++ b/barretenberg/ts/src/barretenberg/backend.ts @@ -1,4 +1,4 @@ -import { BackendOptions, Barretenberg } from './index.js'; +import { BackendOptions, Barretenberg, CircuitOptions } from './index.js'; import { RawBuffer } from '../types/raw_buffer.js'; import { decompressSync as gunzip } from 'fflate'; import { @@ -21,22 +21,30 @@ export class UltraPlonkBackend { protected acirUncompressedBytecode: Uint8Array; - constructor(acirBytecode: string, protected options: BackendOptions = { threads: 1 }) { + constructor( + acirBytecode: string, + protected backendOptions: BackendOptions = { threads: 1 }, + protected circuitOptions: CircuitOptions = { recursive: false }, + ) { this.acirUncompressedBytecode = acirToUint8Array(acirBytecode); } /** @ignore */ async instantiate(): Promise { if (!this.api) { - const api = await Barretenberg.new(this.options); + const api = await Barretenberg.new(this.backendOptions); const honkRecursion = false; // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [_total, subgroupSize] = await api.acirGetCircuitSizes(this.acirUncompressedBytecode, honkRecursion); + const [_total, subgroupSize] = await api.acirGetCircuitSizes( + this.acirUncompressedBytecode, + this.circuitOptions.recursive, + honkRecursion, + ); await api.initSRSForCircuitSize(subgroupSize); this.acirComposer = await api.acirNewAcirComposer(subgroupSize); - await api.acirInitProvingKey(this.acirComposer, this.acirUncompressedBytecode); + await api.acirInitProvingKey(this.acirComposer, this.acirUncompressedBytecode, this.circuitOptions.recursive); this.api = api; } } @@ -47,6 +55,7 @@ export class UltraPlonkBackend { const proofWithPublicInputs = await this.api.acirCreateProof( this.acirComposer, this.acirUncompressedBytecode, + this.circuitOptions.recursive, gunzip(compressedWitness), ); @@ -69,8 +78,8 @@ export class UltraPlonkBackend { * Instead of passing the proof and verification key as a byte array, we pass them * as fields which makes it cheaper to verify in a circuit. * - * The proof that is passed here will have been created using a circuit - * that has the #[recursive] attribute on its `main` method. + * The proof that is passed here will have been created by passing the `recursive` + * parameter to a backend. * * The number of public inputs denotes how many public inputs are in the inner proof. * @@ -145,15 +154,19 @@ export class UltraHonkBackend { protected api!: Barretenberg; protected acirUncompressedBytecode: Uint8Array; - constructor(acirBytecode: string, protected options: BackendOptions = { threads: 1 }) { + constructor( + acirBytecode: string, + protected backendOptions: BackendOptions = { threads: 1 }, + protected circuitOptions: CircuitOptions = { recursive: false }, + ) { this.acirUncompressedBytecode = acirToUint8Array(acirBytecode); } /** @ignore */ async instantiate(): Promise { if (!this.api) { - const api = await Barretenberg.new(this.options); + const api = await Barretenberg.new(this.backendOptions); const honkRecursion = true; - await api.acirInitSRS(this.acirUncompressedBytecode, honkRecursion); + await api.acirInitSRS(this.acirUncompressedBytecode, this.circuitOptions.recursive, honkRecursion); // We don't init a proving key here in the Honk API // await api.acirInitProvingKey(this.acirComposer, this.acirUncompressedBytecode); @@ -165,6 +178,7 @@ export class UltraHonkBackend { await this.instantiate(); const proofWithPublicInputs = await this.api.acirProveUltraHonk( this.acirUncompressedBytecode, + this.circuitOptions.recursive, gunzip(compressedWitness), ); @@ -194,14 +208,14 @@ export class UltraHonkBackend { async verifyProof(proofData: ProofData): Promise { await this.instantiate(); const proof = reconstructHonkProof(flattenFieldsAsArray(proofData.publicInputs), proofData.proof); - const vkBuf = await this.api.acirWriteVkUltraHonk(this.acirUncompressedBytecode); + const vkBuf = await this.api.acirWriteVkUltraHonk(this.acirUncompressedBytecode, this.circuitOptions.recursive); return await this.api.acirVerifyUltraHonk(proof, new RawBuffer(vkBuf)); } async getVerificationKey(): Promise { await this.instantiate(); - return await this.api.acirWriteVkUltraHonk(this.acirUncompressedBytecode); + return await this.api.acirWriteVkUltraHonk(this.acirUncompressedBytecode, this.circuitOptions.recursive); } // TODO(https://github.com/noir-lang/noir/issues/5661): Update this to handle Honk recursive aggregation in the browser once it is ready in the backend itself @@ -222,7 +236,7 @@ export class UltraHonkBackend { // TODO: perhaps we should put this in the init function. Need to benchmark // TODO how long it takes. - const vkBuf = await this.api.acirWriteVkUltraHonk(this.acirUncompressedBytecode); + const vkBuf = await this.api.acirWriteVkUltraHonk(this.acirUncompressedBytecode, this.circuitOptions.recursive); const vk = await this.api.acirVkAsFieldsUltraHonk(vkBuf); return { diff --git a/barretenberg/ts/src/barretenberg/index.ts b/barretenberg/ts/src/barretenberg/index.ts index 5a0a14a2f9b..96b9b7d5c23 100644 --- a/barretenberg/ts/src/barretenberg/index.ts +++ b/barretenberg/ts/src/barretenberg/index.ts @@ -24,6 +24,11 @@ export type BackendOptions = { crsPath?: string; }; +export type CircuitOptions = { + /** @description Whether to produce SNARK friendly proofs */ + recursive: boolean; +}; + /** * The main class library consumers interact with. * It extends the generated api, and provides a static constructor "new" to compose components. @@ -61,9 +66,9 @@ export class Barretenberg extends BarretenbergApi { await this.srsInitSrs(new RawBuffer(crs.getG1Data()), crs.numPoints, new RawBuffer(crs.getG2Data())); } - async acirInitSRS(bytecode: Uint8Array, honkRecursion: boolean): Promise { + async acirInitSRS(bytecode: Uint8Array, recursive: boolean, honkRecursion: boolean): Promise { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [_total, subgroupSize] = await this.acirGetCircuitSizes(bytecode, honkRecursion); + const [_total, subgroupSize] = await this.acirGetCircuitSizes(bytecode, recursive, honkRecursion); return this.initSRSForCircuitSize(subgroupSize); } diff --git a/barretenberg/ts/src/barretenberg_api/index.ts b/barretenberg/ts/src/barretenberg_api/index.ts index d25b0adbc31..06290eda204 100644 --- a/barretenberg/ts/src/barretenberg_api/index.ts +++ b/barretenberg/ts/src/barretenberg_api/index.ts @@ -330,9 +330,10 @@ export class BarretenbergApi { async acirGetCircuitSizes( constraintSystemBuf: Uint8Array, + recursive: boolean, honkRecursion: boolean, - ): Promise<[number, number, number]> { - const inArgs = [constraintSystemBuf, honkRecursion].map(serializeBufferable); + ): Promise<[number, number]> { + const inArgs = [constraintSystemBuf, recursive, honkRecursion].map(serializeBufferable); const outTypes: OutputType[] = [NumberDeserializer(), NumberDeserializer()]; const result = await this.wasm.callWasmExport( 'acir_get_circuit_sizes', @@ -340,7 +341,7 @@ export class BarretenbergApi { outTypes.map(t => t.SIZE_IN_BYTES), ); const out = result.map((r, i) => outTypes[i].fromBuffer(r)); - return out as any; + return out as [number, number]; } async acirNewAcirComposer(sizeHint: number): Promise { @@ -367,8 +368,8 @@ export class BarretenbergApi { return; } - async acirInitProvingKey(acirComposerPtr: Ptr, constraintSystemBuf: Uint8Array): Promise { - const inArgs = [acirComposerPtr, constraintSystemBuf].map(serializeBufferable); + async acirInitProvingKey(acirComposerPtr: Ptr, constraintSystemBuf: Uint8Array, recursive: boolean): Promise { + const inArgs = [acirComposerPtr, constraintSystemBuf, recursive].map(serializeBufferable); const outTypes: OutputType[] = []; const result = await this.wasm.callWasmExport( 'acir_init_proving_key', @@ -382,9 +383,10 @@ export class BarretenbergApi { async acirCreateProof( acirComposerPtr: Ptr, constraintSystemBuf: Uint8Array, + recursive: boolean, witnessBuf: Uint8Array, ): Promise { - const inArgs = [acirComposerPtr, constraintSystemBuf, witnessBuf].map(serializeBufferable); + const inArgs = [acirComposerPtr, constraintSystemBuf, recursive, witnessBuf].map(serializeBufferable); const outTypes: OutputType[] = [BufferDeserializer()]; const result = await this.wasm.callWasmExport( 'acir_create_proof', @@ -395,8 +397,12 @@ export class BarretenbergApi { return out[0]; } - async acirProveAndVerifyUltraHonk(constraintSystemBuf: Uint8Array, witnessBuf: Uint8Array): Promise { - const inArgs = [constraintSystemBuf, witnessBuf].map(serializeBufferable); + async acirProveAndVerifyUltraHonk( + constraintSystemBuf: Uint8Array, + recursive: boolean, + witnessBuf: Uint8Array, + ): Promise { + const inArgs = [constraintSystemBuf, recursive, witnessBuf].map(serializeBufferable); const outTypes: OutputType[] = [BoolDeserializer()]; const result = await this.wasm.callWasmExport( 'acir_prove_and_verify_ultra_honk', @@ -407,8 +413,12 @@ export class BarretenbergApi { return out[0]; } - async acirProveAndVerifyMegaHonk(constraintSystemBuf: Uint8Array, witnessBuf: Uint8Array): Promise { - const inArgs = [constraintSystemBuf, witnessBuf].map(serializeBufferable); + async acirProveAndVerifyMegaHonk( + constraintSystemBuf: Uint8Array, + recursive: boolean, + witnessBuf: Uint8Array, + ): Promise { + const inArgs = [constraintSystemBuf, recursive, witnessBuf].map(serializeBufferable); const outTypes: OutputType[] = [BoolDeserializer()]; const result = await this.wasm.callWasmExport( 'acir_prove_and_verify_mega_honk', @@ -419,8 +429,12 @@ export class BarretenbergApi { return out[0]; } - async acirFoldAndVerifyProgramStack(constraintSystemBuf: Uint8Array, witnessBuf: Uint8Array): Promise { - const inArgs = [constraintSystemBuf, witnessBuf].map(serializeBufferable); + async acirFoldAndVerifyProgramStack( + constraintSystemBuf: Uint8Array, + recursive: boolean, + witnessBuf: Uint8Array, + ): Promise { + const inArgs = [constraintSystemBuf, recursive, witnessBuf].map(serializeBufferable); const outTypes: OutputType[] = [BoolDeserializer()]; const result = await this.wasm.callWasmExport( 'acir_fold_and_verify_program_stack', @@ -467,8 +481,8 @@ export class BarretenbergApi { return out[0]; } - async acirGetProvingKey(acirComposerPtr: Ptr, acirVec: Uint8Array): Promise { - const inArgs = [acirComposerPtr, acirVec].map(serializeBufferable); + async acirGetProvingKey(acirComposerPtr: Ptr, acirVec: Uint8Array, recursive: boolean): Promise { + const inArgs = [acirComposerPtr, acirVec, recursive].map(serializeBufferable); const outTypes: OutputType[] = [BufferDeserializer()]; const result = await this.wasm.callWasmExport( 'acir_get_proving_key', @@ -531,8 +545,8 @@ export class BarretenbergApi { return out as any; } - async acirProveUltraHonk(acirVec: Uint8Array, witnessVec: Uint8Array): Promise { - const inArgs = [acirVec, witnessVec].map(serializeBufferable); + async acirProveUltraHonk(acirVec: Uint8Array, recursive: boolean, witnessVec: Uint8Array): Promise { + const inArgs = [acirVec, recursive, witnessVec].map(serializeBufferable); const outTypes: OutputType[] = [BufferDeserializer()]; const result = await this.wasm.callWasmExport( 'acir_prove_ultra_honk', @@ -555,8 +569,8 @@ export class BarretenbergApi { return out[0]; } - async acirWriteVkUltraHonk(acirVec: Uint8Array): Promise { - const inArgs = [acirVec].map(serializeBufferable); + async acirWriteVkUltraHonk(acirVec: Uint8Array, recursive: boolean): Promise { + const inArgs = [acirVec, recursive].map(serializeBufferable); const outTypes: OutputType[] = [BufferDeserializer()]; const result = await this.wasm.callWasmExport( 'acir_write_vk_ultra_honk', @@ -907,8 +921,12 @@ export class BarretenbergApiSync { return; } - acirGetCircuitSizes(constraintSystemBuf: Uint8Array, honkRecursion: boolean): [number, number, number] { - const inArgs = [constraintSystemBuf, honkRecursion].map(serializeBufferable); + acirGetCircuitSizes( + constraintSystemBuf: Uint8Array, + recursive: boolean, + honkRecursion: boolean, + ): [number, number, number] { + const inArgs = [constraintSystemBuf, recursive, honkRecursion].map(serializeBufferable); const outTypes: OutputType[] = [NumberDeserializer(), NumberDeserializer()]; const result = this.wasm.callWasmExport( 'acir_get_circuit_sizes', @@ -943,8 +961,8 @@ export class BarretenbergApiSync { return; } - acirInitProvingKey(acirComposerPtr: Ptr, constraintSystemBuf: Uint8Array): void { - const inArgs = [acirComposerPtr, constraintSystemBuf].map(serializeBufferable); + acirInitProvingKey(acirComposerPtr: Ptr, constraintSystemBuf: Uint8Array, recursive: boolean): void { + const inArgs = [acirComposerPtr, constraintSystemBuf, recursive].map(serializeBufferable); const outTypes: OutputType[] = []; const result = this.wasm.callWasmExport( 'acir_init_proving_key', @@ -955,8 +973,13 @@ export class BarretenbergApiSync { return; } - acirCreateProof(acirComposerPtr: Ptr, constraintSystemBuf: Uint8Array, witnessBuf: Uint8Array): Uint8Array { - const inArgs = [acirComposerPtr, constraintSystemBuf, witnessBuf].map(serializeBufferable); + acirCreateProof( + acirComposerPtr: Ptr, + constraintSystemBuf: Uint8Array, + recursive: boolean, + witnessBuf: Uint8Array, + ): Uint8Array { + const inArgs = [acirComposerPtr, constraintSystemBuf, recursive, witnessBuf].map(serializeBufferable); const outTypes: OutputType[] = [BufferDeserializer()]; const result = this.wasm.callWasmExport( 'acir_create_proof', @@ -967,8 +990,8 @@ export class BarretenbergApiSync { return out[0]; } - acirProveAndVerifyUltraHonk(constraintSystemBuf: Uint8Array, witnessBuf: Uint8Array): boolean { - const inArgs = [constraintSystemBuf, witnessBuf].map(serializeBufferable); + acirProveAndVerifyUltraHonk(constraintSystemBuf: Uint8Array, recursive: boolean, witnessBuf: Uint8Array): boolean { + const inArgs = [constraintSystemBuf, recursive, witnessBuf].map(serializeBufferable); const outTypes: OutputType[] = [BoolDeserializer()]; const result = this.wasm.callWasmExport( 'acir_prove_and_verify_ultra_honk', @@ -979,8 +1002,8 @@ export class BarretenbergApiSync { return out[0]; } - acirProveAndVerifyMegaHonk(constraintSystemBuf: Uint8Array, witnessBuf: Uint8Array): boolean { - const inArgs = [constraintSystemBuf, witnessBuf].map(serializeBufferable); + acirProveAndVerifyMegaHonk(constraintSystemBuf: Uint8Array, recursive: boolean, witnessBuf: Uint8Array): boolean { + const inArgs = [constraintSystemBuf, recursive, witnessBuf].map(serializeBufferable); const outTypes: OutputType[] = [BoolDeserializer()]; const result = this.wasm.callWasmExport( 'acir_prove_and_verify_mega_honk', @@ -991,8 +1014,8 @@ export class BarretenbergApiSync { return out[0]; } - acirFoldAndVerifyProgramStack(constraintSystemBuf: Uint8Array, witnessBuf: Uint8Array): boolean { - const inArgs = [constraintSystemBuf, witnessBuf].map(serializeBufferable); + acirFoldAndVerifyProgramStack(constraintSystemBuf: Uint8Array, recursive: boolean, witnessBuf: Uint8Array): boolean { + const inArgs = [constraintSystemBuf, recursive, witnessBuf].map(serializeBufferable); const outTypes: OutputType[] = [BoolDeserializer()]; const result = this.wasm.callWasmExport( 'acir_fold_and_verify_program_stack', @@ -1039,8 +1062,8 @@ export class BarretenbergApiSync { return out[0]; } - acirGetProvingKey(acirComposerPtr: Ptr, acirVec: Uint8Array): Uint8Array { - const inArgs = [acirComposerPtr, acirVec].map(serializeBufferable); + acirGetProvingKey(acirComposerPtr: Ptr, acirVec: Uint8Array, recursive: boolean): Uint8Array { + const inArgs = [acirComposerPtr, acirVec, recursive].map(serializeBufferable); const outTypes: OutputType[] = [BufferDeserializer()]; const result = this.wasm.callWasmExport( 'acir_get_proving_key', @@ -1099,8 +1122,8 @@ export class BarretenbergApiSync { return out as any; } - acirProveUltraHonk(acirVec: Uint8Array, witnessVec: Uint8Array): Uint8Array { - const inArgs = [acirVec, witnessVec].map(serializeBufferable); + acirProveUltraHonk(acirVec: Uint8Array, recursive: boolean, witnessVec: Uint8Array): Uint8Array { + const inArgs = [acirVec, recursive, witnessVec].map(serializeBufferable); const outTypes: OutputType[] = [BufferDeserializer()]; const result = this.wasm.callWasmExport( 'acir_prove_ultra_honk', @@ -1123,8 +1146,8 @@ export class BarretenbergApiSync { return out[0]; } - acirWriteVkUltraHonk(acirVec: Uint8Array): Uint8Array { - const inArgs = [acirVec].map(serializeBufferable); + acirWriteVkUltraHonk(acirVec: Uint8Array, recursive: boolean): Uint8Array { + const inArgs = [acirVec, recursive].map(serializeBufferable); const outTypes: OutputType[] = [BufferDeserializer()]; const result = this.wasm.callWasmExport( 'acir_write_vk_ultra_honk', diff --git a/barretenberg/ts/src/barretenberg_wasm/barretenberg_wasm_main/index.ts b/barretenberg/ts/src/barretenberg_wasm/barretenberg_wasm_main/index.ts index 1be981669ae..6dc26bc0097 100644 --- a/barretenberg/ts/src/barretenberg_wasm/barretenberg_wasm_main/index.ts +++ b/barretenberg/ts/src/barretenberg_wasm/barretenberg_wasm_main/index.ts @@ -32,7 +32,7 @@ export class BarretenbergWasmMain extends BarretenbergWasmBase { module: WebAssembly.Module, threads = Math.min(getNumCpu(), BarretenbergWasmMain.MAX_THREADS), logger: (msg: string) => void = debug, - initial = 30, + initial = 31, maximum = 2 ** 16, ) { this.logger = logger; diff --git a/barretenberg/ts/src/main.ts b/barretenberg/ts/src/main.ts index 389f554be9e..1d3af06dd2e 100755 --- a/barretenberg/ts/src/main.ts +++ b/barretenberg/ts/src/main.ts @@ -34,8 +34,8 @@ function getBytecode(bytecodePath: string) { } // TODO(https://github.com/AztecProtocol/barretenberg/issues/1126): split this into separate Plonk and Honk functions as their gate count differs -async function getGatesUltra(bytecodePath: string, honkRecursion: boolean, api: Barretenberg) { - const { total } = await computeCircuitSize(bytecodePath, honkRecursion, api); +async function getGatesUltra(bytecodePath: string, recursive: boolean, honkRecursion: boolean, api: Barretenberg) { + const { total } = await computeCircuitSize(bytecodePath, recursive, honkRecursion, api); return total; } @@ -45,18 +45,24 @@ function getWitness(witnessPath: string) { return decompressed; } -async function computeCircuitSize(bytecodePath: string, honkRecursion: boolean, api: Barretenberg) { +async function computeCircuitSize(bytecodePath: string, recursive: boolean, honkRecursion: boolean, api: Barretenberg) { debug(`computing circuit size...`); const bytecode = getBytecode(bytecodePath); - const [total, subgroup] = await api.acirGetCircuitSizes(bytecode, honkRecursion); + const [total, subgroup] = await api.acirGetCircuitSizes(bytecode, recursive, honkRecursion); return { total, subgroup }; } -async function initUltraPlonk(bytecodePath: string, crsPath: string, subgroupSizeOverride = -1, honkRecursion = false) { +async function initUltraPlonk( + bytecodePath: string, + recursive: boolean, + crsPath: string, + subgroupSizeOverride = -1, + honkRecursion = false, +) { const api = await Barretenberg.new({ threads }); // TODO(https://github.com/AztecProtocol/barretenberg/issues/1126): use specific UltraPlonk function - const circuitSize = await getGatesUltra(bytecodePath, honkRecursion, api); + const circuitSize = await getGatesUltra(bytecodePath, recursive, honkRecursion, api); // TODO(https://github.com/AztecProtocol/barretenberg/issues/811): remove subgroupSizeOverride hack for goblin const subgroupSize = Math.max(subgroupSizeOverride, Math.pow(2, Math.ceil(Math.log2(circuitSize)))); @@ -80,11 +86,11 @@ async function initUltraPlonk(bytecodePath: string, crsPath: string, subgroupSiz return { api, acirComposer, circuitSize, subgroupSize }; } -async function initUltraHonk(bytecodePath: string, crsPath: string) { +async function initUltraHonk(bytecodePath: string, recursive: boolean, crsPath: string) { const api = await Barretenberg.new({ threads }); // TODO(https://github.com/AztecProtocol/barretenberg/issues/1126): use specific UltraHonk function - const circuitSize = await getGatesUltra(bytecodePath, /*honkRecursion=*/ true, api); + const circuitSize = await getGatesUltra(bytecodePath, recursive, /*honkRecursion=*/ true, api); // TODO(https://github.com/AztecProtocol/barretenberg/issues/811): remove subgroupSizeOverride hack for goblin const dyadicCircuitSize = Math.pow(2, Math.ceil(Math.log2(circuitSize))); @@ -99,7 +105,7 @@ async function initUltraHonk(bytecodePath: string, crsPath: string) { return { api, circuitSize, dyadicCircuitSize }; } -async function initClientIVC(bytecodePath: string, crsPath: string) { +async function initClientIVC(bytecodePath: string, recursive: boolean, crsPath: string) { const api = await Barretenberg.new({ threads }); debug('loading BN254 and Grumpkin crs...'); @@ -126,24 +132,24 @@ async function initLite() { return { api, acirComposer }; } -export async function proveAndVerify(bytecodePath: string, witnessPath: string, crsPath: string) { +export async function proveAndVerify(bytecodePath: string, recursive: boolean, witnessPath: string, crsPath: string) { /* eslint-disable camelcase */ const acir_test = path.basename(process.cwd()); - const { api, acirComposer, circuitSize, subgroupSize } = await initUltraPlonk(bytecodePath, crsPath); + const { api, acirComposer, circuitSize, subgroupSize } = await initUltraPlonk(bytecodePath, recursive, crsPath); try { debug(`creating proof...`); const bytecode = getBytecode(bytecodePath); const witness = getWitness(witnessPath); const pkTimer = new Timer(); - await api.acirInitProvingKey(acirComposer, bytecode); + await api.acirInitProvingKey(acirComposer, bytecode, recursive); writeBenchmark('pk_construction_time', pkTimer.ms(), { acir_test, threads }); writeBenchmark('gate_count', circuitSize, { acir_test, threads }); writeBenchmark('subgroup_size', subgroupSize, { acir_test, threads }); const proofTimer = new Timer(); - const proof = await api.acirCreateProof(acirComposer, bytecode, witness); + const proof = await api.acirCreateProof(acirComposer, bytecode, recursive, witness); writeBenchmark('proof_construction_time', proofTimer.ms(), { acir_test, threads }); debug(`verifying...`); @@ -156,14 +162,19 @@ export async function proveAndVerify(bytecodePath: string, witnessPath: string, /* eslint-enable camelcase */ } -export async function proveAndVerifyUltraHonk(bytecodePath: string, witnessPath: string, crsPath: string) { +export async function proveAndVerifyUltraHonk( + bytecodePath: string, + recursive: boolean, + witnessPath: string, + crsPath: string, +) { /* eslint-disable camelcase */ - const { api } = await initUltraHonk(bytecodePath, crsPath); + const { api } = await initUltraHonk(bytecodePath, false, crsPath); try { const bytecode = getBytecode(bytecodePath); const witness = getWitness(witnessPath); - const verified = await api.acirProveAndVerifyUltraHonk(bytecode, witness); + const verified = await api.acirProveAndVerifyUltraHonk(bytecode, recursive, witness); return verified; } finally { await api.destroy(); @@ -171,14 +182,19 @@ export async function proveAndVerifyUltraHonk(bytecodePath: string, witnessPath: /* eslint-enable camelcase */ } -export async function proveAndVerifyMegaHonk(bytecodePath: string, witnessPath: string, crsPath: string) { +export async function proveAndVerifyMegaHonk( + bytecodePath: string, + recursive: boolean, + witnessPath: string, + crsPath: string, +) { /* eslint-disable camelcase */ - const { api } = await initUltraPlonk(bytecodePath, crsPath); + const { api } = await initUltraPlonk(bytecodePath, false, crsPath); try { const bytecode = getBytecode(bytecodePath); const witness = getWitness(witnessPath); - const verified = await api.acirProveAndVerifyMegaHonk(bytecode, witness); + const verified = await api.acirProveAndVerifyMegaHonk(bytecode, recursive, witness); return verified; } finally { await api.destroy(); @@ -186,14 +202,19 @@ export async function proveAndVerifyMegaHonk(bytecodePath: string, witnessPath: /* eslint-enable camelcase */ } -export async function foldAndVerifyProgram(bytecodePath: string, witnessPath: string, crsPath: string) { +export async function foldAndVerifyProgram( + bytecodePath: string, + recursive: boolean, + witnessPath: string, + crsPath: string, +) { /* eslint-disable camelcase */ - const { api } = await initClientIVC(bytecodePath, crsPath); + const { api } = await initClientIVC(bytecodePath, recursive, crsPath); try { const bytecode = getBytecode(bytecodePath); const witness = getWitness(witnessPath); - const verified = await api.acirFoldAndVerifyProgramStack(bytecode, witness); + const verified = await api.acirFoldAndVerifyProgramStack(bytecode, recursive, witness); debug(`verified: ${verified}`); return verified; } finally { @@ -202,13 +223,19 @@ export async function foldAndVerifyProgram(bytecodePath: string, witnessPath: st /* eslint-enable camelcase */ } -export async function prove(bytecodePath: string, witnessPath: string, crsPath: string, outputPath: string) { - const { api, acirComposer } = await initUltraPlonk(bytecodePath, crsPath); +export async function prove( + bytecodePath: string, + recursive: boolean, + witnessPath: string, + crsPath: string, + outputPath: string, +) { + const { api, acirComposer } = await initUltraPlonk(bytecodePath, recursive, crsPath); try { debug(`creating proof...`); const bytecode = getBytecode(bytecodePath); const witness = getWitness(witnessPath); - const proof = await api.acirCreateProof(acirComposer, bytecode, witness); + const proof = await api.acirCreateProof(acirComposer, bytecode, recursive, witness); debug(`done.`); if (outputPath === '-') { @@ -223,10 +250,10 @@ export async function prove(bytecodePath: string, witnessPath: string, crsPath: } } -export async function gateCountUltra(bytecodePath: string, honkRecursion: boolean) { +export async function gateCountUltra(bytecodePath: string, recursive: boolean, honkRecursion: boolean) { const api = await Barretenberg.new({ threads: 1 }); try { - const numberOfGates = await getGatesUltra(bytecodePath, honkRecursion, api); + const numberOfGates = await getGatesUltra(bytecodePath, recursive, honkRecursion, api); debug(`number of gates: : ${numberOfGates}`); // Create an 8-byte buffer and write the number into it. // Writing number directly to stdout will result in a variable sized @@ -270,12 +297,12 @@ export async function contract(outputPath: string, vkPath: string) { } } -export async function writeVk(bytecodePath: string, crsPath: string, outputPath: string) { - const { api, acirComposer } = await initUltraPlonk(bytecodePath, crsPath); +export async function writeVk(bytecodePath: string, recursive: boolean, crsPath: string, outputPath: string) { + const { api, acirComposer } = await initUltraPlonk(bytecodePath, recursive, crsPath); try { debug('initing proving key...'); const bytecode = getBytecode(bytecodePath); - await api.acirInitProvingKey(acirComposer, bytecode); + await api.acirInitProvingKey(acirComposer, bytecode, recursive); debug('initing verification key...'); const vk = await api.acirGetVerificationKey(acirComposer); @@ -292,12 +319,12 @@ export async function writeVk(bytecodePath: string, crsPath: string, outputPath: } } -export async function writePk(bytecodePath: string, crsPath: string, outputPath: string) { - const { api, acirComposer } = await initUltraPlonk(bytecodePath, crsPath); +export async function writePk(bytecodePath: string, recursive: boolean, crsPath: string, outputPath: string) { + const { api, acirComposer } = await initUltraPlonk(bytecodePath, recursive, crsPath); try { debug('initing proving key...'); const bytecode = getBytecode(bytecodePath); - const pk = await api.acirGetProvingKey(acirComposer, bytecode); + const pk = await api.acirGetProvingKey(acirComposer, bytecode, recursive); if (outputPath === '-') { process.stdout.write(pk); @@ -362,13 +389,19 @@ export async function vkAsFields(vkPath: string, vkeyOutputPath: string) { } } -export async function proveUltraHonk(bytecodePath: string, witnessPath: string, crsPath: string, outputPath: string) { - const { api } = await initUltraHonk(bytecodePath, crsPath); +export async function proveUltraHonk( + bytecodePath: string, + recursive: boolean, + witnessPath: string, + crsPath: string, + outputPath: string, +) { + const { api } = await initUltraHonk(bytecodePath, recursive, crsPath); try { debug(`creating proof...`); const bytecode = getBytecode(bytecodePath); const witness = getWitness(witnessPath); - const proof = await api.acirProveUltraHonk(bytecode, witness); + const proof = await api.acirProveUltraHonk(bytecode, recursive, witness); debug(`done.`); if (outputPath === '-') { @@ -383,12 +416,12 @@ export async function proveUltraHonk(bytecodePath: string, witnessPath: string, } } -export async function writeVkUltraHonk(bytecodePath: string, crsPath: string, outputPath: string) { - const { api } = await initUltraHonk(bytecodePath, crsPath); +export async function writeVkUltraHonk(bytecodePath: string, recursive: boolean, crsPath: string, outputPath: string) { + const { api } = await initUltraHonk(bytecodePath, recursive, crsPath); try { const bytecode = getBytecode(bytecodePath); debug('initing verification key...'); - const vk = await api.acirWriteVkUltraHonk(bytecode); + const vk = await api.acirWriteVkUltraHonk(bytecode, recursive); if (outputPath === '-') { process.stdout.write(vk); @@ -471,10 +504,11 @@ program .command('prove_and_verify') .description('Generate a proof and verify it. Process exits with success or failure code.') .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/program.json') + .option('-r, --recursive', 'Whether to use a SNARK friendly proof', false) .option('-w, --witness-path ', 'Specify the witness path', './target/witness.gz') - .action(async ({ bytecodePath, witnessPath, crsPath }) => { + .action(async ({ bytecodePath, recursive, witnessPath, crsPath }) => { handleGlobalOptions(); - const result = await proveAndVerify(bytecodePath, witnessPath, crsPath); + const result = await proveAndVerify(bytecodePath, recursive, witnessPath, crsPath); process.exit(result ? 0 : 1); }); @@ -482,10 +516,11 @@ program .command('prove_and_verify_ultra_honk') .description('Generate an UltraHonk proof and verify it. Process exits with success or failure code.') .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/program.json') + .option('-r, --recursive', 'Whether to use a SNARK friendly proof', false) .option('-w, --witness-path ', 'Specify the witness path', './target/witness.gz') - .action(async ({ bytecodePath, witnessPath, crsPath }) => { + .action(async ({ bytecodePath, recursive, witnessPath, crsPath }) => { handleGlobalOptions(); - const result = await proveAndVerifyUltraHonk(bytecodePath, witnessPath, crsPath); + const result = await proveAndVerifyUltraHonk(bytecodePath, recursive, witnessPath, crsPath); process.exit(result ? 0 : 1); }); @@ -493,10 +528,11 @@ program .command('prove_and_verify_mega_honk') .description('Generate a MegaHonk proof and verify it. Process exits with success or failure code.') .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/program.json') + .option('-r, --recursive', 'Whether to use a SNARK friendly proof', false) .option('-w, --witness-path ', 'Specify the witness path', './target/witness.gz') - .action(async ({ bytecodePath, witnessPath, crsPath }) => { + .action(async ({ bytecodePath, recursive, witnessPath, crsPath }) => { handleGlobalOptions(); - const result = await proveAndVerifyMegaHonk(bytecodePath, witnessPath, crsPath); + const result = await proveAndVerifyMegaHonk(bytecodePath, recursive, witnessPath, crsPath); process.exit(result ? 0 : 1); }); @@ -504,10 +540,11 @@ program .command('fold_and_verify_program') .description('Accumulate a set of circuits using ClientIvc then verify. Process exits with success or failure code.') .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/program.json') + .option('-r, --recursive', 'Create a SNARK friendly proof', false) .option('-w, --witness-path ', 'Specify the witness path', './target/witness.gz') - .action(async ({ bytecodePath, witnessPath, crsPath }) => { + .action(async ({ bytecodePath, recursive, witnessPath, crsPath }) => { handleGlobalOptions(); - const result = await foldAndVerifyProgram(bytecodePath, witnessPath, crsPath); + const result = await foldAndVerifyProgram(bytecodePath, recursive, witnessPath, crsPath); process.exit(result ? 0 : 1); }); @@ -515,21 +552,23 @@ program .command('prove') .description('Generate a proof and write it to a file.') .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/program.json') + .option('-r, --recursive', 'Create a SNARK friendly proof', false) .option('-w, --witness-path ', 'Specify the witness path', './target/witness.gz') .option('-o, --output-path ', 'Specify the proof output path', './proofs/proof') - .action(async ({ bytecodePath, witnessPath, outputPath, crsPath }) => { + .action(async ({ bytecodePath, recursive, witnessPath, outputPath, crsPath }) => { handleGlobalOptions(); - await prove(bytecodePath, witnessPath, crsPath, outputPath); + await prove(bytecodePath, recursive, witnessPath, crsPath, outputPath); }); program .command('gates') .description('Print Ultra Builder gate count to standard output.') .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/program.json') + .option('-r, --recursive', 'Create a SNARK friendly proof', false) .option('-hr, --honk-recursion', 'Specify whether to use UltraHonk recursion', false) - .action(async ({ bytecodePath: bytecodePath, honkRecursion: honkRecursion }) => { + .action(async ({ bytecodePath, recursive, honkRecursion: honkRecursion }) => { handleGlobalOptions(); - await gateCountUltra(bytecodePath, honkRecursion); + await gateCountUltra(bytecodePath, recursive, honkRecursion); }); program @@ -558,20 +597,22 @@ program .command('write_vk') .description('Output verification key.') .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/program.json') + .option('-r, --recursive', 'Create a SNARK friendly proof', false) .option('-o, --output-path ', 'Specify the path to write the key') - .action(async ({ bytecodePath, outputPath, crsPath }) => { + .action(async ({ bytecodePath, recursive, outputPath, crsPath }) => { handleGlobalOptions(); - await writeVk(bytecodePath, crsPath, outputPath); + await writeVk(bytecodePath, recursive, crsPath, outputPath); }); program .command('write_pk') .description('Output proving key.') .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/program.json') + .option('-r, --recursive', 'Create a SNARK friendly proof', false) .requiredOption('-o, --output-path ', 'Specify the path to write the key') - .action(async ({ bytecodePath, outputPath, crsPath }) => { + .action(async ({ bytecodePath, recursive, outputPath, crsPath }) => { handleGlobalOptions(); - await writePk(bytecodePath, crsPath, outputPath); + await writePk(bytecodePath, recursive, crsPath, outputPath); }); program @@ -599,21 +640,23 @@ program .command('prove_ultra_honk') .description('Generate a proof and write it to a file.') .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/program.json') + .option('-r, --recursive', 'Create a SNARK friendly proof', false) .option('-w, --witness-path ', 'Specify the witness path', './target/witness.gz') .option('-o, --output-path ', 'Specify the proof output path', './proofs/proof') - .action(async ({ bytecodePath, witnessPath, outputPath, crsPath }) => { + .action(async ({ bytecodePath, recursive, witnessPath, outputPath, crsPath }) => { handleGlobalOptions(); - await proveUltraHonk(bytecodePath, witnessPath, crsPath, outputPath); + await proveUltraHonk(bytecodePath, recursive, witnessPath, crsPath, outputPath); }); program .command('write_vk_ultra_honk') .description('Output verification key.') .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/program.json') + .option('-r, --recursive', 'Create a SNARK friendly proof', false) .requiredOption('-o, --output-path ', 'Specify the path to write the key') - .action(async ({ bytecodePath, outputPath, crsPath }) => { + .action(async ({ bytecodePath, recursive, outputPath, crsPath }) => { handleGlobalOptions(); - await writeVkUltraHonk(bytecodePath, crsPath, outputPath); + await writeVkUltraHonk(bytecodePath, recursive, crsPath, outputPath); }); program diff --git a/bootstrap.sh b/bootstrap.sh index c1e00678ce4..5c0f807af75 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -74,7 +74,8 @@ function check_toolchains { if ! $tool --version 2> /dev/null | grep 25f24e6 > /dev/null; then encourage_dev_container echo "$tool not in PATH or incorrect version (requires 25f24e677a6a32a62512ad4f561995589ac2c7dc)." - echo "Installation: https://book.getfoundry.sh/getting-started/installation (requires rust 1.75)" + echo "Installation: https://book.getfoundry.sh/getting-started/installation (requires rust 1.79+)" + echo " rustup toolchain install 1.79" echo " curl -L https://foundry.paradigm.xyz | bash" echo " foundryup -b 25f24e677a6a32a62512ad4f561995589ac2c7dc" exit 1 diff --git a/boxes/boxes/react/index.html b/boxes/boxes/react/index.html index 7e9084bc121..46f1cbe7d4a 100644 --- a/boxes/boxes/react/index.html +++ b/boxes/boxes/react/index.html @@ -4,12 +4,7 @@ Private Token Noir Smart Contract - - -

- - - + + +
+ + diff --git a/boxes/boxes/react/src/contracts/src/main.nr b/boxes/boxes/react/src/contracts/src/main.nr index ed7cb86ea80..f4924981e8e 100644 --- a/boxes/boxes/react/src/contracts/src/main.nr +++ b/boxes/boxes/react/src/contracts/src/main.nr @@ -26,7 +26,7 @@ contract BoxReact { let mut new_number = ValueNote::new(number, owner); let owner_ovpk_m = get_public_keys(owner).ovpk_m; - numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_note(&mut context, owner_ovpk_m, owner)); + numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_note(&mut context, owner_ovpk_m, owner, context.msg_sender())); } #[private] @@ -38,7 +38,7 @@ contract BoxReact { let mut new_number = ValueNote::new(number, owner); let owner_ovpk_m = get_public_keys(owner).ovpk_m; - numbers.at(owner).replace(&mut new_number).emit(encode_and_encrypt_note(&mut context, owner_ovpk_m, owner)); + numbers.at(owner).replace(&mut new_number).emit(encode_and_encrypt_note(&mut context, owner_ovpk_m, owner, context.msg_sender())); } unconstrained fn getNumber(owner: AztecAddress) -> pub ValueNote { diff --git a/boxes/boxes/vanilla/src/contracts/src/main.nr b/boxes/boxes/vanilla/src/contracts/src/main.nr index 98c205b85fd..c27e8f81a74 100644 --- a/boxes/boxes/vanilla/src/contracts/src/main.nr +++ b/boxes/boxes/vanilla/src/contracts/src/main.nr @@ -26,7 +26,7 @@ contract Vanilla { let mut new_number = ValueNote::new(number, owner); let owner_ovpk_m = get_public_keys(owner).ovpk_m; - numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_note(&mut context, owner_ovpk_m, owner)); + numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_note(&mut context, owner_ovpk_m, owner, context.msg_sender())); } #[private] @@ -38,7 +38,7 @@ contract Vanilla { let mut new_number = ValueNote::new(number, owner); let owner_ovpk_m = get_public_keys(owner).ovpk_m; - numbers.at(owner).replace(&mut new_number).emit(encode_and_encrypt_note(&mut context, owner_ovpk_m, owner)); + numbers.at(owner).replace(&mut new_number).emit(encode_and_encrypt_note(&mut context, owner_ovpk_m, owner, context.msg_sender())); } unconstrained fn getNumber(owner: AztecAddress) -> pub ValueNote { diff --git a/build-images/README.md b/build-images/README.md index bf750f98a1d..559a46b6da0 100644 --- a/build-images/README.md +++ b/build-images/README.md @@ -18,7 +18,7 @@ Your repo will be mounted at `/workspaces/aztec-packages`, and your home directo ## GitHub Codespaces -This is also compatible with GitHub codespaces. Visit the repo at `http://github.com/aztecprotocol/aztec-packages`. +This is also compatible with GitHub codespaces. Visit the repo at `https://github.com/aztecprotocol/aztec-packages`. Press `.`, and open a terminal window. You will be prompted to create a new machine. You can then continue to work within the browser, or reopen the codespace in your local vscode. diff --git a/build-system/README.md b/build-system/README.md index 013e95625b5..8fbf0a01346 100644 --- a/build-system/README.md +++ b/build-system/README.md @@ -35,7 +35,7 @@ We avoid using any Circle CI specific features. They are very general purpose, a The build system leverages image names and tags in the docker image registry to keep track of it's historical success or failure in terms of builds, tests, and deployments. It's otherwise stateless, meaning it only needs a container registry to track state. -We work in terms of _contest hashes_, not commit hashes or branches. Content hashes are like commit hashes, but are scoped to files matching the rebuild patterns. +We work in terms of _content hashes_, not commit hashes or branches. Content hashes are like commit hashes, but are scoped to files matching the rebuild patterns. There is a `build_manifest.yml` that describes various settings for each project (dependencies, rebuild patterns, etc). The dependencies as listed in the build manifest represent the graph such that if project A changes, all projects that depend on A will also be rebuilt. This likely closely mirrors the workflow graph as defined in Circle CI's `config.yml`. In the future we can generate a target CI platforms configuration from this build manifest. diff --git a/build-system/s3-cache-scripts/cache-download.sh b/build-system/s3-cache-scripts/cache-download.sh index 8d72f860460..31c62bfe3ca 100755 --- a/build-system/s3-cache-scripts/cache-download.sh +++ b/build-system/s3-cache-scripts/cache-download.sh @@ -17,8 +17,17 @@ function on_exit() { # Run on any exit trap on_exit EXIT +# Extract endpoint URL if S3_BUILD_CACHE_AWS_PARAMS is set +if [[ -n "${S3_BUILD_CACHE_AWS_PARAMS:-}" ]]; then + # Extract URL from S3_BUILD_CACHE_AWS_PARAMS (assumes the format "--endpoint-url ") + # TODO stop passing with endpoint url + S3_ENDPOINT=$(echo "$S3_BUILD_CACHE_AWS_PARAMS" | sed -n 's/--endpoint-url \([^ ]*\)/\1/p') +else + # Default to AWS S3 URL if no custom endpoint is set + S3_ENDPOINT="http://aztec-ci-artifacts.s3.amazonaws.com" +fi # Attempt to download the cache file -aws ${S3_BUILD_CACHE_AWS_PARAMS:-} s3 cp "s3://aztec-ci-artifacts/build-cache/$TAR_FILE" "$TAR_FILE" --quiet --no-sign-request || (echo "Cache download of $TAR_FILE failed." && exit 1) +curl -s -f -O "${S3_ENDPOINT}/build-cache/$TAR_FILE" || (echo "Cache download of $TAR_FILE failed." && exit 1) # Extract the cache file mkdir -p "$OUT_DIR" diff --git a/build-system/s3-cache-scripts/earthly-s3-cache.sh b/build-system/s3-cache-scripts/earthly-s3-cache.sh index 571eb210450..01267639be0 100755 --- a/build-system/s3-cache-scripts/earthly-s3-cache.sh +++ b/build-system/s3-cache-scripts/earthly-s3-cache.sh @@ -19,7 +19,7 @@ function s3_upload() { if [ "${S3_BUILD_CACHE_UPLOAD:-true}" = "false" ] || [ "${AWS_ACCESS_KEY_ID}" == "" ] ; then return 0 # exit silently fi - /usr/src/build-system/s3-cache-scripts/cache-upload.sh "$FILE" $build_artifacts + /usr/src/build-system/s3-cache-scripts/cache-upload.sh "$FILE" $build_artifacts || echo "WARNING: S3 upload failed!" >&2 } function minio_download() { if [ -z "$S3_BUILD_CACHE_MINIO_URL" ] ; then @@ -35,7 +35,7 @@ function minio_upload() { fi # minio is S3-compatible S3_BUILD_CACHE_AWS_PARAMS="--endpoint-url $S3_BUILD_CACHE_MINIO_URL" AWS_SECRET_ACCESS_KEY=minioadmin AWS_ACCESS_KEY_ID=minioadmin \ - /usr/src/build-system/s3-cache-scripts/cache-upload.sh "$FILE" $build_artifacts + /usr/src/build-system/s3-cache-scripts/cache-upload.sh "$FILE" $build_artifacts || echo "WARNING Minio upload failed!" >&2 } # commands @@ -53,5 +53,5 @@ if ! bash -c "$command" ; then exit 1 # we have failed to build, don't continue fi -minio_upload || echo "Minio upload failed!" -s3_upload || echo "S3 upload failed!" +minio_upload +s3_upload diff --git a/cspell.json b/cspell.json index d964b9afe22..42e5efb8715 100644 --- a/cspell.json +++ b/cspell.json @@ -270,9 +270,6 @@ "unexcluded", "unfinalised", "unprefixed", - "unshield", - "unshielding", - "unshields", "unzipit", "updateable", "upperfirst", @@ -283,6 +280,7 @@ "Validium", "vals", "viem", + "vpks", "Vyper", "wasms", "webassembly", diff --git a/docker-compose.provernet.yml b/docker-compose.provernet.yml index 58bb54087c4..57d7d374f35 100644 --- a/docker-compose.provernet.yml +++ b/docker-compose.provernet.yml @@ -136,6 +136,8 @@ services: BOT_PUBLIC_TRANSFERS_PER_TX: 0 BOT_NO_WAIT_FOR_TRANSFERS: true BOT_NO_START: false + BOT_MAX_CONSECUTIVE_ERRORS: 3 + BOT_STOP_WHEN_UNHEALTHY: true PXE_PROVER_ENABLED: "${PROVER_REAL_PROOFS:-false}" PROVER_REAL_PROOFS: "${PROVER_REAL_PROOFS:-false}" BB_SKIP_CLEANUP: "${BB_SKIP_CLEANUP:-0}" # Persist tmp dirs for debugging @@ -150,7 +152,7 @@ services: test: [ "CMD", "curl", "-fSs", "http://127.0.0.1:80/status" ] interval: 3s timeout: 30s - start_period: 10s + start_period: 90s restart: on-failure:5 command: [ "start", "--bot", "--pxe" ] diff --git a/docker_fast.sh b/docker_fast.sh index 2c24eceac12..49d30243be6 100755 --- a/docker_fast.sh +++ b/docker_fast.sh @@ -1,43 +1,24 @@ #!/usr/bin/env bash # TODO eventually rename this docker.sh when we've moved to it entirely -set -eux +set -eu -function start_minio() { - if nc -z 127.0.0.1 12000 2>/dev/null >/dev/null ; then - # Already started - return - fi - docker run -d -p 12000:9000 -p 12001:12001 -v minio-data:/data \ - quay.io/minio/minio server /data --console-address ":12001" - # Make our cache bucket - AWS_ACCESS_KEY_ID="minioadmin" AWS_SECRET_ACCESS_KEY="minioadmin" aws --endpoint-url http://localhost:12000 s3 mb s3://aztec-ci-artifacts 2>/dev/null || true -} +MAKE_END_TO_END=${1:-false} S3_BUILD_CACHE_UPLOAD=${S3_BUILD_CACHE_UPLOAD:-false} S3_BUILD_CACHE_MINIO_URL="http://$(hostname -I | awk '{print $1}'):12000" -# Start local file server for a quicker cache layer -start_minio - if ! git diff-index --quiet HEAD --; then - echo "Warning: You have unstaged changes. Disabling S3 caching and local MinIO caching to avoid polluting cache (which uses Git data)." >&2 + echo "Warning: You have unstaged changes. For now this is a fatal error as this script relies on git metadata." >&2 S3_BUILD_CACHE_UPLOAD=false S3_BUILD_CACHE_DOWNLOAD=false - S3_BUILD_CACHE_MINIO_URL="" - echo "Fatal: For now, this is a fatal error as it would defeat the purpose of 'fast'." >&2 + S3_BUILD_CACHE_MINIO_URL=""A exit 1 elif [ ! -z "${AWS_ACCESS_KEY_ID:-}" ] ; then S3_BUILD_CACHE_DOWNLOAD=true elif [ -f ~/.aws/credentials ]; then # Retrieve credentials if available in AWS config - - # Do not trace this information - set +x AWS_ACCESS_KEY_ID=$(aws configure get default.aws_access_key_id) AWS_SECRET_ACCESS_KEY=$(aws configure get default.aws_secret_access_key) - - # Resume tracing - set -x S3_BUILD_CACHE_DOWNLOAD=true else S3_BUILD_CACHE_UPLOAD=false @@ -52,11 +33,8 @@ function on_exit() { trap on_exit EXIT # Save each secret environment variable into a separate file in $TMP directory -set +x echo "${AWS_ACCESS_KEY_ID:-}" > "$TMP/aws_access_key_id.txt" echo "${AWS_SECRET_ACCESS_KEY:-}" > "$TMP/aws_secret_access_key.txt" -set -x - echo "${S3_BUILD_CACHE_MINIO_URL:-}" > "$TMP/s3_build_cache_minio_url.txt" echo "${S3_BUILD_CACHE_UPLOAD:-}" > "$TMP/s3_build_cache_upload.txt" echo "${S3_BUILD_CACHE_DOWNLOAD:-}" > "$TMP/s3_build_cache_download.txt" @@ -73,10 +51,19 @@ PROJECTS=( yarn-project ) +function copy() { + local project=$1 + git archive --format=tar.gz --mtime='1970-01-01T00:00Z' -o "$TMP/$project.tar.gz" $(git rev-parse HEAD) $project + cd "$TMP" + tar -xzf $project.tar.gz + rm $project.tar.gz +} +# Write the git archives in parallel for project in "${PROJECTS[@]}"; do - # Archive Git-tracked files per project into a tar.gz file - git archive --format=tar.gz -o "$TMP/$project.tar.gz" HEAD $project + # Copy over JUST the git version of files over (bail if any fail) + copy $project || kill $0 & done +wait # Run Docker build with secrets in the folder with our archive DOCKER_BUILDKIT=1 docker build -t aztecprotocol/aztec -f Dockerfile.fast --progress=plain \ @@ -85,4 +72,8 @@ DOCKER_BUILDKIT=1 docker build -t aztecprotocol/aztec -f Dockerfile.fast --progr --secret id=s3_build_cache_minio_url,src=$TMP/s3_build_cache_minio_url.txt \ --secret id=s3_build_cache_upload,src=$TMP/s3_build_cache_upload.txt \ --secret id=s3_build_cache_download,src=$TMP/s3_build_cache_download.txt \ - "$TMP" \ No newline at end of file + "$TMP" + +if [ $MAKE_END_TO_END != "false" ] ; then + DOCKER_BUILDKIT=1 docker build -t aztecprotocol/end-to-end -f Dockerfile.end-to-end.fast --progress=plain "$TMP" +fi diff --git a/docs/docs/aztec/concepts/accounts/index.md b/docs/docs/aztec/concepts/accounts/index.md index 7315c99e0c4..4c982e0fe44 100644 --- a/docs/docs/aztec/concepts/accounts/index.md +++ b/docs/docs/aztec/concepts/accounts/index.md @@ -37,7 +37,7 @@ A simple example would be Gnosis Safe (see [_Account Abstraction is NOT coming_] Ethereum is currently following this approach via [EIP4337](https://eips.ethereum.org/EIPS/eip-4337), an evolution of the [GSN](https://opengsn.org/). This EIP defines a standard method for relaying meta-transactions in a decentralized way, including options for delegating payment to other agents (called paymasters). See [this chart](https://x.com/koeppelmann/status/1632257610455089154) on how 4337 relates to other smart contract wallet efforts. -Implementing AA at the application layer has the main drawback that it's more complex than doing so at the protocol layer. It also leads to duplicated efforts in both layers (eg the wrapper transaction in a meta-transactions still needs to be checked for its ECDSA signature, and then the smart contract wallet needs to verify another set of signatures). +Implementing AA at the application layer has the main drawback that it's more complex than doing so at the protocol layer. It also leads to duplicated efforts in both layers (e.g. the wrapper transaction in a meta-transactions still needs to be checked for its ECDSA signature, and then the smart contract wallet needs to verify another set of signatures). Now, there have also been multiple proposals for getting AA implemented at the _protocol_ level in Ethereum. This usually implies introducing a new transaction type or set of opcodes where signature verification and fee payment is handled by the EVM. See EIPs [2803](https://eips.ethereum.org/EIPS/eip-2803), [2938](https://eips.ethereum.org/EIPS/eip-2938), or [3074](https://eips.ethereum.org/EIPS/eip-3074). None of these have gained traction due to the efforts involved in implementing while keeping backwards compatibility. @@ -76,7 +76,7 @@ Read more about how to write an account contract [here](../../../tutorials/codea ### Account contracts and wallets -Account contracts are tightly coupled to the wallet software that users use to interact with the protocol. Dapps submit to the wallet software one or more function calls to be executed (eg "call swap in X contract"), and the wallet encodes and authenticates the request as a valid payload for the user's account contract. The account contract then validates the request encoded and authenticated by the wallet, and executes the function calls requested by the dapp. +Account contracts are tightly coupled to the wallet software that users use to interact with the protocol. Dapps submit to the wallet software one or more function calls to be executed (e.g. "call swap in X contract"), and the wallet encodes and authenticates the request as a valid payload for the user's account contract. The account contract then validates the request encoded and authenticated by the wallet, and executes the function calls requested by the dapp. ### Execution requests diff --git a/docs/docs/aztec/concepts/accounts/keys.md b/docs/docs/aztec/concepts/accounts/keys.md index 30f2e8610e1..cf731a5813c 100644 --- a/docs/docs/aztec/concepts/accounts/keys.md +++ b/docs/docs/aztec/concepts/accounts/keys.md @@ -11,8 +11,8 @@ In short, there is a **nullifier key** (to spend your notes), an **incoming view Each account in Aztec is backed by 4 key pairs: - A **nullifier key pair** used for note nullifier computation, comprising the master nullifier secret key (`nsk_m`) and master nullifier public key (`Npk_m`). -- A **incoming viewing key pair** used to encrypt a note for the recipient, consisting of the master incoming viewing secret key (`ivsk_m`) and master incoming viewing public key (`Ivpk_m`). -- A **outgoing viewing key pair** used to encrypt a note for the sender, includes the master outgoing viewing secret key (`ovsk_m`) and master outgoing viewing public key (`Ovpk_m`). +- An **incoming viewing key pair** used to encrypt a note for the recipient, consisting of the master incoming viewing secret key (`ivsk_m`) and master incoming viewing public key (`Ivpk_m`). +- An **outgoing viewing key pair** used to encrypt a note for the sender, includes the master outgoing viewing secret key (`ovsk_m`) and master outgoing viewing public key (`Ovpk_m`). - A **tagging key pair** used to compute tags in a [tagging note discovery scheme](../../../protocol-specs/private-message-delivery/private-msg-delivery.md#note-tagging), comprising the master tagging secret key (`tsk_m`) and master tagging public key (`Tpk_m`). :::info @@ -27,22 +27,13 @@ Instead it's up to the account contract developer to implement it. ## Public keys retrieval -The keys can be retrieved from the [Private eXecution Environment (PXE)](../pxe/index.md) using the following getter in Aztec.nr: +The keys for our accounts can be retrieved from the [Private eXecution Environment (PXE)](../pxe/index.md) using the following getter in Aztec.nr: ``` fn get_public_keys(account: AztecAddress) -> PublicKeys; ``` -It is necessary to first register the user as a recipient in our PXE, providing their public keys. - -First we need to get a hold of recipient's [complete address](#complete-address). -Below are some ways how we could instantiate it after getting the information in a string form from a recipient: - -#include_code instantiate-complete-address /yarn-project/circuits.js/src/structs/complete_address.test.ts rust - -Then to register the recipient's [complete address](#complete-address) in PXE we would call `registerRecipient` PXE endpoint using Aztec.js. - -#include_code register-recipient /yarn-project/aztec.js/src/wallet/create_recipient.ts rust +It is necessary to first register the user as an account in our PXE, by calling the `registerAccount` PXE endpoint using Aztec.js, providing the account's secret key and partial address. During private function execution these keys are obtained via an oracle call from PXE. @@ -133,7 +124,7 @@ A compromise between the two solutions above is to use shared state. This would #### Reusing some of the in-protocol keys It is possible to use some of the key pairs defined in protocol (e.g. incoming viewing keys) as the signing key. -Since this key is part of the address preimage (more on this on the privacy master key section), you it can be validated against the account contract address rather than having to store it. +Since this key is part of the address preimage (more on this on the privacy master key section), it can be validated against the account contract address rather than having to store it. However, this approach is not recommended since it reduces the security of the user's account. #### Using a separate keystore diff --git a/docs/docs/aztec/concepts/circuits/rollup_circuits/index.md b/docs/docs/aztec/concepts/circuits/rollup_circuits/index.md index 72f08e093d3..7935d44c591 100644 --- a/docs/docs/aztec/concepts/circuits/rollup_circuits/index.md +++ b/docs/docs/aztec/concepts/circuits/rollup_circuits/index.md @@ -12,7 +12,7 @@ The way we 'squish' all this data is in a 'binary tree of proofs' topology. > Example: If there were 16 txs in a rollup, we'd arrange the 16 kernel proofs into 8 pairs and merge each pair into a single proof (using zk-snark recursion techniques), resulting in 8 output proofs. We'd then arrange those 8 proofs into pairs and again merge each pair into a single proof, resulting in 4 output proofs. And so on until we'd be left with a single proof, which represents the correctness of the original 16 txs. > This 'binary tree of proofs' topology allows proof generation to be greatly parallelized across prover instances. Each layer of the tree can be computed in parallel. Or alternatively, subtrees can be coordinated to be computed in parallel. -> Note: 'binary tree of proofs' is actually an over simplification. The Rollup Circuits are designed so that a Sequencer can actually deviate from a neat, symmetrical tree, for the purposes of efficiency, and instead sometimes create wonky trees. +> Note: 'binary tree of proofs' is actually an oversimplification. The Rollup Circuits are designed so that a Sequencer can actually deviate from a neat, symmetrical tree, for the purposes of efficiency, and instead sometimes create wonky trees. Some of the Rollup Circuits also do some protocol checks and computations, for efficiency reasons. We might rearrange which circuit does what computation, as we discover opportunities for efficiency. diff --git a/docs/docs/aztec/concepts/communication/cross_chain_calls.md b/docs/docs/aztec/concepts/communication/cross_chain_calls.md index a65573ac251..b606a5e9311 100644 --- a/docs/docs/aztec/concepts/communication/cross_chain_calls.md +++ b/docs/docs/aztec/concepts/communication/cross_chain_calls.md @@ -13,7 +13,7 @@ import Image from "@theme/IdealImage"; In Aztec, what we call _portals_ are the key element in facilitating communication between L1 and L2. While typical L2 solutions rely on synchronous communication with L1, Aztec's privacy-first nature means this is not possible. You can learn more about why in the previous section. -Traditional L1 \<-\> L2 communication might involve direct calls between L2 nd L1 contracts. However, in Aztec, due to the privacy components and the way transactions are processed (kernel proofs built on historical data), direct calls between L1 and L2 would not be possible if we want to maintain privacy. +Traditional L1 \<-\> L2 communication might involve direct calls between L2 and L1 contracts. However, in Aztec, due to the privacy components and the way transactions are processed (kernel proofs built on historical data), direct calls between L1 and L2 would not be possible if we want to maintain privacy. Portals are the solution to this problem, acting as bridges for communication between the two layers. These portals can transmit messages from public functions in L1 to private functions in L2 and vice versa, thus enabling messaging while maintaining privacy. @@ -108,7 +108,7 @@ For the sake of cross-chain messages, this means inserting and nullifying L1 $\r While a message could theoretically be arbitrarily long, we want to limit the cost of the insertion on L1 as much as possible. Therefore, we allow the users to send 32 bytes of "content" between L1 and L2. If 32 suffices, no packing required. If the 32 is too "small" for the message directly, the sender should simply pass along a `sha256(content)` instead of the content directly (note that this hash should fit in a field element which is ~254 bits. More info on this below). The content can then either be emitted as an event on L2 or kept by the sender, who should then be the only entity that can "unpack" the message. In this manner, there is some way to "unpack" the content on the receiving domain. -The message that is passed along, require the `sender/recipient` pair to be communicated as well (we need to know who should receive the message and be able to check). By having the pending messages be a contract on L1, we can ensure that the `sender = msg.sender` and let only `content` and `recipient` be provided by the caller. Summing up, we can use the struct's seen below, and only store the commitment (`sha256(LxToLyMsg)`) on chain or in the trees, this way, we need only update a single storage slot per message. +The message that is passed along, require the `sender/recipient` pair to be communicated as well (we need to know who should receive the message and be able to check). By having the pending messages be a contract on L1, we can ensure that the `sender = msg.sender` and let only `content` and `recipient` be provided by the caller. Summing up, we can use the structs seen below, and only store the commitment (`sha256(LxToLyMsg)`) on chain or in the trees, this way, we need only update a single storage slot per message. ```solidity struct L1Actor { diff --git a/docs/docs/aztec/concepts/state_model/index.md b/docs/docs/aztec/concepts/state_model/index.md index d256ef0996f..37a0455456f 100644 --- a/docs/docs/aztec/concepts/state_model/index.md +++ b/docs/docs/aztec/concepts/state_model/index.md @@ -20,7 +20,7 @@ Private state is encrypted and therefore is "owned" by a user or a set of users Private state is represented in an append-only database since updating a record would leak information about the transaction graph. -The act of "deleting" a private satate variable can be represented by adding an associated nullifier to a nullifier set. The nullifier is generated such that, without knowing the decryption key of the owner, an observer cannot link a state record with a nullifier. +The act of "deleting" a private state variable can be represented by adding an associated nullifier to a nullifier set. The nullifier is generated such that, without knowing the decryption key of the owner, an observer cannot link a state record with a nullifier. Modification of state variables can be emulated by nullifying the state record and creating a new record to represent the variable. Private state has an intrinsic UTXO structure and this must be represented in the language semantics of manipulating private state. diff --git a/docs/docs/aztec/concepts/storage/trees/indexed_merkle_tree.mdx b/docs/docs/aztec/concepts/storage/trees/indexed_merkle_tree.mdx index f2fba696620..e2a1a7415e7 100644 --- a/docs/docs/aztec/concepts/storage/trees/indexed_merkle_tree.mdx +++ b/docs/docs/aztec/concepts/storage/trees/indexed_merkle_tree.mdx @@ -30,7 +30,7 @@ The content was also covered in a presentation for the [Privacy + Scaling Explor ## Primer on Nullifier Trees -Currently the only feasible way to get privacy in public blockchains is via a UTXO model. In this model, state is stored in encrypted UTXO's in merkle trees. However, to maintain privacy, state can not be updated. The very act of performing an update leaks information. In order to simulate "updating" the state, we "destroy" old UTXO's and create new ones for each state update. Resulting in a merkle tree that is append-only. +Currently the only feasible way to get privacy in public blockchains is via a UTXO model. In this model, state is stored in encrypted UTXO's in merkle trees. However, to maintain privacy, state cannot be updated. The very act of performing an update leaks information. In order to simulate "updating" the state, we "destroy" old UTXO's and create new ones for each state update. Resulting in a merkle tree that is append-only. A classic merkle tree: @@ -219,7 +219,7 @@ In the following example we insert a subtree of size 4 into our tree at step 4. #### Performance gains from subtree insertion -Lets go back over the numbers: +Let's go back over the numbers: Insertions into a sparse nullifier tree involve 1 non membership check (254 hashes) and 1 insertion (254 hashes). If we were performing insertion for 4 values that would entail 2032 hashes. In the depth 32 indexed tree construction, each subtree insertion costs 1 non membership check (32 hashes), 1 pointer update (32 hashes) for each value as well as the cost of constructing and inserting a subtree (~67 hashes. Which is 327 hashes, an incredible efficiency increase.) diff --git a/docs/docs/aztec/concepts/wallets/architecture.md b/docs/docs/aztec/concepts/wallets/architecture.md index fcab210d5ab..adb5743c78d 100644 --- a/docs/docs/aztec/concepts/wallets/architecture.md +++ b/docs/docs/aztec/concepts/wallets/architecture.md @@ -3,7 +3,7 @@ title: Wallet Architecture tags: [protocol, accounts] --- -This page talks about the architecture of a wallet in Aztec. Wallets expose to dapps an interface that allows them to act on behalf of the user, such as querying private state or sending transactions. Bear mind that, as in Ethereum, wallets should require user confirmation whenever carrying out a potentially sensitive action requested by a dapp. +This page talks about the architecture of a wallet in Aztec. Wallets expose to dapps an interface that allows them to act on behalf of the user, such as querying private state or sending transactions. Bear in mind that, as in Ethereum, wallets should require user confirmation whenever carrying out a potentially sensitive action requested by a dapp. ## Overview @@ -23,6 +23,6 @@ The account interface is used for creating an _execution request_ out of one or ## PXE interface -A wallet exposes the PXE interface to dapps by running an PXE instance. The PXE requires a keystore and a database implementation for storing keys, private state, and recipient encryption public keys. +A wallet exposes the PXE interface to dapps by running a PXE instance. The PXE requires a keystore and a database implementation for storing keys, private state, and recipient encryption public keys. #include_code pxe-interface /yarn-project/circuit-types/src/interfaces/pxe.ts typescript diff --git a/docs/docs/aztec/concepts/wallets/index.md b/docs/docs/aztec/concepts/wallets/index.md index 790db2cd1f9..1c64d7a2843 100644 --- a/docs/docs/aztec/concepts/wallets/index.md +++ b/docs/docs/aztec/concepts/wallets/index.md @@ -4,7 +4,7 @@ sidebar_position: 1 tags: [accounts] --- -In this page we will cover the main responsibilities of a wallet in the Aztec network. +On this page we will cover the main responsibilities of a wallet in the Aztec network. Refer to [writing an account contract](../../../tutorials/codealong/contract_tutorials/write_accounts_contract.md) for a tutorial on how to write a contract to back a user's account. diff --git a/docs/docs/aztec/how_to_participate.md b/docs/docs/aztec/how_to_participate.md index b0e14dadbda..6038a827abc 100644 --- a/docs/docs/aztec/how_to_participate.md +++ b/docs/docs/aztec/how_to_participate.md @@ -18,5 +18,5 @@ Decentralization is one of our core values, so we want to encourage participatio ## Grants -- The Aztec Labs Grants Program supports developers building with, and contributing to, the Noir programming language and the Aztec network. Applications can submitted on the [Grants page](https://aztec.network/grants/) of the Aztec website. +- The Aztec Labs Grants Program supports developers building with, and contributing to, the Noir programming language and the Aztec network. Applications can be submitted on the [Grants page](https://aztec.network/grants/) of the Aztec website. - We are currently operating with a retroactive grants funding model, and we strive to respond back to grants applications with a decision within a few days. Check out our [grants page](https://aztec.network/grants/) for more information diff --git a/docs/docs/aztec/smart_contracts/functions/attributes.md b/docs/docs/aztec/smart_contracts/functions/attributes.md index f8491bca254..e7dc10e8d77 100644 --- a/docs/docs/aztec/smart_contracts/functions/attributes.md +++ b/docs/docs/aztec/smart_contracts/functions/attributes.md @@ -77,7 +77,7 @@ This function takes the application context, and converts it into the `PrivateCi ## Unconstrained functions -Unconstrained functions are an underlying part of Noir. In short, they are functions which are not directly constrained and therefore should be seen as un-trusted. That they are un-trusted means that the developer must make sure to constrain their return values when used. Note: Calling an unconstrained function from a private function means that you are injecting unconstrained values. +Unconstrained functions are an underlying part of Noir. In short, they are functions which are not directly constrained and therefore should be seen as untrusted. That they are un-trusted means that the developer must make sure to constrain their return values when used. Note: Calling an unconstrained function from a private function means that you are injecting unconstrained values. Defining a function as `unconstrained` tells Aztec to simulate it completely client-side in the [ACIR simulator](../../concepts/pxe/index.md) without generating proofs. They are useful for extracting information from a user through an [oracle](../oracles/index.md). diff --git a/docs/docs/aztec/smart_contracts/functions/context.md b/docs/docs/aztec/smart_contracts/functions/context.md index 92ec63343ff..6929153a1f7 100644 --- a/docs/docs/aztec/smart_contracts/functions/context.md +++ b/docs/docs/aztec/smart_contracts/functions/context.md @@ -6,9 +6,9 @@ tags: [functions, context] ## What is the context -The context is an object that is made available within every function in `Aztec.nr`. As mentioned in the [kernel circuit documentation](../../concepts/circuits/kernels/private_kernel.md). At the beginning of a function's execution, the context contains all of the kernel information that application needs to execute. During the lifecycle of a transaction, the function will update the context with each of it's side effects (created notes, nullifiers etc.). At the end of a function's execution the mutated context is returned to the kernel to be checked for validity. +The context is an object that is made available within every function in `Aztec.nr`. As mentioned in the [kernel circuit documentation](../../concepts/circuits/kernels/private_kernel.md). At the beginning of a function's execution, the context contains all of the kernel information that application needs to execute. During the lifecycle of a transaction, the function will update the context with each of its side effects (created notes, nullifiers etc.). At the end of a function's execution the mutated context is returned to the kernel to be checked for validity. -Behind the scenes, Aztec.nr will pass data the kernel needs to and from a circuit, this is abstracted away from the developer. In an developer's eyes; the context is a useful structure that allows access and mutate the state of the `Aztec` blockchain. +Behind the scenes, Aztec.nr will pass data the kernel needs to and from a circuit, this is abstracted away from the developer. In a developer's eyes; the context is a useful structure that allows access and mutate the state of the `Aztec` blockchain. On this page, you'll learn diff --git a/docs/docs/aztec/smart_contracts/functions/index.md b/docs/docs/aztec/smart_contracts/functions/index.md index 750fe9ba997..8427c5e7684 100644 --- a/docs/docs/aztec/smart_contracts/functions/index.md +++ b/docs/docs/aztec/smart_contracts/functions/index.md @@ -7,7 +7,7 @@ Functions serve as the building blocks of smart contracts. Functions can be eith For a more practical guide of using multiple types of functions, follow the [token tutorial](../../../tutorials/codealong/contract_tutorials/token_contract.md). -Currently, any function is "mutable" in the sense that it might alter state. However, we also support support static calls, similarly to EVM. A static call is essentially a call that does not alter state (it keeps state static). +Currently, any function is "mutable" in the sense that it might alter state. However, we also support static calls, similarly to EVM. A static call is essentially a call that does not alter state (it keeps state static). ## Initializer functions diff --git a/docs/docs/aztec/smart_contracts/functions/visibility.md b/docs/docs/aztec/smart_contracts/functions/visibility.md index 4e508582494..1fc206cd6fa 100644 --- a/docs/docs/aztec/smart_contracts/functions/visibility.md +++ b/docs/docs/aztec/smart_contracts/functions/visibility.md @@ -6,7 +6,7 @@ tags: [functions] In Aztec there are multiple different types of visibility that can be applied to functions. Namely we have `data visibility` and `function visibility`. This page explains these types of visibility. -For a practical guide of using multiple types of data and function visibility,follow the [token tutorial](../../../tutorials/codealong/contract_tutorials/token_contract.md). +For a practical guide of using multiple types of data and function visibility, follow the [token tutorial](../../../tutorials/codealong/contract_tutorials/token_contract.md). ### Data Visibility diff --git a/docs/docs/aztec/smart_contracts_overview.md b/docs/docs/aztec/smart_contracts_overview.md index 2ccb8b76692..234467d06e7 100644 --- a/docs/docs/aztec/smart_contracts_overview.md +++ b/docs/docs/aztec/smart_contracts_overview.md @@ -19,7 +19,7 @@ A "smart contract" is defined as a set of public and private functions written a [Noir](https://noir-lang.org) is a programming language designed for converting high-level programs into ZK circuits. Based on Rust, the goal is to present an idiomatic way of writing private smart contracts that is familiar to Ethereum developers. Noir is under active development adding features such as contracts, functions and storage variables. -The end goal is a language that is intuitive to use for developers with no cryptographic knowledge and empowers developers to easily write programable private smart contracts. +The end goal is a language that is intuitive to use for developers with no cryptographic knowledge and empowers developers to easily write programmable private smart contracts. There are no plans for EVM compatibility or to support Solidity in Aztec. The privacy-first nature of Aztec is fundamentally incompatible with the EVM architecture and Solidity's semantics. In addition, the heavy use of client-side proof construction makes this impractical. diff --git a/docs/docs/guides/developer_guides/js_apps/authwit.md b/docs/docs/guides/developer_guides/js_apps/authwit.md index 9dc7dcae0d8..d4db5d8c5cd 100644 --- a/docs/docs/guides/developer_guides/js_apps/authwit.md +++ b/docs/docs/guides/developer_guides/js_apps/authwit.md @@ -85,7 +85,7 @@ Create a private authwit like this: In this example, - `wallets[0]` is the authwit giver -- `wallets[1]` is the authwit reciever and caller of the function +- `wallets[1]` is the authwit receiver and caller of the function - `action` was [defined previously](#define-the-action) If you created an arbitrary message, you can create the authwit by replacing these params with the outer hash: diff --git a/docs/docs/guides/developer_guides/js_apps/test.md b/docs/docs/guides/developer_guides/js_apps/test.md index 3c604d2e27f..a290623a51c 100644 --- a/docs/docs/guides/developer_guides/js_apps/test.md +++ b/docs/docs/guides/developer_guides/js_apps/test.md @@ -59,7 +59,7 @@ You can use the `debug` option in the `wait` method to get more information abou This debug information will be populated in the transaction receipt. You can log it to the console or use it to make assertions about the transaction. -#include_code debug /yarn-project/end-to-end/src/e2e_token_contract/minting.test.ts typescript +#include_code debug /yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts typescript You can also log directly from Aztec contracts. Read [this guide](../../../reference/developer_references/debugging.md#logging-in-aztecnr) for some more information. diff --git a/docs/docs/guides/developer_guides/smart_contracts/how_to_compile_contract.md b/docs/docs/guides/developer_guides/smart_contracts/how_to_compile_contract.md index 414fc543bf4..c356ddb1544 100644 --- a/docs/docs/guides/developer_guides/smart_contracts/how_to_compile_contract.md +++ b/docs/docs/guides/developer_guides/smart_contracts/how_to_compile_contract.md @@ -246,7 +246,7 @@ contract FPC { #[private] fn fee_entrypoint_private(amount: Field, asset: AztecAddress, secret_hash: Field, nonce: Field) { assert(asset == storage.other_asset.read_private()); - Token::at(asset).unshield(context.msg_sender(), context.this_address(), amount, nonce).call(&mut context); + Token::at(asset).transfer_to_public(context.msg_sender(), context.this_address(), amount, nonce).call(&mut context); FPC::at(context.this_address()).pay_fee_with_shielded_rebate(amount, asset, secret_hash).enqueue(&mut context); } diff --git a/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/authwit.md b/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/authwit.md index 71c085f3ca6..dce64ae625e 100644 --- a/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/authwit.md +++ b/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/authwit.md @@ -175,7 +175,7 @@ In the snippet below, this is done as a separate contract call, but can also be We have cases where we need a non-wallet contract to approve an action to be executed by another contract. One of the cases could be when making more complex defi where funds are passed along. When doing so, we need the intermediate contracts to support approving of actions on their behalf. -This is fairly straight forward to do using the `auth` library which includes logic for updating values in the public auth registry. Namely, you can prepare the `message_hash` using `compute_authwit_message_hash_from_call` and then simply feed it into the `set_authorized` function (both are in `auth` library) to update the value. +This is fairly straight forward to do using the `auth` library which includes logic for updating values in the public auth registry. Namely, you can prepare the `message_hash` using `compute_authwit_message_hash_from_call` and then simply feed it into the `set_authorized` function (both are in `auth` library) to update the value. When another contract later is consuming the authwit using `assert_current_call_valid_authwit_public` it will be calling the registry, and spend that authwit. @@ -197,7 +197,7 @@ sequenceDiagram activate AC; AC->>CC: Swap 1000 token A to B activate CC; - CC->>T: unshield 1000 tokens from Alice Account to CCS + CC->>T: Transfer to public 1000 tokens from Alice Account to CCS activate T; T->>AC: Have you approved this?? AC-->>A: Please give me an AuthWit diff --git a/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/common_patterns/index.md b/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/common_patterns/index.md index fdcc1af2b19..848fffa0e1f 100644 --- a/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/common_patterns/index.md +++ b/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/common_patterns/index.md @@ -93,7 +93,7 @@ When you send someone a note, the note hash gets added to the note hash tree. To 1. When sending someone a note, use `encrypt_and_emit_note` (the function encrypts the log in such a way that only a recipient can decrypt it). PXE then tries to decrypt all the encrypted logs, and stores the successfully decrypted one. [More info here](../how_to_emit_event.md) 2. Manually using `pxe.addNote()` - If you choose to not emit logs to save gas or when creating a note in the public domain and want to consume it in private domain (`encrypt_and_emit_note` shouldn't be called in the public domain because everything is public), like in the previous section where we created a TransparentNote in public. -#include_code pxe_add_note yarn-project/end-to-end/src/e2e_cheat_codes.test.ts typescript +#include_code pxe_add_note yarn-project/end-to-end/src/composed/e2e_persistence.test.ts typescript In the token contract, TransparentNotes are stored in a set called "pending_shields" which is in storage slot 5tutorials/tutorials/codealong/contract_tutorials/token_contract.md#contract-storage) @@ -131,7 +131,7 @@ To send a note to someone, they need to have a key which we can encrypt the note There are several patterns here: 1. Give the contract a key and share it amongst all participants. This leaks privacy, as anyone can see all the notes in the contract. -2. `Unshield` funds into the contract - this is used in the [Uniswap smart contract example where a user sends private funds into a Uniswap Portal contract which eventually withdraws to L1 to swap on L1 Uniswap](../../../../../tutorials/examples/uniswap/index.md). This works like Ethereum - to achieve contract composability, you move funds into the public domain. This way the contract doesn't even need keys. +2. `transfer_to_public` funds into the contract - this is used in the [Uniswap smart contract example where a user sends private funds into a Uniswap Portal contract which eventually withdraws to L1 to swap on L1 Uniswap](../../../../../tutorials/examples/uniswap/index.md). This works like Ethereum - to achieve contract composability, you move funds into the public domain. This way the contract doesn't even need keys. There are several other designs we are discussing through [in this discourse post](https://discourse.aztec.network/t/how-to-handle-private-escrows-between-two-parties/2440) but they need some changes in the protocol or in our demo contract. If you are interested in this discussion, please participate in the discourse post! diff --git a/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/how_to_emit_event.md b/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/how_to_emit_event.md index 0c0c7fc9d33..47440c4fd1c 100644 --- a/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/how_to_emit_event.md +++ b/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/how_to_emit_event.md @@ -16,24 +16,6 @@ Unlike on Ethereum, there are 2 types of events supported by Aztec: [encrypted]( ## Encrypted Events -### Register a recipient - -Encrypted events can only be emitted by private functions and are encrypted using a public key of a recipient. -For this reason it is necessary to register a recipient in the Private Execution Environment (PXE) before encrypting the events for them. - -First we need to get a hold of recipient's complete address. -Below are some ways how we could instantiate it after getting the information in a string form from a recipient: - -#include_code instantiate-complete-address /yarn-project/circuits.js/src/structs/complete_address.test.ts rust - -Then to register the recipient's complete address in PXE we would call `registerRecipient` PXE endpoint using Aztec.js - -#include_code register-recipient /yarn-project/aztec.js/src/wallet/create_recipient.ts rust - -:::info -If a note recipient is one of the accounts inside the PXE, we don't need to register it as a recipient because we already have the public key available. You can register a recipient as shown [here](../how_to_deploy_contract.md) -::: - ### Call emit To emit encrypted logs you can import the `encode_and_encrypt` or `encode_and_encrypt_with_keys` functions and pass them into the `emit` function after inserting a note. An example can be seen in the reference token contract's transfer function: diff --git a/docs/docs/migration_notes.md b/docs/docs/migration_notes.md index cc9f695d4e9..4e44286b40c 100644 --- a/docs/docs/migration_notes.md +++ b/docs/docs/migration_notes.md @@ -6,7 +6,7 @@ keywords: [sandbox, aztec, notes, migration, updating, upgrading] Aztec is in full-speed development. Literally every version breaks compatibility with the previous ones. This page attempts to target errors and difficulties you might encounter when upgrading, and how to resolve them. -## 0.X.X +## 0.62.0 ### [TXE] Single execution environment Thanks to recent advancements in Brillig TXE performs every single call as if it was a nested call, spawning a new ACVM or AVM simulator without performance loss. This ensures every single test runs in a consistent environment and allows for clearer test syntax: @@ -18,6 +18,55 @@ This ensures every single test runs in a consistent environment and allows for c ``` This implies every contract has to be deployed before it can be tested (via `env.deploy` or `env.deploy_self`) and of course it has to be recompiled if its code was changed before TXE can use the modified bytecode. +### Uniqueness of L1 to L2 messages + +L1 to L2 messages have been updated to guarantee their uniqueness. This means that the hash of an L1 to L2 message cannot be precomputed, and must be obtained from the `MessageSent` event emitted by the `Inbox` contract, found in the L1 transaction receipt that inserted the message: + +```solidity +event MessageSent(uint256 indexed l2BlockNumber, uint256 index, bytes32 indexed hash); +``` + +This event now also includes an `index`. This index was previously required to consume an L1 to L2 message in a public function, and now it is also required for doing so in a private function, since it is part of the message hash preimage. The `PrivateContext` in aztec-nr has been updated to reflect this: + +```diff +pub fn consume_l1_to_l2_message( + &mut self, + content: Field, + secret: Field, + sender: EthAddress, ++ leaf_index: Field, +) { +``` + +This change has also modified the internal structure of the archiver database, making it incompatible with previous ones. Last, the API for obtaining an L1 to L2 message membership witness has been simplified to leverage message uniqueness: + +```diff +getL1ToL2MessageMembershipWitness( + blockNumber: L2BlockNumber, + l1ToL2Message: Fr, +- startIndex: bigint, +): Promise<[bigint, SiblingPath] | undefined>; +``` + +### Address is now a point + +The address now serves as someone's public key to encrypt incoming notes. An address point has a corresponding address secret, which is used to decrypt the notes encrypted with the address point. + +### Notes no longer store a hash of the nullifier public keys, and now store addresses + +Because of removing key rotation, we can now store addresses as the owner of a note. Because of this and the above change, we can and have removed the process of registering a recipient, because now we do not need any keys of the recipient. + +example_note.nr +```diff +-npk_m_hash: Field ++owner: AztecAddress +``` + +PXE Interface +```diff +-registerRecipient(completeAddress: CompleteAddress) +``` + ## 0.58.0 ### [l1-contracts] Inbox's MessageSent event emits global tree index Earlier `MessageSent` event in Inbox emitted a subtree index (index of the message in the subtree of the l2Block). But the nodes and Aztec.nr expects the index in the global L1_TO_L2_MESSAGES_TREE. So to make it easier to parse this, Inbox now emits this global index. @@ -26,7 +75,7 @@ Earlier `MessageSent` event in Inbox emitted a subtree index (index of the messa ### Changes to PXE API and `ContractFunctionInteraction`` -PXE APIs have been refactored to better reflext the lifecycle of a Tx (`execute private -> simulate kernels -> simulate public (estimate gas) -> prove -> send`) +PXE APIs have been refactored to better reflect the lifecycle of a Tx (`execute private -> simulate kernels -> simulate public (estimate gas) -> prove -> send`) * `.simulateTx`: Now returns a `TxSimulationResult`, containing the output of private execution, kernel simulation and public simulation (optional). * `.proveTx`: Now accepts the result of executing the private part of a transaction, so simulation doesn't have to happen again. diff --git a/docs/docs/protocol-specs/gas-and-fees/tx-setup-and-teardown.md b/docs/docs/protocol-specs/gas-and-fees/tx-setup-and-teardown.md index 6ea9057ecbb..b5b4783462c 100644 --- a/docs/docs/protocol-specs/gas-and-fees/tx-setup-and-teardown.md +++ b/docs/docs/protocol-specs/gas-and-fees/tx-setup-and-teardown.md @@ -37,7 +37,7 @@ Consider a user, Alice, who does not have FPA but wishes to interact with the ne Suppose there is a Fee Payment Contract (FPC) that has been deployed by another user to the network. Alice can structure her transaction as follows: -0. Before the transaction, Alice creates a private authwit in her wallet, allowing the FPC to unshield a specified amount of BananaCoin from Alice's private balance to the FPC's public balance. +0. Before the transaction, Alice creates a private authwit in her wallet, allowing the FPC to transfer to public a specified amount of BananaCoin from Alice's private balance to the FPC's public balance. 1. Private setup: - Alice calls a private function on the FPC which is exposed for public fee payment in BananaCoin. - The FPC checks that the amount of teardown gas Alice has allocated is sufficient to cover the gas associated with the teardown function it will use to provide a refund to Alice. diff --git a/docs/docs/reference/developer_references/smart_contract_reference/macros.md b/docs/docs/reference/developer_references/smart_contract_reference/macros.md index 3f9115af224..edf3adc8442 100644 --- a/docs/docs/reference/developer_references/smart_contract_reference/macros.md +++ b/docs/docs/reference/developer_references/smart_contract_reference/macros.md @@ -11,7 +11,7 @@ It is also worth mentioning Noir's `unconstrained` function type [here (Noir doc - `#[aztec]` - Defines a contract, placed above `contract ContractName{}` - `#[public]` or `#[private]` - Whether the function is to be executed from a public or private context (see Further Reading) -- `#[initializer]` - If one or more functions are marked as an initializer, then one of them must be called before any non-initilizer functions +- `#[initializer]` - If one or more functions are marked as an initializer, then one of them must be called before any non-initializer functions - `#[noinitcheck]` - The function is able to be called before an initializer (if one exists) - `#[view]` - Makes calls to the function static (see also [Static calls in the protocol spec](../../../protocol-specs/calls/static-calls.md)) - `#[internal]` - Function can only be called from within the contract diff --git a/docs/docs/tutorials/codealong/contract_tutorials/advanced/token_bridge/1_depositing_to_aztec.md b/docs/docs/tutorials/codealong/contract_tutorials/advanced/token_bridge/1_depositing_to_aztec.md index dccc268926b..0d7632cc1bc 100644 --- a/docs/docs/tutorials/codealong/contract_tutorials/advanced/token_bridge/1_depositing_to_aztec.md +++ b/docs/docs/tutorials/codealong/contract_tutorials/advanced/token_bridge/1_depositing_to_aztec.md @@ -58,7 +58,7 @@ Here is an explanation of what it is doing: - We use our utility method that creates a sha256 hash but truncates it to fit into a field - Since we want to mint tokens on Aztec publicly, the content here is the amount to mint and the address on Aztec who will receive the tokens. - We encode this message as a mint_public function call, to specify the exact intentions and parameters we want to execute on L2. - - In reality the content can be constructed in any manner as long as the sister contract on L2 can also create it. But for clarity, we are constructing the content like a abi encoded function call. + - In reality the content can be constructed in any manner as long as the sister contract on L2 can also create it. But for clarity, we are constructing the content like an ABI encoded function call. - It is good practice to include all parameters used by L2 into this content (like the amount and to) so that a malicious actor can’t change the to to themselves when consuming the message. 3. The tokens are transferred from the user to the portal using `underlying.safeTransferFrom()`. This puts the funds under the portal's control. 4. Next we send the message to the inbox contract. The inbox expects the following parameters: diff --git a/docs/docs/tutorials/codealong/contract_tutorials/crowdfunding_contract.md b/docs/docs/tutorials/codealong/contract_tutorials/crowdfunding_contract.md index ed7b0b9d21f..397ac584839 100644 --- a/docs/docs/tutorials/codealong/contract_tutorials/crowdfunding_contract.md +++ b/docs/docs/tutorials/codealong/contract_tutorials/crowdfunding_contract.md @@ -26,7 +26,7 @@ Along the way you will: ### Install tools -Please ensure that the you already have [Installed the Sandbox](../../../guides/developer_guides/getting_started) +Please ensure that you already have [Installed the Sandbox](../../../guides/developer_guides/getting_started) ### Create an Aztec project diff --git a/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md b/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md index 9638102a0d6..efb6fda0342 100644 --- a/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md +++ b/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md @@ -77,9 +77,9 @@ These are functions that have transparent logic, will execute in a publicly veri These are functions that have private logic and will be executed on user devices to maintain privacy. The only data that is submitted to the network is a proof of correct execution, new data commitments and nullifiers, so users will not reveal which contract they are interacting with or which function they are executing. The only information that will be revealed publicly is that someone executed a private transaction on Aztec. - `redeem_shield` enables accounts to claim tokens that have been made private via `mint_private` or `shield` by providing the secret -- `unshield` enables an account to send tokens from their private balance to any other account's public balance +- `transfer_to_public` enables an account to send tokens from their private balance to any other account's public balance - `transfer` enables an account to send tokens from their private balance to another account's private balance -- `transferFrom` enables an account to send tokens from another account's private balance to another account's private balance +- `transfer_from` enables an account to send tokens from another account's private balance to another account's private balance - `cancel_authwit` enables an account to cancel an authorization to spend tokens - `burn` enables tokens to be burned privately @@ -87,7 +87,7 @@ These are functions that have private logic and will be executed on user devices Internal functions are functions that can only be called by the contract itself. These can be used when the contract needs to call one of it's public functions from one of it's private functions. -- `_increase_public_balance` increases the public balance of an account when `unshield` is called +- `_increase_public_balance` increases the public balance of an account when `transfer_to_public` is called - `_reduce_total_supply` reduces the total supply of tokens when a token is privately burned To clarify, let's review some details of the Aztec transaction lifecycle, particularly how a transaction "moves through" these contexts. @@ -133,7 +133,6 @@ We are importing: - `CompressedString` to hold the token symbol - Types from `aztec::prelude` -- `compute_secret_hash` that will help with the shielding and unshielding, allowing someone to claim a token from private to public - Types for storing note types ### Types files @@ -206,7 +205,7 @@ First, storage is initialized. Then the function checks that the `msg_sender` is This public function allows an account approved in the public `minters` mapping to create new private tokens that can be claimed by anyone that has the pre-image to the `secret_hash`. -First, public storage is initialized. Then it checks that the `msg_sender` is an approved minter. Then a new `TransparentNote` is created with the specified `amount` and `secret_hash`. You can read the details of the `TransparentNote` in the `types.nr` file [here (GitHub link)](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/noir-projects/noir-contracts/contracts/token_contract/src/types.nr#L61). The `amount` is added to the existing public `total_supply` and the storage value is updated. Then the new `TransparentNote` is added to the `pending_shields` using the `insert_from_public` function, which is accessible on the `PrivateSet` type. Then it's ready to be claimed by anyone with the `secret_hash` pre-image using the `redeem_shield` function. +First, public storage is initialized. Then it checks that the `msg_sender` is an approved minter. Then a new `TransparentNote` is created with the specified `amount` and `secret_hash`. You can read the details of the `TransparentNote` in the `types.nr` file [here (GitHub link)](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/noir-projects/noir-contracts/contracts/token_contract/src/types.nr#L61). The `amount` is added to the existing public `total_supply` and the storage value is updated. Then the new `TransparentNote` is added to the `pending_shields` using the `insert_from_public` function, which is accessible on the `PrivateSet` type. Then it's ready to be claimed by anyone with the `secret_hash` pre-image using the `redeem_shield` function. #include_code mint_private /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust @@ -251,7 +250,7 @@ Private functions are declared with the `#[private]` macro above the function na fn redeem_shield( ``` -As described in the [execution contexts section above](#execution-contexts), private function logic and transaction information is hidden from the world and is executed on user devices. Private functions update private state, but can pass data to the public execution context (e.g. see the [`unshield`](#unshield) function). +As described in the [execution contexts section above](#execution-contexts), private function logic and transaction information is hidden from the world and is executed on user devices. Private functions update private state, but can pass data to the public execution context (e.g. see the [`transfer_to_public`](#transfer_to_public) function). Storage is referenced as `storage.variable`. @@ -265,7 +264,7 @@ The function returns `1` to indicate successful execution. #include_code redeem_shield /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust -#### `unshield` +#### `transfer_to_public` This private function enables un-shielding of private `UintNote`s stored in `balances` to any Aztec account's `public_balance`. @@ -273,7 +272,7 @@ After initializing storage, the function checks that the `msg_sender` is authori The function returns `1` to indicate successful execution. -#include_code unshield /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust +#include_code transfer_to_public /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust #### `transfer` @@ -303,7 +302,7 @@ Internal functions are functions that can only be called by this contract. The f #### `_increase_public_balance` -This function is called from [`unshield`](#unshield). The account's private balance is decremented in `shield` and the public balance is increased in this function. +This function is called from [`transfer_to_public`](#transfer_to_public). The account's private balance is decremented in `shield` and the public balance is increased in this function. #include_code increase_public_balance /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust diff --git a/docs/docs/tutorials/codealong/simple_dapp/3_contract_interaction.md b/docs/docs/tutorials/codealong/simple_dapp/3_contract_interaction.md index 660908b7010..769e53d6e00 100644 --- a/docs/docs/tutorials/codealong/simple_dapp/3_contract_interaction.md +++ b/docs/docs/tutorials/codealong/simple_dapp/3_contract_interaction.md @@ -104,7 +104,7 @@ While [private and public state](../../../aztec/concepts/state_model/index.md) a #include_code showPublicBalances yarn-project/end-to-end/src/sample-dapp/index.mjs javascript :::info -Since this we are working with pubic balances, we can now query the balance for any address, not just those registered in our local PXE. We can also send funds to addresses for which we don't know their [public encryption key](../../../aztec/concepts/accounts/keys.md#encryption-keys). +Since this we are working with public balances, we can now query the balance for any address, not just those registered in our local PXE. We can also send funds to addresses for which we don't know their [public encryption key](../../../aztec/concepts/accounts/keys.md#encryption-keys). ::: Here, since the token contract does not mint any initial funds upon deployment, the balances for all of our user's accounts will be zero. diff --git a/docs/docs/tutorials/examples/uniswap/l2_contract.md b/docs/docs/tutorials/examples/uniswap/l2_contract.md index da656ec2b2c..b8f9101e7a1 100644 --- a/docs/docs/tutorials/examples/uniswap/l2_contract.md +++ b/docs/docs/tutorials/examples/uniswap/l2_contract.md @@ -3,7 +3,7 @@ title: L2 Contracts (Aztec) sidebar_position: 1 --- -This page goes over the code in the L2 contract for Uniswap, which works alongside a [token bridge (codealong tutorial)](../../codealong/contract_tutorials/advanced/token_bridge/index.md). +This page goes over the code in the L2 contract for Uniswap, which works alongside a [token bridge (codealong tutorial)](../../codealong/contract_tutorials/advanced/token_bridge/index.md). ## Main.nr @@ -47,8 +47,8 @@ This uses a util function `compute_swap_private_content_hash()` - find that [her This flow works similarly to the public flow with a few notable changes: - Notice how in the `swap_private()`, user has to pass in `token` address which they didn't in the public flow? Since `swap_private()` is a private method, it can't read what token is publicly stored on the token bridge, so instead the user passes a token address, and `_assert_token_is_same()` checks that this user provided address is same as the one in storage. Note that because public functions are executed by the sequencer while private methods are executed locally, all public calls are always done after all private calls are done. So first the burn would happen and only later the sequencer asserts that the token is same. Note that the sequencer just sees a request to `execute_assert_token_is_same` and therefore has no context on what the appropriate private method was. If the assertion fails, then the kernel circuit will fail to create a proof and hence the transaction will be dropped. -- In the public flow, the user calls `transfer_public()`. Here instead, the user calls `unshield()`. Why? The user can't directly transfer their private tokens (their notes) to the uniswap contract, because later the Uniswap contract has to approve the bridge to burn these notes and withdraw to L1. The authwit flow for the private domain requires a signature from the `sender`, which in this case would be the Uniswap contract. For the contract to sign, it would need a private key associated to it. But who would operate this key? -- To work around this, the user can unshield their private tokens into Uniswap L2 contract. Unshielding would convert user's private notes to public balance. It is a private method on the token contract that reduces a user’s private balance and then calls a public method to increase the recipient’s (ie Uniswap) public balance. **Remember that first all private methods are executed and then later all public methods will be - so the Uniswap contract won’t have the funds until public execution begins.** +- In the public flow, the user calls `transfer_public()`. Here instead, the user calls `transfer_to_public()`. Why? The user can't directly transfer their private tokens (their notes) to the uniswap contract, because later the Uniswap contract has to approve the bridge to burn these notes and withdraw to L1. The authwit flow for the private domain requires a signature from the `sender`, which in this case would be the Uniswap contract. For the contract to sign, it would need a private key associated to it. But who would operate this key? +- To work around this, the user can transfer to public their private tokens into Uniswap L2 contract. Transferring to public would convert user's private notes to public balance. It is a private method on the token contract that reduces a user’s private balance and then calls a public method to increase the recipient’s (ie Uniswap) public balance. **Remember that first all private methods are executed and then later all public methods will be - so the Uniswap contract won’t have the funds until public execution begins.** - Now uniswap has public balance (like with the public flow). Hence, `swap_private()` calls the internal public method which approves the input token bridge to burn Uniswap’s tokens and calls `exit_to_l1_public` to create an L2 → L1 message to exit to L1. - Constructing the message content for swapping works exactly as the public flow except instead of specifying who would be the Aztec address that receives the swapped funds, we specify a secret hash (`secret_hash_for_redeeming_minted_notes`). Only those who know the preimage to the secret can later redeem the minted notes to themselves. diff --git a/l1-contracts/README.md b/l1-contracts/README.md index 149ae82027c..6adb41d2c7f 100644 --- a/l1-contracts/README.md +++ b/l1-contracts/README.md @@ -10,7 +10,7 @@ Alternatively you can use docker instead, it will handle installations and run t ## Structure -The `src` folder contain contracts that is to be used by the local developer testnet. It is grouped into 3 catagories: +The `src` folder contain contracts that is to be used by the local developer testnet. It is grouped into 3 categories: - `core` contains the required contracts, the bare minimum - `mock` contains stubs, for now an always true verifier. diff --git a/l1-contracts/src/core/FeeJuicePortal.sol b/l1-contracts/src/core/FeeJuicePortal.sol index e84c31ec082..59aad41c5e5 100644 --- a/l1-contracts/src/core/FeeJuicePortal.sol +++ b/l1-contracts/src/core/FeeJuicePortal.sol @@ -59,12 +59,12 @@ contract FeeJuicePortal is IFeeJuicePortal { * @param _to - The aztec address of the recipient * @param _amount - The amount to deposit * @param _secretHash - The hash of the secret consumable message. The hash should be 254 bits (so it can fit in a Field element) - * @return - The key of the entry in the Inbox + * @return - The key of the entry in the Inbox and its leaf index */ function depositToAztecPublic(bytes32 _to, uint256 _amount, bytes32 _secretHash) external override(IFeeJuicePortal) - returns (bytes32) + returns (bytes32, uint256) { // Preamble address rollup = canonicalRollup(); @@ -80,11 +80,11 @@ contract FeeJuicePortal is IFeeJuicePortal { UNDERLYING.safeTransferFrom(msg.sender, address(this), _amount); // Send message to rollup - bytes32 key = inbox.sendL2Message(actor, contentHash, _secretHash); + (bytes32 key, uint256 index) = inbox.sendL2Message(actor, contentHash, _secretHash); - emit DepositToAztecPublic(_to, _amount, _secretHash, key); + emit DepositToAztecPublic(_to, _amount, _secretHash, key, index); - return key; + return (key, index); } /** diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index 2be3634d580..9a627d5d690 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -121,15 +121,6 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { setupEpoch(); } - function quoteToDigest(EpochProofQuoteLib.EpochProofQuote memory quote) - public - view - override(IRollup) - returns (bytes32) - { - return _hashTypedDataV4(EpochProofQuoteLib.hash(quote)); - } - /** * @notice Prune the pending chain up to the last proven block * @@ -153,25 +144,6 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { assumeProvenThroughBlockNumber = blockNumber; } - function fakeBlockNumberAsProven(uint256 blockNumber) private { - if (blockNumber > tips.provenBlockNumber && blockNumber <= tips.pendingBlockNumber) { - tips.provenBlockNumber = blockNumber; - - // If this results on a new epoch, create a fake claim for it - // Otherwise nextEpochToProve will report an old epoch - Epoch epoch = getEpochForBlock(blockNumber); - if (Epoch.unwrap(epoch) == 0 || Epoch.unwrap(epoch) > Epoch.unwrap(proofClaim.epochToProve)) { - proofClaim = DataStructures.EpochProofClaim({ - epochToProve: epoch, - basisPointFee: 0, - bondAmount: 0, - bondProvider: address(0), - proposerClaimant: msg.sender - }); - } - } - } - /** * @notice Set the verifier contract * @@ -367,7 +339,8 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { Slot slot = getSlotAt(_ts); // Consider if a prune will hit in this slot - uint256 pendingBlockNumber = _canPruneAt(_ts) ? tips.provenBlockNumber : tips.pendingBlockNumber; + uint256 pendingBlockNumber = + _canPruneAtTime(_ts) ? tips.provenBlockNumber : tips.pendingBlockNumber; Slot lastSlot = blocks[pendingBlockNumber].slotNumber; @@ -441,7 +414,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { public override(IRollup) { - validateEpochProofRightClaim(_quote); + validateEpochProofRightClaimAtTime(Timestamp.wrap(block.timestamp), _quote); Slot currentSlot = getCurrentSlot(); Epoch epochToProve = getEpochToProve(); @@ -545,6 +518,15 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { } } + function quoteToDigest(EpochProofQuoteLib.EpochProofQuote memory quote) + public + view + override(IRollup) + returns (bytes32) + { + return _hashTypedDataV4(EpochProofQuoteLib.hash(quote)); + } + /** * @notice Returns the computed public inputs for the given epoch proof. * @@ -684,17 +666,21 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { return publicInputs; } - function validateEpochProofRightClaim(EpochProofQuoteLib.SignedEpochProofQuote calldata _quote) - public - view - override(IRollup) - { + function validateEpochProofRightClaimAtTime( + Timestamp _ts, + EpochProofQuoteLib.SignedEpochProofQuote calldata _quote + ) public view override(IRollup) { SignatureLib.verify(_quote.signature, _quote.quote.prover, quoteToDigest(_quote.quote)); - Slot currentSlot = getCurrentSlot(); - address currentProposer = getCurrentProposer(); + Slot currentSlot = getSlotAt(_ts); + address currentProposer = getProposerAt(_ts); Epoch epochToProve = getEpochToProve(); + require( + _quote.quote.validUntilSlot >= currentSlot, + Errors.Rollup__QuoteExpired(currentSlot, _quote.quote.validUntilSlot) + ); + require( _quote.quote.basisPointFee <= 10_000, Errors.Rollup__InvalidBasisPointFee(_quote.quote.basisPointFee) @@ -734,11 +720,6 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { _quote.quote.bondAmount <= availableFundsInEscrow, Errors.Rollup__InsufficientFundsInEscrow(_quote.quote.bondAmount, availableFundsInEscrow) ); - - require( - _quote.quote.validUntilSlot >= currentSlot, - Errors.Rollup__QuoteExpired(currentSlot, _quote.quote.validUntilSlot) - ); } /** @@ -794,6 +775,10 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { return bytes32(0); } + function canPrune() public view override(IRollup) returns (bool) { + return _canPruneAtTime(Timestamp.wrap(block.timestamp)); + } + function _prune() internal { // TODO #8656 delete proofClaim; @@ -809,11 +794,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { emit PrunedPending(tips.provenBlockNumber, pending); } - function canPrune() public view returns (bool) { - return _canPruneAt(Timestamp.wrap(block.timestamp)); - } - - function _canPruneAt(Timestamp _ts) internal view returns (bool) { + function _canPruneAtTime(Timestamp _ts) internal view returns (bool) { if ( tips.pendingBlockNumber == tips.provenBlockNumber || tips.pendingBlockNumber <= assumeProvenThroughBlockNumber @@ -861,7 +842,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { DataStructures.ExecutionFlags memory _flags ) internal view { uint256 pendingBlockNumber = - _canPruneAt(_currentTime) ? tips.provenBlockNumber : tips.pendingBlockNumber; + _canPruneAtTime(_currentTime) ? tips.provenBlockNumber : tips.pendingBlockNumber; _validateHeaderForSubmissionBase( _header, _currentTime, _txEffectsHash, pendingBlockNumber, _flags ); @@ -986,4 +967,23 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { require(_header.globalVariables.gasFees.feePerL2Gas == 0, Errors.Rollup__NonZeroL2Fee()); } } + + function fakeBlockNumberAsProven(uint256 blockNumber) private { + if (blockNumber > tips.provenBlockNumber && blockNumber <= tips.pendingBlockNumber) { + tips.provenBlockNumber = blockNumber; + + // If this results on a new epoch, create a fake claim for it + // Otherwise nextEpochToProve will report an old epoch + Epoch epoch = getEpochForBlock(blockNumber); + if (Epoch.unwrap(epoch) == 0 || Epoch.unwrap(epoch) > Epoch.unwrap(proofClaim.epochToProve)) { + proofClaim = DataStructures.EpochProofClaim({ + epochToProve: epoch, + basisPointFee: 0, + bondAmount: 0, + bondProvider: address(0), + proposerClaimant: msg.sender + }); + } + } + } } diff --git a/l1-contracts/src/core/interfaces/IFeeJuicePortal.sol b/l1-contracts/src/core/interfaces/IFeeJuicePortal.sol index 5537127f095..19de1638ac5 100644 --- a/l1-contracts/src/core/interfaces/IFeeJuicePortal.sol +++ b/l1-contracts/src/core/interfaces/IFeeJuicePortal.sol @@ -6,14 +6,16 @@ import {IERC20} from "@oz/token/ERC20/IERC20.sol"; import {IRegistry} from "@aztec/governance/interfaces/IRegistry.sol"; interface IFeeJuicePortal { - event DepositToAztecPublic(bytes32 indexed to, uint256 amount, bytes32 secretHash, bytes32 key); + event DepositToAztecPublic( + bytes32 indexed to, uint256 amount, bytes32 secretHash, bytes32 key, uint256 index + ); event FeesDistributed(address indexed to, uint256 amount); function initialize() external; function distributeFees(address _to, uint256 _amount) external; function depositToAztecPublic(bytes32 _to, uint256 _amount, bytes32 _secretHash) external - returns (bytes32); + returns (bytes32, uint256); function canonicalRollup() external view returns (address); function UNDERLYING() external view returns (IERC20); diff --git a/l1-contracts/src/core/interfaces/IRollup.sol b/l1-contracts/src/core/interfaces/IRollup.sol index 24dca3a436f..c3141e0fc40 100644 --- a/l1-contracts/src/core/interfaces/IRollup.sol +++ b/l1-contracts/src/core/interfaces/IRollup.sol @@ -32,8 +32,6 @@ interface IRollup { function prune() external; - function canPrune() external view returns (bool); - function claimEpochProofRight(EpochProofQuoteLib.SignedEpochProofQuote calldata _quote) external; function propose( @@ -102,14 +100,16 @@ interface IRollup { function archive() external view returns (bytes32); function archiveAt(uint256 _blockNumber) external view returns (bytes32); + function canPrune() external view returns (bool); function getProvenBlockNumber() external view returns (uint256); function getPendingBlockNumber() external view returns (uint256); function getEpochToProve() external view returns (Epoch); function getClaimableEpoch() external view returns (Epoch); function getEpochForBlock(uint256 blockNumber) external view returns (Epoch); - function validateEpochProofRightClaim(EpochProofQuoteLib.SignedEpochProofQuote calldata _quote) - external - view; + function validateEpochProofRightClaimAtTime( + Timestamp _ts, + EpochProofQuoteLib.SignedEpochProofQuote calldata _quote + ) external view; function getEpochProofPublicInputs( uint256 _epochSize, bytes32[7] calldata _args, diff --git a/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol b/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol index b399edb805f..0bceddbc7e8 100644 --- a/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol +++ b/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol @@ -25,13 +25,13 @@ interface IInbox { * @param _recipient - The recipient of the message * @param _content - The content of the message (application specific) * @param _secretHash - The secret hash of the message (make it possible to hide when a specific message is consumed on L2) - * @return The key of the message in the set + * @return The key of the message in the set and its leaf index in the tree */ function sendL2Message( DataStructures.L2Actor memory _recipient, bytes32 _content, bytes32 _secretHash - ) external returns (bytes32); + ) external returns (bytes32, uint256); // docs:end:send_l1_to_l2_message // docs:start:consume diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 8c42fe769d9..3c2ae9ebf1d 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -92,6 +92,7 @@ library Constants { uint256 internal constant FUNCTION_SELECTOR_NUM_BYTES = 4; uint256 internal constant INITIALIZATION_SLOT_SEPARATOR = 1000000000; uint256 internal constant INITIAL_L2_BLOCK_NUM = 1; + uint256 internal constant PRIVATE_LOG_SIZE_IN_BYTES = 576; uint256 internal constant BLOB_SIZE_IN_BYTES = 126976; uint256 internal constant ETHEREUM_SLOT_DURATION = 12; uint256 internal constant AZTEC_SLOT_DURATION = 24; diff --git a/l1-contracts/src/core/libraries/DataStructures.sol b/l1-contracts/src/core/libraries/DataStructures.sol index 537f3d6e7b2..61accf40655 100644 --- a/l1-contracts/src/core/libraries/DataStructures.sol +++ b/l1-contracts/src/core/libraries/DataStructures.sol @@ -41,12 +41,14 @@ library DataStructures { * @param recipient - The recipient of the message * @param content - The content of the message (application specific) padded to bytes32 or hashed if larger. * @param secretHash - The secret hash of the message (make it possible to hide when a specific message is consumed on L2). + * @param index - Global leaf index on the L1 to L2 messages tree. */ struct L1ToL2Msg { L1Actor sender; L2Actor recipient; bytes32 content; bytes32 secretHash; + uint256 index; } // docs:end:l1_to_l2_msg diff --git a/l1-contracts/src/core/libraries/crypto/Hash.sol b/l1-contracts/src/core/libraries/crypto/Hash.sol index df3f9467279..766df2b08e0 100644 --- a/l1-contracts/src/core/libraries/crypto/Hash.sol +++ b/l1-contracts/src/core/libraries/crypto/Hash.sol @@ -18,7 +18,9 @@ library Hash { */ function sha256ToField(DataStructures.L1ToL2Msg memory _message) internal pure returns (bytes32) { return sha256ToField( - abi.encode(_message.sender, _message.recipient, _message.content, _message.secretHash) + abi.encode( + _message.sender, _message.recipient, _message.content, _message.secretHash, _message.index + ) ); } diff --git a/l1-contracts/src/core/messagebridge/Inbox.sol b/l1-contracts/src/core/messagebridge/Inbox.sol index e8b32214104..ade70b5f321 100644 --- a/l1-contracts/src/core/messagebridge/Inbox.sol +++ b/l1-contracts/src/core/messagebridge/Inbox.sol @@ -57,13 +57,13 @@ contract Inbox is IInbox { * @param _content - The content of the message (application specific) * @param _secretHash - The secret hash of the message (make it possible to hide when a specific message is consumed on L2) * - * @return Hash of the sent message. + * @return Hash of the sent message and its leaf index in the tree. */ function sendL2Message( DataStructures.L2Actor memory _recipient, bytes32 _content, bytes32 _secretHash - ) external override(IInbox) returns (bytes32) { + ) external override(IInbox) returns (bytes32, uint256) { require( uint256(_recipient.actor) <= Constants.MAX_FIELD_VALUE, Errors.Inbox__ActorTooLarge(_recipient.actor) @@ -81,23 +81,25 @@ contract Inbox is IInbox { currentTree = trees[inProgress]; } + // this is the global leaf index and not index in the l2Block subtree + // such that users can simply use it and don't need access to a node if they are to consume it in public. + // trees are constant size so global index = tree number * size + subtree index + uint256 index = (inProgress - Constants.INITIAL_L2_BLOCK_NUM) * SIZE + currentTree.nextIndex; + DataStructures.L1ToL2Msg memory message = DataStructures.L1ToL2Msg({ sender: DataStructures.L1Actor(msg.sender, block.chainid), recipient: _recipient, content: _content, - secretHash: _secretHash + secretHash: _secretHash, + index: index }); bytes32 leaf = message.sha256ToField(); - // this is the global leaf index and not index in the l2Block subtree - // such that users can simply use it and don't need access to a node if they are to consume it in public. - // trees are constant size so global index = tree number * size + subtree index - uint256 index = - (inProgress - Constants.INITIAL_L2_BLOCK_NUM) * SIZE + currentTree.insertLeaf(leaf); + currentTree.insertLeaf(leaf); totalMessagesInserted++; emit MessageSent(inProgress, index, leaf); - return leaf; + return (leaf, index); } /** diff --git a/l1-contracts/src/mock/MockFeeJuicePortal.sol b/l1-contracts/src/mock/MockFeeJuicePortal.sol index 5227f60717d..ec90fda40f9 100644 --- a/l1-contracts/src/mock/MockFeeJuicePortal.sol +++ b/l1-contracts/src/mock/MockFeeJuicePortal.sol @@ -20,8 +20,13 @@ contract MockFeeJuicePortal is IFeeJuicePortal { function distributeFees(address, uint256) external override {} - function depositToAztecPublic(bytes32, uint256, bytes32) external pure override returns (bytes32) { - return bytes32(0); + function depositToAztecPublic(bytes32, uint256, bytes32) + external + pure + override + returns (bytes32, uint256) + { + return (bytes32(0), 0); } function canonicalRollup() external pure override returns (address) { diff --git a/l1-contracts/test/Inbox.t.sol b/l1-contracts/test/Inbox.t.sol index eb6e278ca42..0447d1dd38e 100644 --- a/l1-contracts/test/Inbox.t.sol +++ b/l1-contracts/test/Inbox.t.sol @@ -38,7 +38,8 @@ contract InboxTest is Test { version: version }), content: 0x2000000000000000000000000000000000000000000000000000000000000000, - secretHash: 0x3000000000000000000000000000000000000000000000000000000000000000 + secretHash: 0x3000000000000000000000000000000000000000000000000000000000000000, + index: 0x01 }); } @@ -46,7 +47,7 @@ contract InboxTest is Test { return (a + b - 1) / b; } - function _boundMessage(DataStructures.L1ToL2Msg memory _message) + function _boundMessage(DataStructures.L1ToL2Msg memory _message, uint256 _globalLeafIndex) internal view returns (DataStructures.L1ToL2Msg memory) @@ -61,6 +62,8 @@ contract InboxTest is Test { _message.secretHash = bytes32(uint256(_message.secretHash) % Constants.P); // update version _message.recipient.version = version; + // set leaf index + _message.index = _globalLeafIndex; return _message; } @@ -84,32 +87,40 @@ contract InboxTest is Test { } function testFuzzInsert(DataStructures.L1ToL2Msg memory _message) public checkInvariant { - DataStructures.L1ToL2Msg memory message = _boundMessage(_message); + uint256 globalLeafIndex = (FIRST_REAL_TREE_NUM - 1) * SIZE; + DataStructures.L1ToL2Msg memory message = _boundMessage(_message, globalLeafIndex); bytes32 leaf = message.sha256ToField(); vm.expectEmit(true, true, true, true); // event we expect - uint256 globalLeafIndex = (FIRST_REAL_TREE_NUM - 1) * SIZE; emit IInbox.MessageSent(FIRST_REAL_TREE_NUM, globalLeafIndex, leaf); // event we will get - bytes32 insertedLeaf = + (bytes32 insertedLeaf, uint256 insertedIndex) = inbox.sendL2Message(message.recipient, message.content, message.secretHash); assertEq(insertedLeaf, leaf); + assertEq(insertedIndex, globalLeafIndex); } function testSendDuplicateL2Messages() public checkInvariant { DataStructures.L1ToL2Msg memory message = _fakeMessage(); - bytes32 leaf1 = inbox.sendL2Message(message.recipient, message.content, message.secretHash); - bytes32 leaf2 = inbox.sendL2Message(message.recipient, message.content, message.secretHash); - bytes32 leaf3 = inbox.sendL2Message(message.recipient, message.content, message.secretHash); + (bytes32 leaf1, uint256 index1) = + inbox.sendL2Message(message.recipient, message.content, message.secretHash); + (bytes32 leaf2, uint256 index2) = + inbox.sendL2Message(message.recipient, message.content, message.secretHash); + (bytes32 leaf3, uint256 index3) = + inbox.sendL2Message(message.recipient, message.content, message.secretHash); // Only 1 tree should be non-zero assertEq(inbox.getNumTrees(), 1); - // All the leaves should be the same - assertEq(leaf1, leaf2); - assertEq(leaf2, leaf3); + // All the leaves should be different since the index gets mixed in + assertNotEq(leaf1, leaf2); + assertNotEq(leaf2, leaf3); + + // Check indices + assertEq(index1 + 1, index2); + assertEq(index1 + 2, index3); } function testRevertIfActorTooLarge() public { @@ -161,7 +172,8 @@ contract InboxTest is Test { // We send the messages and then check that toConsume root did not change. for (uint256 i = 0; i < _messages.length; i++) { - DataStructures.L1ToL2Msg memory message = _boundMessage(_messages[i]); + DataStructures.L1ToL2Msg memory message = + _boundMessage(_messages[i], inbox.getNextMessageIndex()); // We check whether a new tree is correctly initialized when the one in progress is full uint256 numTrees = inbox.getNumTrees(); diff --git a/l1-contracts/test/Rollup.t.sol b/l1-contracts/test/Rollup.t.sol index 6677ac68f06..457e3507845 100644 --- a/l1-contracts/test/Rollup.t.sol +++ b/l1-contracts/test/Rollup.t.sol @@ -115,6 +115,23 @@ contract RollupTest is DecoderBase { vm.warp(Timestamp.unwrap(rollup.getTimestampForSlot(Slot.wrap(_slot)))); } + function testClaimInTheFuture(uint256 _futureSlot) public setUpFor("mixed_block_1") { + uint256 futureSlot = bound(_futureSlot, 1, 1e20); + _testBlock("mixed_block_1", false, 1); + + rollup.validateEpochProofRightClaimAtTime(Timestamp.wrap(block.timestamp), signedQuote); + + Timestamp t = rollup.getTimestampForSlot(quote.validUntilSlot + Slot.wrap(futureSlot)); + vm.expectRevert( + abi.encodeWithSelector( + Errors.Rollup__QuoteExpired.selector, + Slot.wrap(futureSlot) + quote.validUntilSlot, + signedQuote.quote.validUntilSlot + ) + ); + rollup.validateEpochProofRightClaimAtTime(t, signedQuote); + } + function testClaimableEpoch(uint256 epochForMixedBlock) public setUpFor("mixed_block_1") { epochForMixedBlock = bound(epochForMixedBlock, 1, 10); vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__NoEpochToProve.selector)); @@ -266,6 +283,8 @@ contract RollupTest is DecoderBase { function testClaimTwice() public setUpFor("mixed_block_1") { _testBlock("mixed_block_1", false, 1); + quote.validUntilSlot = Epoch.wrap(1e9).toSlots(); + signedQuote = _quoteToSignedQuote(quote); rollup.claimEpochProofRight(signedQuote); @@ -291,7 +310,8 @@ contract RollupTest is DecoderBase { function testClaimOutsideClaimPhase() public setUpFor("mixed_block_1") { _testBlock("mixed_block_1", false, 1); - + quote.validUntilSlot = Epoch.wrap(1e9).toSlots(); + signedQuote = _quoteToSignedQuote(quote); warpToL2Slot(Constants.AZTEC_EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS()); vm.expectRevert( @@ -840,19 +860,6 @@ contract RollupTest is DecoderBase { _submitEpochProof(rollup, 1, preArchive, data.archive, preBlockHash, wrongBlockHash, bytes32(0)); } - function _quoteToSignedQuote(EpochProofQuoteLib.EpochProofQuote memory _quote) - internal - view - returns (EpochProofQuoteLib.SignedEpochProofQuote memory) - { - bytes32 digest = rollup.quoteToDigest(_quote); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); - return EpochProofQuoteLib.SignedEpochProofQuote({ - quote: _quote, - signature: SignatureLib.Signature({isEmpty: false, v: v, r: r, s: s}) - }); - } - function _testBlock(string memory name, bool _submitProof) public { _testBlock(name, _submitProof, 0); } @@ -998,4 +1005,17 @@ contract RollupTest is DecoderBase { _rollup.submitEpochRootProof(_epochSize, args, fees, aggregationObject, proof); } + + function _quoteToSignedQuote(EpochProofQuoteLib.EpochProofQuote memory _quote) + internal + view + returns (EpochProofQuoteLib.SignedEpochProofQuote memory) + { + bytes32 digest = rollup.quoteToDigest(_quote); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + return EpochProofQuoteLib.SignedEpochProofQuote({ + quote: _quote, + signature: SignatureLib.Signature({isEmpty: false, v: v, r: r, s: s}) + }); + } } diff --git a/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol b/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol index 21294aababc..b77bdfa3cbc 100644 --- a/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol +++ b/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol @@ -77,12 +77,14 @@ contract DepositToAztecPublic is Test { bytes32 to = bytes32(0x0); bytes32 secretHash = bytes32(uint256(0x01)); uint256 amount = 100 ether; + uint256 expectedIndex = 2 ** Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT; DataStructures.L1ToL2Msg memory message = DataStructures.L1ToL2Msg({ sender: DataStructures.L1Actor(address(feeJuicePortal), block.chainid), recipient: DataStructures.L2Actor(feeJuicePortal.L2_TOKEN_ADDRESS(), 1 + numberOfRollups), content: Hash.sha256ToField(abi.encodeWithSignature("claim(bytes32,uint256)", to, amount)), - secretHash: secretHash + secretHash: secretHash, + index: expectedIndex }); bytes32 expectedKey = message.sha256ToField(); @@ -92,16 +94,16 @@ contract DepositToAztecPublic is Test { Inbox inbox = Inbox(address(Rollup(address(registry.getRollup())).INBOX())); assertEq(inbox.totalMessagesInserted(), 0); - uint256 index = 2 ** Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT; vm.expectEmit(true, true, true, true, address(inbox)); - emit IInbox.MessageSent(2, index, expectedKey); + emit IInbox.MessageSent(2, expectedIndex, expectedKey); vm.expectEmit(true, true, true, true, address(feeJuicePortal)); - emit IFeeJuicePortal.DepositToAztecPublic(to, amount, secretHash, expectedKey); + emit IFeeJuicePortal.DepositToAztecPublic(to, amount, secretHash, expectedKey, expectedIndex); - bytes32 key = feeJuicePortal.depositToAztecPublic(to, amount, secretHash); + (bytes32 key, uint256 index) = feeJuicePortal.depositToAztecPublic(to, amount, secretHash); assertEq(inbox.totalMessagesInserted(), 1); assertEq(key, expectedKey); + assertEq(index, expectedIndex); } } diff --git a/l1-contracts/test/fixtures/empty_block_1.json b/l1-contracts/test/fixtures/empty_block_1.json index 990b1e5b8e7..796fc4cd040 100644 --- a/l1-contracts/test/fixtures/empty_block_1.json +++ b/l1-contracts/test/fixtures/empty_block_1.json @@ -8,8 +8,8 @@ "l2ToL1Messages": [] }, "block": { - "archive": "0x18589e53843baf9415aa9a4943bd4d8fa19b318329604f51a4ac0b8e7eb8e155", - "blockHash": "0x2662dae080808aceafe3377c439d8bea968d5ea74c98643adf5c2ea805f72c0c", + "archive": "0x04f48997a6e0472d7919af16c50859f86c041ef9aefceba4be94657943618ac1", + "blockHash": "0x2f3394755802dfe8105c0e7a5a7733970b59d83f6b9bd6eb754317f4d6a73c0f", "body": "0x00000000", "txsEffectsHash": "0x00e994e16b3763fd5039413cf99c2b3c378e2bab939e7992a77bd201b28160d6", "decodedHeader": { @@ -21,12 +21,12 @@ }, "globalVariables": { "blockNumber": 1, - "slotNumber": "0x0000000000000000000000000000000000000000000000000000000000000011", + "slotNumber": "0x0000000000000000000000000000000000000000000000000000000000000012", "chainId": 31337, - "timestamp": 1728563130, + "timestamp": 1730234580, "version": 1, - "coinbase": "0xd498444361455d5099a036e93f395a584cf085c6", - "feeRecipient": "0x07c66a9b410a7e26824d677a12d8af977c0db64e5fcdfc8ad704c71c0267d649", + "coinbase": "0x2590e3544a7e2e7d736649fefc72ea5ad1d6efc3", + "feeRecipient": "0x2146e005e28b76eedc61edeff431b412b5ece74a5818080051832d286795ce2e", "gasFees": { "feePerDaGas": 0, "feePerL2Gas": 0 @@ -57,8 +57,8 @@ } } }, - "header": "0x1200a06aae1368abe36530b585bd7a4d2ba4de5037b82076412691a187d7621e00000001000000000000000000000000000000000000000000000000000000000000000200e994e16b3763fd5039413cf99c2b3c378e2bab939e7992a77bd201b28160d600089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb14f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb3000000100b59baa35b9dc267744f0ccb4e3b0255c1fc512460d91130c6bc19fb2668568d0000008019a8c197c12bb33da6314c4ef4f8f6fcb9e25250c085df8672adf67c8f1e3dbc0000010023c08a6b1297210c5e24c76b9a936250a1ce2721576c26ea797c7ec35f9e46a9000001000000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000006707c7bad498444361455d5099a036e93f395a584cf085c607c66a9b410a7e26824d677a12d8af977c0db64e5fcdfc8ad704c71c0267d649000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "publicInputsHash": "0x00e081da850e07688f3b8eac898caaeb80fabf215e1c87bbecc180d88a19f34e", + "header": "0x1200a06aae1368abe36530b585bd7a4d2ba4de5037b82076412691a187d7621e00000001000000000000000000000000000000000000000000000000000000000000000200e994e16b3763fd5039413cf99c2b3c378e2bab939e7992a77bd201b28160d600089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb14f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb3000000100b59baa35b9dc267744f0ccb4e3b0255c1fc512460d91130c6bc19fb2668568d0000008019a8c197c12bb33da6314c4ef4f8f6fcb9e25250c085df8672adf67c8f1e3dbc0000010023c08a6b1297210c5e24c76b9a936250a1ce2721576c26ea797c7ec35f9e46a9000001000000000000000000000000000000000000000000000000000000000000007a6900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000672148d42590e3544a7e2e7d736649fefc72ea5ad1d6efc32146e005e28b76eedc61edeff431b412b5ece74a5818080051832d286795ce2e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "publicInputsHash": "0x0086289eca84479d5e34db9c4548bdd156af80fb725c51219650aa4460c80240", "numTxs": 0 } } \ No newline at end of file diff --git a/l1-contracts/test/fixtures/empty_block_2.json b/l1-contracts/test/fixtures/empty_block_2.json index 7d4da40c666..de81f67a810 100644 --- a/l1-contracts/test/fixtures/empty_block_2.json +++ b/l1-contracts/test/fixtures/empty_block_2.json @@ -8,8 +8,8 @@ "l2ToL1Messages": [] }, "block": { - "archive": "0x1d3dc82359e412945750080a10eede5c3ee3cb9360dc3ca35dda2c5f2e296c52", - "blockHash": "0x15fb8c7900432692f9f4e590e6df42d6fff4ce24f0b720514d6bc30ba234d548", + "archive": "0x07d7359b2b2922b8126019bef4f2d5698f25226f5f2270a79d1105503727dd6e", + "blockHash": "0x1836f56b16db44064b3231102c84c6eaf3327d0204a5171f65b3019fc79c2b5e", "body": "0x00000000", "txsEffectsHash": "0x00e994e16b3763fd5039413cf99c2b3c378e2bab939e7992a77bd201b28160d6", "decodedHeader": { @@ -21,12 +21,12 @@ }, "globalVariables": { "blockNumber": 2, - "slotNumber": "0x0000000000000000000000000000000000000000000000000000000000000012", + "slotNumber": "0x0000000000000000000000000000000000000000000000000000000000000013", "chainId": 31337, - "timestamp": 1728563154, + "timestamp": 1730234604, "version": 1, - "coinbase": "0xd498444361455d5099a036e93f395a584cf085c6", - "feeRecipient": "0x07c66a9b410a7e26824d677a12d8af977c0db64e5fcdfc8ad704c71c0267d649", + "coinbase": "0x2590e3544a7e2e7d736649fefc72ea5ad1d6efc3", + "feeRecipient": "0x2146e005e28b76eedc61edeff431b412b5ece74a5818080051832d286795ce2e", "gasFees": { "feePerDaGas": 0, "feePerL2Gas": 0 @@ -34,7 +34,7 @@ }, "lastArchive": { "nextAvailableLeafIndex": 2, - "root": "0x18589e53843baf9415aa9a4943bd4d8fa19b318329604f51a4ac0b8e7eb8e155" + "root": "0x04f48997a6e0472d7919af16c50859f86c041ef9aefceba4be94657943618ac1" }, "stateReference": { "l1ToL2MessageTree": { @@ -57,8 +57,8 @@ } } }, - "header": "0x18589e53843baf9415aa9a4943bd4d8fa19b318329604f51a4ac0b8e7eb8e15500000002000000000000000000000000000000000000000000000000000000000000000200e994e16b3763fd5039413cf99c2b3c378e2bab939e7992a77bd201b28160d600089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb14f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb3000000200b59baa35b9dc267744f0ccb4e3b0255c1fc512460d91130c6bc19fb2668568d0000010019a8c197c12bb33da6314c4ef4f8f6fcb9e25250c085df8672adf67c8f1e3dbc0000018023c08a6b1297210c5e24c76b9a936250a1ce2721576c26ea797c7ec35f9e46a9000001800000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000006707c7d2d498444361455d5099a036e93f395a584cf085c607c66a9b410a7e26824d677a12d8af977c0db64e5fcdfc8ad704c71c0267d649000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "publicInputsHash": "0x00815a814be4b3e0ac1a671e1a0321c231ac90e23ef7b2b1e411d32a2fb1948d", + "header": "0x04f48997a6e0472d7919af16c50859f86c041ef9aefceba4be94657943618ac100000002000000000000000000000000000000000000000000000000000000000000000200e994e16b3763fd5039413cf99c2b3c378e2bab939e7992a77bd201b28160d600089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb14f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb3000000200b59baa35b9dc267744f0ccb4e3b0255c1fc512460d91130c6bc19fb2668568d0000010019a8c197c12bb33da6314c4ef4f8f6fcb9e25250c085df8672adf67c8f1e3dbc0000018023c08a6b1297210c5e24c76b9a936250a1ce2721576c26ea797c7ec35f9e46a9000001800000000000000000000000000000000000000000000000000000000000007a6900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000672148ec2590e3544a7e2e7d736649fefc72ea5ad1d6efc32146e005e28b76eedc61edeff431b412b5ece74a5818080051832d286795ce2e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "publicInputsHash": "0x009727b9d0f6c22ea711c2e31249776bd687ffa0eaf97b54120af68c96da0eda", "numTxs": 0 } } \ No newline at end of file diff --git a/l1-contracts/test/fixtures/mixed_block_1.json b/l1-contracts/test/fixtures/mixed_block_1.json index 6caa935c1d7..29d5521d59b 100644 --- a/l1-contracts/test/fixtures/mixed_block_1.json +++ b/l1-contracts/test/fixtures/mixed_block_1.json @@ -58,8 +58,8 @@ ] }, "block": { - "archive": "0x24c11def12fd4dbe0974daf2b642f44404b96f4bfac4ab9c990c911e7bd96eaa", - "blockHash": "0x2586fea74850f3a459cf291ab83242733b67e885e23aa0cc2a8ca169e78dc97c", + "archive": "0x12e65b031a1821b8ed8d2934a32482f660c0fb9d10557f476efb616c0651c75b", + "blockHash": "0x22a46343d98db8051fed1c42153e8c0a29174978493d65d27b93432506bb8029", "body": "0x00000004000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014100000000000000000000000000000000000000000000000000000000000001420000000000000000000000000000000000000000000000000000000000000143000000000000000000000000000000000000000000000000000000000000014400000000000000000000000000000000000000000000000000000000000001450000000000000000000000000000000000000000000000000000000000000146000000000000000000000000000000000000000000000000000000000000014700000000000000000000000000000000000000000000000000000000000001480000000000000000000000000000000000000000000000000000000000000149000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014b000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000014d000000000000000000000000000000000000000000000000000000000000014e000000000000000000000000000000000000000000000000000000000000014f0000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000015100000000000000000000000000000000000000000000000000000000000001520000000000000000000000000000000000000000000000000000000000000153000000000000000000000000000000000000000000000000000000000000015400000000000000000000000000000000000000000000000000000000000001550000000000000000000000000000000000000000000000000000000000000156000000000000000000000000000000000000000000000000000000000000015700000000000000000000000000000000000000000000000000000000000001580000000000000000000000000000000000000000000000000000000000000159000000000000000000000000000000000000000000000000000000000000015a000000000000000000000000000000000000000000000000000000000000015b000000000000000000000000000000000000000000000000000000000000015c000000000000000000000000000000000000000000000000000000000000015d000000000000000000000000000000000000000000000000000000000000015e000000000000000000000000000000000000000000000000000000000000015f0000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000001620000000000000000000000000000000000000000000000000000000000000163000000000000000000000000000000000000000000000000000000000000016400000000000000000000000000000000000000000000000000000000000001650000000000000000000000000000000000000000000000000000000000000166000000000000000000000000000000000000000000000000000000000000016700000000000000000000000000000000000000000000000000000000000001680000000000000000000000000000000000000000000000000000000000000169000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016b000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000000016d000000000000000000000000000000000000000000000000000000000000016e000000000000000000000000000000000000000000000000000000000000016f0000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000017100000000000000000000000000000000000000000000000000000000000001720000000000000000000000000000000000000000000000000000000000000173000000000000000000000000000000000000000000000000000000000000017400000000000000000000000000000000000000000000000000000000000001750000000000000000000000000000000000000000000000000000000000000176000000000000000000000000000000000000000000000000000000000000017700000000000000000000000000000000000000000000000000000000000001780000000000000000000000000000000000000000000000000000000000000179000000000000000000000000000000000000000000000000000000000000017a000000000000000000000000000000000000000000000000000000000000017b000000000000000000000000000000000000000000000000000000000000017c000000000000000000000000000000000000000000000000000000000000017d000000000000000000000000000000000000000000000000000000000000017e000000000000000000000000000000000000000000000000000000000000017f3f0000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000024100000000000000000000000000000000000000000000000000000000000002420000000000000000000000000000000000000000000000000000000000000243000000000000000000000000000000000000000000000000000000000000024400000000000000000000000000000000000000000000000000000000000002450000000000000000000000000000000000000000000000000000000000000246000000000000000000000000000000000000000000000000000000000000024700000000000000000000000000000000000000000000000000000000000002480000000000000000000000000000000000000000000000000000000000000249000000000000000000000000000000000000000000000000000000000000024a000000000000000000000000000000000000000000000000000000000000024b000000000000000000000000000000000000000000000000000000000000024c000000000000000000000000000000000000000000000000000000000000024d000000000000000000000000000000000000000000000000000000000000024e000000000000000000000000000000000000000000000000000000000000024f0000000000000000000000000000000000000000000000000000000000000250000000000000000000000000000000000000000000000000000000000000025100000000000000000000000000000000000000000000000000000000000002520000000000000000000000000000000000000000000000000000000000000253000000000000000000000000000000000000000000000000000000000000025400000000000000000000000000000000000000000000000000000000000002550000000000000000000000000000000000000000000000000000000000000256000000000000000000000000000000000000000000000000000000000000025700000000000000000000000000000000000000000000000000000000000002580000000000000000000000000000000000000000000000000000000000000259000000000000000000000000000000000000000000000000000000000000025a000000000000000000000000000000000000000000000000000000000000025b000000000000000000000000000000000000000000000000000000000000025c000000000000000000000000000000000000000000000000000000000000025d000000000000000000000000000000000000000000000000000000000000025e000000000000000000000000000000000000000000000000000000000000025f0000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000026100000000000000000000000000000000000000000000000000000000000002620000000000000000000000000000000000000000000000000000000000000263000000000000000000000000000000000000000000000000000000000000026400000000000000000000000000000000000000000000000000000000000002650000000000000000000000000000000000000000000000000000000000000266000000000000000000000000000000000000000000000000000000000000026700000000000000000000000000000000000000000000000000000000000002680000000000000000000000000000000000000000000000000000000000000269000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000026b000000000000000000000000000000000000000000000000000000000000026c000000000000000000000000000000000000000000000000000000000000026d000000000000000000000000000000000000000000000000000000000000026e000000000000000000000000000000000000000000000000000000000000026f0000000000000000000000000000000000000000000000000000000000000270000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000002720000000000000000000000000000000000000000000000000000000000000273000000000000000000000000000000000000000000000000000000000000027400000000000000000000000000000000000000000000000000000000000002750000000000000000000000000000000000000000000000000000000000000276000000000000000000000000000000000000000000000000000000000000027700000000000000000000000000000000000000000000000000000000000002780000000000000000000000000000000000000000000000000000000000000279000000000000000000000000000000000000000000000000000000000000027a000000000000000000000000000000000000000000000000000000000000027b000000000000000000000000000000000000000000000000000000000000027c000000000000000000000000000000000000000000000000000000000000027d000000000000000000000000000000000000000000000000000000000000027e0800c47667396742a5474f325e2567bff3bb99b7f0bfd2b1a689b635d8b8726cce00284120278895e8d47084ae759f390e9634881e41369a257a36fe99a2369dc800a328b4a4a6ed156325253b4ab2af7ca00e21caf7963f0fba8a88ccdc3512c300705c99df420bface231f6855799db1d0ed7d39419abc47aa8c6efe2ae7aae2009299cc308de6d23788384411e024791a5b2448e455fbdd1d1f28f3ff76631f002d6c03d81ad764de51b0f34584645191cdc2aaae2ca08fb838d142b95d62f5003032f3618b2df0fa335d5fd548d6d85e42b4e7eb5fff9eb687facbbdecb8a60016cab7ddf4d1b440d53d10284c5c82a78b2d4e27dcdb44ef434ef4c6bad6783f0000000000000000000000000000000000000000000000000000000000000540000000000000000000000000000000000000000000000000000000000000054a0000000000000000000000000000000000000000000000000000000000000541000000000000000000000000000000000000000000000000000000000000054b0000000000000000000000000000000000000000000000000000000000000542000000000000000000000000000000000000000000000000000000000000054c0000000000000000000000000000000000000000000000000000000000000543000000000000000000000000000000000000000000000000000000000000054d0000000000000000000000000000000000000000000000000000000000000544000000000000000000000000000000000000000000000000000000000000054e0000000000000000000000000000000000000000000000000000000000000545000000000000000000000000000000000000000000000000000000000000054f00000000000000000000000000000000000000000000000000000000000005460000000000000000000000000000000000000000000000000000000000000550000000000000000000000000000000000000000000000000000000000000054700000000000000000000000000000000000000000000000000000000000005510000000000000000000000000000000000000000000000000000000000000548000000000000000000000000000000000000000000000000000000000000055200000000000000000000000000000000000000000000000000000000000005490000000000000000000000000000000000000000000000000000000000000553000000000000000000000000000000000000000000000000000000000000054a0000000000000000000000000000000000000000000000000000000000000554000000000000000000000000000000000000000000000000000000000000054b0000000000000000000000000000000000000000000000000000000000000555000000000000000000000000000000000000000000000000000000000000054c0000000000000000000000000000000000000000000000000000000000000556000000000000000000000000000000000000000000000000000000000000054d0000000000000000000000000000000000000000000000000000000000000557000000000000000000000000000000000000000000000000000000000000054e0000000000000000000000000000000000000000000000000000000000000558000000000000000000000000000000000000000000000000000000000000054f00000000000000000000000000000000000000000000000000000000000005590000000000000000000000000000000000000000000000000000000000000550000000000000000000000000000000000000000000000000000000000000055a0000000000000000000000000000000000000000000000000000000000000551000000000000000000000000000000000000000000000000000000000000055b0000000000000000000000000000000000000000000000000000000000000552000000000000000000000000000000000000000000000000000000000000055c0000000000000000000000000000000000000000000000000000000000000553000000000000000000000000000000000000000000000000000000000000055d0000000000000000000000000000000000000000000000000000000000000554000000000000000000000000000000000000000000000000000000000000055e0000000000000000000000000000000000000000000000000000000000000555000000000000000000000000000000000000000000000000000000000000055f00000000000000000000000000000000000000000000000000000000000005560000000000000000000000000000000000000000000000000000000000000560000000000000000000000000000000000000000000000000000000000000055700000000000000000000000000000000000000000000000000000000000005610000000000000000000000000000000000000000000000000000000000000558000000000000000000000000000000000000000000000000000000000000056200000000000000000000000000000000000000000000000000000000000005590000000000000000000000000000000000000000000000000000000000000563000000000000000000000000000000000000000000000000000000000000055a0000000000000000000000000000000000000000000000000000000000000564000000000000000000000000000000000000000000000000000000000000055b0000000000000000000000000000000000000000000000000000000000000565000000000000000000000000000000000000000000000000000000000000055c0000000000000000000000000000000000000000000000000000000000000566000000000000000000000000000000000000000000000000000000000000055d0000000000000000000000000000000000000000000000000000000000000567000000000000000000000000000000000000000000000000000000000000055e0000000000000000000000000000000000000000000000000000000000000568000000000000000000000000000000000000000000000000000000000000055f00000000000000000000000000000000000000000000000000000000000005690000000000000000000000000000000000000000000000000000000000000560000000000000000000000000000000000000000000000000000000000000056a0000000000000000000000000000000000000000000000000000000000000561000000000000000000000000000000000000000000000000000000000000056b0000000000000000000000000000000000000000000000000000000000000562000000000000000000000000000000000000000000000000000000000000056c0000000000000000000000000000000000000000000000000000000000000563000000000000000000000000000000000000000000000000000000000000056d0000000000000000000000000000000000000000000000000000000000000564000000000000000000000000000000000000000000000000000000000000056e0000000000000000000000000000000000000000000000000000000000000565000000000000000000000000000000000000000000000000000000000000056f00000000000000000000000000000000000000000000000000000000000005660000000000000000000000000000000000000000000000000000000000000570000000000000000000000000000000000000000000000000000000000000056700000000000000000000000000000000000000000000000000000000000005710000000000000000000000000000000000000000000000000000000000000568000000000000000000000000000000000000000000000000000000000000057200000000000000000000000000000000000000000000000000000000000005690000000000000000000000000000000000000000000000000000000000000573000000000000000000000000000000000000000000000000000000000000056a0000000000000000000000000000000000000000000000000000000000000574000000000000000000000000000000000000000000000000000000000000056b0000000000000000000000000000000000000000000000000000000000000575000000000000000000000000000000000000000000000000000000000000056c0000000000000000000000000000000000000000000000000000000000000576000000000000000000000000000000000000000000000000000000000000056d0000000000000000000000000000000000000000000000000000000000000577000000000000000000000000000000000000000000000000000000000000056e0000000000000000000000000000000000000000000000000000000000000578000000000000000000000000000000000000000000000000000000000000056f00000000000000000000000000000000000000000000000000000000000005790000000000000000000000000000000000000000000000000000000000000570000000000000000000000000000000000000000000000000000000000000057a0000000000000000000000000000000000000000000000000000000000000571000000000000000000000000000000000000000000000000000000000000057b0000000000000000000000000000000000000000000000000000000000000572000000000000000000000000000000000000000000000000000000000000057c0000000000000000000000000000000000000000000000000000000000000573000000000000000000000000000000000000000000000000000000000000057d0000000000000000000000000000000000000000000000000000000000000574000000000000000000000000000000000000000000000000000000000000057e0000000000000000000000000000000000000000000000000000000000000575000000000000000000000000000000000000000000000000000000000000057f00000000000000000000000000000000000000000000000000000000000005760000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000057700000000000000000000000000000000000000000000000000000000000005810000000000000000000000000000000000000000000000000000000000000578000000000000000000000000000000000000000000000000000000000000058200000000000000000000000000000000000000000000000000000000000005790000000000000000000000000000000000000000000000000000000000000583000000000000000000000000000000000000000000000000000000000000057a0000000000000000000000000000000000000000000000000000000000000584000000000000000000000000000000000000000000000000000000000000057b0000000000000000000000000000000000000000000000000000000000000585000000000000000000000000000000000000000000000000000000000000057c0000000000000000000000000000000000000000000000000000000000000586000000000000000000000000000000000000000000000000000000000000057d0000000000000000000000000000000000000000000000000000000000000587000000000000000000000000000000000000000000000000000000000000057e0000000000000000000000000000000000000000000000000000000000000588000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000018100000000000000000000000000000000000000000000000000000000000001820000000000000000000000000000000000000000000000000000000000000183000000000000000000000000000000000000000000000000000000000000018400000000000000000000000000000000000000000000000000000000000001850000000000000000000000000000000000000000000000000000000000000186000000000000000000000000000000000000000000000000000000000000018700000000000000000000000000000000000000000000000000000000000001880000000000000000000000000000000000000000000000000000000000000189000000000000000000000000000000000000000000000000000000000000018a000000000000000000000000000000000000000000000000000000000000018b000000000000000000000000000000000000000000000000000000000000018c000000000000000000000000000000000000000000000000000000000000018d000000000000000000000000000000000000000000000000000000000000018e000000000000000000000000000000000000000000000000000000000000018f0000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000000019100000000000000000000000000000000000000000000000000000000000001920000000000000000000000000000000000000000000000000000000000000193000000000000000000000000000000000000000000000000000000000000019400000000000000000000000000000000000000000000000000000000000001950000000000000000000000000000000000000000000000000000000000000196000000000000000000000000000000000000000000000000000000000000019700000000000000000000000000000000000000000000000000000000000001980000000000000000000000000000000000000000000000000000000000000199000000000000000000000000000000000000000000000000000000000000019a000000000000000000000000000000000000000000000000000000000000019b000000000000000000000000000000000000000000000000000000000000019c000000000000000000000000000000000000000000000000000000000000019d000000000000000000000000000000000000000000000000000000000000019e000000000000000000000000000000000000000000000000000000000000019f00000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001a100000000000000000000000000000000000000000000000000000000000001a200000000000000000000000000000000000000000000000000000000000001a300000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000000001a500000000000000000000000000000000000000000000000000000000000001a600000000000000000000000000000000000000000000000000000000000001a700000000000000000000000000000000000000000000000000000000000001a800000000000000000000000000000000000000000000000000000000000001a900000000000000000000000000000000000000000000000000000000000001aa00000000000000000000000000000000000000000000000000000000000001ab00000000000000000000000000000000000000000000000000000000000001ac00000000000000000000000000000000000000000000000000000000000001ad00000000000000000000000000000000000000000000000000000000000001ae00000000000000000000000000000000000000000000000000000000000001af00000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b100000000000000000000000000000000000000000000000000000000000001b200000000000000000000000000000000000000000000000000000000000001b300000000000000000000000000000000000000000000000000000000000001b400000000000000000000000000000000000000000000000000000000000001b500000000000000000000000000000000000000000000000000000000000001b600000000000000000000000000000000000000000000000000000000000001b700000000000000000000000000000000000000000000000000000000000001b800000000000000000000000000000000000000000000000000000000000001b900000000000000000000000000000000000000000000000000000000000001ba00000000000000000000000000000000000000000000000000000000000001bb00000000000000000000000000000000000000000000000000000000000001bc00000000000000000000000000000000000000000000000000000000000001bd00000000000000000000000000000000000000000000000000000000000001be00000000000000000000000000000000000000000000000000000000000001bf3f0000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000028100000000000000000000000000000000000000000000000000000000000002820000000000000000000000000000000000000000000000000000000000000283000000000000000000000000000000000000000000000000000000000000028400000000000000000000000000000000000000000000000000000000000002850000000000000000000000000000000000000000000000000000000000000286000000000000000000000000000000000000000000000000000000000000028700000000000000000000000000000000000000000000000000000000000002880000000000000000000000000000000000000000000000000000000000000289000000000000000000000000000000000000000000000000000000000000028a000000000000000000000000000000000000000000000000000000000000028b000000000000000000000000000000000000000000000000000000000000028c000000000000000000000000000000000000000000000000000000000000028d000000000000000000000000000000000000000000000000000000000000028e000000000000000000000000000000000000000000000000000000000000028f0000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000000000000000000000000000000029100000000000000000000000000000000000000000000000000000000000002920000000000000000000000000000000000000000000000000000000000000293000000000000000000000000000000000000000000000000000000000000029400000000000000000000000000000000000000000000000000000000000002950000000000000000000000000000000000000000000000000000000000000296000000000000000000000000000000000000000000000000000000000000029700000000000000000000000000000000000000000000000000000000000002980000000000000000000000000000000000000000000000000000000000000299000000000000000000000000000000000000000000000000000000000000029a000000000000000000000000000000000000000000000000000000000000029b000000000000000000000000000000000000000000000000000000000000029c000000000000000000000000000000000000000000000000000000000000029d000000000000000000000000000000000000000000000000000000000000029e000000000000000000000000000000000000000000000000000000000000029f00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002a100000000000000000000000000000000000000000000000000000000000002a200000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000002a400000000000000000000000000000000000000000000000000000000000002a500000000000000000000000000000000000000000000000000000000000002a600000000000000000000000000000000000000000000000000000000000002a700000000000000000000000000000000000000000000000000000000000002a800000000000000000000000000000000000000000000000000000000000002a900000000000000000000000000000000000000000000000000000000000002aa00000000000000000000000000000000000000000000000000000000000002ab00000000000000000000000000000000000000000000000000000000000002ac00000000000000000000000000000000000000000000000000000000000002ad00000000000000000000000000000000000000000000000000000000000002ae00000000000000000000000000000000000000000000000000000000000002af00000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002b100000000000000000000000000000000000000000000000000000000000002b200000000000000000000000000000000000000000000000000000000000002b300000000000000000000000000000000000000000000000000000000000002b400000000000000000000000000000000000000000000000000000000000002b500000000000000000000000000000000000000000000000000000000000002b600000000000000000000000000000000000000000000000000000000000002b700000000000000000000000000000000000000000000000000000000000002b800000000000000000000000000000000000000000000000000000000000002b900000000000000000000000000000000000000000000000000000000000002ba00000000000000000000000000000000000000000000000000000000000002bb00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bd00000000000000000000000000000000000000000000000000000000000002be0800789ff73d7787206612d96dfc2143f2344de21669a3f8cae7fe9a8918631eb00084a17f00bf8793b6851a106e9155543125e0be987ad3c8334456bdda171d0b00a400f8fd336ad84f467465964008238fd1b7f9c51c22912d706cd2b874d24e002c79bdd83c14ff50a46964f838ee207564909e28af79a57fc195810d36f9b20070083c6ef1e4dd88a064e94d2582283b203cf8a2ab1667f4370eda1b4c1fe8005373dffb5b590053d7762efcf9e11280f1486ce82e7996d94ee0f5d7c093bc009eefd90eb40e79c78bac1f71ec78bdc2f8b30041974239bdc765edffed813800ea95742e72792ca7a0f66ce9f55bc47dc09d5ea08c1b9018763102776978303f0000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000058a0000000000000000000000000000000000000000000000000000000000000581000000000000000000000000000000000000000000000000000000000000058b0000000000000000000000000000000000000000000000000000000000000582000000000000000000000000000000000000000000000000000000000000058c0000000000000000000000000000000000000000000000000000000000000583000000000000000000000000000000000000000000000000000000000000058d0000000000000000000000000000000000000000000000000000000000000584000000000000000000000000000000000000000000000000000000000000058e0000000000000000000000000000000000000000000000000000000000000585000000000000000000000000000000000000000000000000000000000000058f00000000000000000000000000000000000000000000000000000000000005860000000000000000000000000000000000000000000000000000000000000590000000000000000000000000000000000000000000000000000000000000058700000000000000000000000000000000000000000000000000000000000005910000000000000000000000000000000000000000000000000000000000000588000000000000000000000000000000000000000000000000000000000000059200000000000000000000000000000000000000000000000000000000000005890000000000000000000000000000000000000000000000000000000000000593000000000000000000000000000000000000000000000000000000000000058a0000000000000000000000000000000000000000000000000000000000000594000000000000000000000000000000000000000000000000000000000000058b0000000000000000000000000000000000000000000000000000000000000595000000000000000000000000000000000000000000000000000000000000058c0000000000000000000000000000000000000000000000000000000000000596000000000000000000000000000000000000000000000000000000000000058d0000000000000000000000000000000000000000000000000000000000000597000000000000000000000000000000000000000000000000000000000000058e0000000000000000000000000000000000000000000000000000000000000598000000000000000000000000000000000000000000000000000000000000058f00000000000000000000000000000000000000000000000000000000000005990000000000000000000000000000000000000000000000000000000000000590000000000000000000000000000000000000000000000000000000000000059a0000000000000000000000000000000000000000000000000000000000000591000000000000000000000000000000000000000000000000000000000000059b0000000000000000000000000000000000000000000000000000000000000592000000000000000000000000000000000000000000000000000000000000059c0000000000000000000000000000000000000000000000000000000000000593000000000000000000000000000000000000000000000000000000000000059d0000000000000000000000000000000000000000000000000000000000000594000000000000000000000000000000000000000000000000000000000000059e0000000000000000000000000000000000000000000000000000000000000595000000000000000000000000000000000000000000000000000000000000059f000000000000000000000000000000000000000000000000000000000000059600000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000059700000000000000000000000000000000000000000000000000000000000005a1000000000000000000000000000000000000000000000000000000000000059800000000000000000000000000000000000000000000000000000000000005a2000000000000000000000000000000000000000000000000000000000000059900000000000000000000000000000000000000000000000000000000000005a3000000000000000000000000000000000000000000000000000000000000059a00000000000000000000000000000000000000000000000000000000000005a4000000000000000000000000000000000000000000000000000000000000059b00000000000000000000000000000000000000000000000000000000000005a5000000000000000000000000000000000000000000000000000000000000059c00000000000000000000000000000000000000000000000000000000000005a6000000000000000000000000000000000000000000000000000000000000059d00000000000000000000000000000000000000000000000000000000000005a7000000000000000000000000000000000000000000000000000000000000059e00000000000000000000000000000000000000000000000000000000000005a8000000000000000000000000000000000000000000000000000000000000059f00000000000000000000000000000000000000000000000000000000000005a900000000000000000000000000000000000000000000000000000000000005a000000000000000000000000000000000000000000000000000000000000005aa00000000000000000000000000000000000000000000000000000000000005a100000000000000000000000000000000000000000000000000000000000005ab00000000000000000000000000000000000000000000000000000000000005a200000000000000000000000000000000000000000000000000000000000005ac00000000000000000000000000000000000000000000000000000000000005a300000000000000000000000000000000000000000000000000000000000005ad00000000000000000000000000000000000000000000000000000000000005a400000000000000000000000000000000000000000000000000000000000005ae00000000000000000000000000000000000000000000000000000000000005a500000000000000000000000000000000000000000000000000000000000005af00000000000000000000000000000000000000000000000000000000000005a600000000000000000000000000000000000000000000000000000000000005b000000000000000000000000000000000000000000000000000000000000005a700000000000000000000000000000000000000000000000000000000000005b100000000000000000000000000000000000000000000000000000000000005a800000000000000000000000000000000000000000000000000000000000005b200000000000000000000000000000000000000000000000000000000000005a900000000000000000000000000000000000000000000000000000000000005b300000000000000000000000000000000000000000000000000000000000005aa00000000000000000000000000000000000000000000000000000000000005b400000000000000000000000000000000000000000000000000000000000005ab00000000000000000000000000000000000000000000000000000000000005b500000000000000000000000000000000000000000000000000000000000005ac00000000000000000000000000000000000000000000000000000000000005b600000000000000000000000000000000000000000000000000000000000005ad00000000000000000000000000000000000000000000000000000000000005b700000000000000000000000000000000000000000000000000000000000005ae00000000000000000000000000000000000000000000000000000000000005b800000000000000000000000000000000000000000000000000000000000005af00000000000000000000000000000000000000000000000000000000000005b900000000000000000000000000000000000000000000000000000000000005b000000000000000000000000000000000000000000000000000000000000005ba00000000000000000000000000000000000000000000000000000000000005b100000000000000000000000000000000000000000000000000000000000005bb00000000000000000000000000000000000000000000000000000000000005b200000000000000000000000000000000000000000000000000000000000005bc00000000000000000000000000000000000000000000000000000000000005b300000000000000000000000000000000000000000000000000000000000005bd00000000000000000000000000000000000000000000000000000000000005b400000000000000000000000000000000000000000000000000000000000005be00000000000000000000000000000000000000000000000000000000000005b500000000000000000000000000000000000000000000000000000000000005bf00000000000000000000000000000000000000000000000000000000000005b600000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000005b700000000000000000000000000000000000000000000000000000000000005c100000000000000000000000000000000000000000000000000000000000005b800000000000000000000000000000000000000000000000000000000000005c200000000000000000000000000000000000000000000000000000000000005b900000000000000000000000000000000000000000000000000000000000005c300000000000000000000000000000000000000000000000000000000000005ba00000000000000000000000000000000000000000000000000000000000005c400000000000000000000000000000000000000000000000000000000000005bb00000000000000000000000000000000000000000000000000000000000005c500000000000000000000000000000000000000000000000000000000000005bc00000000000000000000000000000000000000000000000000000000000005c600000000000000000000000000000000000000000000000000000000000005bd00000000000000000000000000000000000000000000000000000000000005c700000000000000000000000000000000000000000000000000000000000005be00000000000000000000000000000000000000000000000000000000000005c80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001c100000000000000000000000000000000000000000000000000000000000001c200000000000000000000000000000000000000000000000000000000000001c300000000000000000000000000000000000000000000000000000000000001c400000000000000000000000000000000000000000000000000000000000001c500000000000000000000000000000000000000000000000000000000000001c600000000000000000000000000000000000000000000000000000000000001c700000000000000000000000000000000000000000000000000000000000001c800000000000000000000000000000000000000000000000000000000000001c900000000000000000000000000000000000000000000000000000000000001ca00000000000000000000000000000000000000000000000000000000000001cb00000000000000000000000000000000000000000000000000000000000001cc00000000000000000000000000000000000000000000000000000000000001cd00000000000000000000000000000000000000000000000000000000000001ce00000000000000000000000000000000000000000000000000000000000001cf00000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001d100000000000000000000000000000000000000000000000000000000000001d200000000000000000000000000000000000000000000000000000000000001d300000000000000000000000000000000000000000000000000000000000001d400000000000000000000000000000000000000000000000000000000000001d500000000000000000000000000000000000000000000000000000000000001d600000000000000000000000000000000000000000000000000000000000001d700000000000000000000000000000000000000000000000000000000000001d800000000000000000000000000000000000000000000000000000000000001d900000000000000000000000000000000000000000000000000000000000001da00000000000000000000000000000000000000000000000000000000000001db00000000000000000000000000000000000000000000000000000000000001dc00000000000000000000000000000000000000000000000000000000000001dd00000000000000000000000000000000000000000000000000000000000001de00000000000000000000000000000000000000000000000000000000000001df00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001e100000000000000000000000000000000000000000000000000000000000001e200000000000000000000000000000000000000000000000000000000000001e300000000000000000000000000000000000000000000000000000000000001e400000000000000000000000000000000000000000000000000000000000001e500000000000000000000000000000000000000000000000000000000000001e600000000000000000000000000000000000000000000000000000000000001e700000000000000000000000000000000000000000000000000000000000001e800000000000000000000000000000000000000000000000000000000000001e900000000000000000000000000000000000000000000000000000000000001ea00000000000000000000000000000000000000000000000000000000000001eb00000000000000000000000000000000000000000000000000000000000001ec00000000000000000000000000000000000000000000000000000000000001ed00000000000000000000000000000000000000000000000000000000000001ee00000000000000000000000000000000000000000000000000000000000001ef00000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000001f100000000000000000000000000000000000000000000000000000000000001f200000000000000000000000000000000000000000000000000000000000001f300000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000001f600000000000000000000000000000000000000000000000000000000000001f700000000000000000000000000000000000000000000000000000000000001f800000000000000000000000000000000000000000000000000000000000001f900000000000000000000000000000000000000000000000000000000000001fa00000000000000000000000000000000000000000000000000000000000001fb00000000000000000000000000000000000000000000000000000000000001fc00000000000000000000000000000000000000000000000000000000000001fd00000000000000000000000000000000000000000000000000000000000001fe00000000000000000000000000000000000000000000000000000000000001ff3f00000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002c100000000000000000000000000000000000000000000000000000000000002c200000000000000000000000000000000000000000000000000000000000002c300000000000000000000000000000000000000000000000000000000000002c400000000000000000000000000000000000000000000000000000000000002c500000000000000000000000000000000000000000000000000000000000002c600000000000000000000000000000000000000000000000000000000000002c700000000000000000000000000000000000000000000000000000000000002c800000000000000000000000000000000000000000000000000000000000002c900000000000000000000000000000000000000000000000000000000000002ca00000000000000000000000000000000000000000000000000000000000002cb00000000000000000000000000000000000000000000000000000000000002cc00000000000000000000000000000000000000000000000000000000000002cd00000000000000000000000000000000000000000000000000000000000002ce00000000000000000000000000000000000000000000000000000000000002cf00000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002d100000000000000000000000000000000000000000000000000000000000002d200000000000000000000000000000000000000000000000000000000000002d300000000000000000000000000000000000000000000000000000000000002d400000000000000000000000000000000000000000000000000000000000002d500000000000000000000000000000000000000000000000000000000000002d600000000000000000000000000000000000000000000000000000000000002d700000000000000000000000000000000000000000000000000000000000002d800000000000000000000000000000000000000000000000000000000000002d900000000000000000000000000000000000000000000000000000000000002da00000000000000000000000000000000000000000000000000000000000002db00000000000000000000000000000000000000000000000000000000000002dc00000000000000000000000000000000000000000000000000000000000002dd00000000000000000000000000000000000000000000000000000000000002de00000000000000000000000000000000000000000000000000000000000002df00000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002e100000000000000000000000000000000000000000000000000000000000002e200000000000000000000000000000000000000000000000000000000000002e300000000000000000000000000000000000000000000000000000000000002e400000000000000000000000000000000000000000000000000000000000002e500000000000000000000000000000000000000000000000000000000000002e600000000000000000000000000000000000000000000000000000000000002e700000000000000000000000000000000000000000000000000000000000002e800000000000000000000000000000000000000000000000000000000000002e900000000000000000000000000000000000000000000000000000000000002ea00000000000000000000000000000000000000000000000000000000000002eb00000000000000000000000000000000000000000000000000000000000002ec00000000000000000000000000000000000000000000000000000000000002ed00000000000000000000000000000000000000000000000000000000000002ee00000000000000000000000000000000000000000000000000000000000002ef00000000000000000000000000000000000000000000000000000000000002f000000000000000000000000000000000000000000000000000000000000002f100000000000000000000000000000000000000000000000000000000000002f200000000000000000000000000000000000000000000000000000000000002f300000000000000000000000000000000000000000000000000000000000002f400000000000000000000000000000000000000000000000000000000000002f500000000000000000000000000000000000000000000000000000000000002f600000000000000000000000000000000000000000000000000000000000002f700000000000000000000000000000000000000000000000000000000000002f800000000000000000000000000000000000000000000000000000000000002f900000000000000000000000000000000000000000000000000000000000002fa00000000000000000000000000000000000000000000000000000000000002fb00000000000000000000000000000000000000000000000000000000000002fc00000000000000000000000000000000000000000000000000000000000002fd00000000000000000000000000000000000000000000000000000000000002fe0800a68d2df6e48c8b31f4c76529e586de2cd9d0f2f2fdfbc4c523db9e3a29e9b80016c236e57bf17afe324437acd1060772e3f31d4f9e734ad758d0627c4ba2a200d659011ddde95e32886bdd8c9464da1ca144ccadd539f0992b4abb491d812a00c8025bb9006a976ebd6ad940155f15f89ca0bb7312b53841fc257e7ed689c8004165f2c46b70fb183468b38f31bba7aa9d22ce8dfb61fe721470729ce1c6b100afeb60dd983514ebbaee141b2196f3eb3c9c299f576a0097872cc85a79a43f005f6bfee53d20a474901493558419dbceb3aca40e5e18915d38031498f4d2bb008791217ee341ec5dc11f7d7a31160fb1b1f2cf767062970e9526e5751956253f00000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000005ca00000000000000000000000000000000000000000000000000000000000005c100000000000000000000000000000000000000000000000000000000000005cb00000000000000000000000000000000000000000000000000000000000005c200000000000000000000000000000000000000000000000000000000000005cc00000000000000000000000000000000000000000000000000000000000005c300000000000000000000000000000000000000000000000000000000000005cd00000000000000000000000000000000000000000000000000000000000005c400000000000000000000000000000000000000000000000000000000000005ce00000000000000000000000000000000000000000000000000000000000005c500000000000000000000000000000000000000000000000000000000000005cf00000000000000000000000000000000000000000000000000000000000005c600000000000000000000000000000000000000000000000000000000000005d000000000000000000000000000000000000000000000000000000000000005c700000000000000000000000000000000000000000000000000000000000005d100000000000000000000000000000000000000000000000000000000000005c800000000000000000000000000000000000000000000000000000000000005d200000000000000000000000000000000000000000000000000000000000005c900000000000000000000000000000000000000000000000000000000000005d300000000000000000000000000000000000000000000000000000000000005ca00000000000000000000000000000000000000000000000000000000000005d400000000000000000000000000000000000000000000000000000000000005cb00000000000000000000000000000000000000000000000000000000000005d500000000000000000000000000000000000000000000000000000000000005cc00000000000000000000000000000000000000000000000000000000000005d600000000000000000000000000000000000000000000000000000000000005cd00000000000000000000000000000000000000000000000000000000000005d700000000000000000000000000000000000000000000000000000000000005ce00000000000000000000000000000000000000000000000000000000000005d800000000000000000000000000000000000000000000000000000000000005cf00000000000000000000000000000000000000000000000000000000000005d900000000000000000000000000000000000000000000000000000000000005d000000000000000000000000000000000000000000000000000000000000005da00000000000000000000000000000000000000000000000000000000000005d100000000000000000000000000000000000000000000000000000000000005db00000000000000000000000000000000000000000000000000000000000005d200000000000000000000000000000000000000000000000000000000000005dc00000000000000000000000000000000000000000000000000000000000005d300000000000000000000000000000000000000000000000000000000000005dd00000000000000000000000000000000000000000000000000000000000005d400000000000000000000000000000000000000000000000000000000000005de00000000000000000000000000000000000000000000000000000000000005d500000000000000000000000000000000000000000000000000000000000005df00000000000000000000000000000000000000000000000000000000000005d600000000000000000000000000000000000000000000000000000000000005e000000000000000000000000000000000000000000000000000000000000005d700000000000000000000000000000000000000000000000000000000000005e100000000000000000000000000000000000000000000000000000000000005d800000000000000000000000000000000000000000000000000000000000005e200000000000000000000000000000000000000000000000000000000000005d900000000000000000000000000000000000000000000000000000000000005e300000000000000000000000000000000000000000000000000000000000005da00000000000000000000000000000000000000000000000000000000000005e400000000000000000000000000000000000000000000000000000000000005db00000000000000000000000000000000000000000000000000000000000005e500000000000000000000000000000000000000000000000000000000000005dc00000000000000000000000000000000000000000000000000000000000005e600000000000000000000000000000000000000000000000000000000000005dd00000000000000000000000000000000000000000000000000000000000005e700000000000000000000000000000000000000000000000000000000000005de00000000000000000000000000000000000000000000000000000000000005e800000000000000000000000000000000000000000000000000000000000005df00000000000000000000000000000000000000000000000000000000000005e900000000000000000000000000000000000000000000000000000000000005e000000000000000000000000000000000000000000000000000000000000005ea00000000000000000000000000000000000000000000000000000000000005e100000000000000000000000000000000000000000000000000000000000005eb00000000000000000000000000000000000000000000000000000000000005e200000000000000000000000000000000000000000000000000000000000005ec00000000000000000000000000000000000000000000000000000000000005e300000000000000000000000000000000000000000000000000000000000005ed00000000000000000000000000000000000000000000000000000000000005e400000000000000000000000000000000000000000000000000000000000005ee00000000000000000000000000000000000000000000000000000000000005e500000000000000000000000000000000000000000000000000000000000005ef00000000000000000000000000000000000000000000000000000000000005e600000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000005e700000000000000000000000000000000000000000000000000000000000005f100000000000000000000000000000000000000000000000000000000000005e800000000000000000000000000000000000000000000000000000000000005f200000000000000000000000000000000000000000000000000000000000005e900000000000000000000000000000000000000000000000000000000000005f300000000000000000000000000000000000000000000000000000000000005ea00000000000000000000000000000000000000000000000000000000000005f400000000000000000000000000000000000000000000000000000000000005eb00000000000000000000000000000000000000000000000000000000000005f500000000000000000000000000000000000000000000000000000000000005ec00000000000000000000000000000000000000000000000000000000000005f600000000000000000000000000000000000000000000000000000000000005ed00000000000000000000000000000000000000000000000000000000000005f700000000000000000000000000000000000000000000000000000000000005ee00000000000000000000000000000000000000000000000000000000000005f800000000000000000000000000000000000000000000000000000000000005ef00000000000000000000000000000000000000000000000000000000000005f900000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000005fa00000000000000000000000000000000000000000000000000000000000005f100000000000000000000000000000000000000000000000000000000000005fb00000000000000000000000000000000000000000000000000000000000005f200000000000000000000000000000000000000000000000000000000000005fc00000000000000000000000000000000000000000000000000000000000005f300000000000000000000000000000000000000000000000000000000000005fd00000000000000000000000000000000000000000000000000000000000005f400000000000000000000000000000000000000000000000000000000000005fe00000000000000000000000000000000000000000000000000000000000005f500000000000000000000000000000000000000000000000000000000000005ff00000000000000000000000000000000000000000000000000000000000005f6000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000005f7000000000000000000000000000000000000000000000000000000000000060100000000000000000000000000000000000000000000000000000000000005f8000000000000000000000000000000000000000000000000000000000000060200000000000000000000000000000000000000000000000000000000000005f9000000000000000000000000000000000000000000000000000000000000060300000000000000000000000000000000000000000000000000000000000005fa000000000000000000000000000000000000000000000000000000000000060400000000000000000000000000000000000000000000000000000000000005fb000000000000000000000000000000000000000000000000000000000000060500000000000000000000000000000000000000000000000000000000000005fc000000000000000000000000000000000000000000000000000000000000060600000000000000000000000000000000000000000000000000000000000005fd000000000000000000000000000000000000000000000000000000000000060700000000000000000000000000000000000000000000000000000000000005fe0000000000000000000000000000000000000000000000000000000000000608000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020100000000000000000000000000000000000000000000000000000000000002020000000000000000000000000000000000000000000000000000000000000203000000000000000000000000000000000000000000000000000000000000020400000000000000000000000000000000000000000000000000000000000002050000000000000000000000000000000000000000000000000000000000000206000000000000000000000000000000000000000000000000000000000000020700000000000000000000000000000000000000000000000000000000000002080000000000000000000000000000000000000000000000000000000000000209000000000000000000000000000000000000000000000000000000000000020a000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000020c000000000000000000000000000000000000000000000000000000000000020d000000000000000000000000000000000000000000000000000000000000020e000000000000000000000000000000000000000000000000000000000000020f0000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000021100000000000000000000000000000000000000000000000000000000000002120000000000000000000000000000000000000000000000000000000000000213000000000000000000000000000000000000000000000000000000000000021400000000000000000000000000000000000000000000000000000000000002150000000000000000000000000000000000000000000000000000000000000216000000000000000000000000000000000000000000000000000000000000021700000000000000000000000000000000000000000000000000000000000002180000000000000000000000000000000000000000000000000000000000000219000000000000000000000000000000000000000000000000000000000000021a000000000000000000000000000000000000000000000000000000000000021b000000000000000000000000000000000000000000000000000000000000021c000000000000000000000000000000000000000000000000000000000000021d000000000000000000000000000000000000000000000000000000000000021e000000000000000000000000000000000000000000000000000000000000021f0000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000022100000000000000000000000000000000000000000000000000000000000002220000000000000000000000000000000000000000000000000000000000000223000000000000000000000000000000000000000000000000000000000000022400000000000000000000000000000000000000000000000000000000000002250000000000000000000000000000000000000000000000000000000000000226000000000000000000000000000000000000000000000000000000000000022700000000000000000000000000000000000000000000000000000000000002280000000000000000000000000000000000000000000000000000000000000229000000000000000000000000000000000000000000000000000000000000022a000000000000000000000000000000000000000000000000000000000000022b000000000000000000000000000000000000000000000000000000000000022c000000000000000000000000000000000000000000000000000000000000022d000000000000000000000000000000000000000000000000000000000000022e000000000000000000000000000000000000000000000000000000000000022f0000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000023100000000000000000000000000000000000000000000000000000000000002320000000000000000000000000000000000000000000000000000000000000233000000000000000000000000000000000000000000000000000000000000023400000000000000000000000000000000000000000000000000000000000002350000000000000000000000000000000000000000000000000000000000000236000000000000000000000000000000000000000000000000000000000000023700000000000000000000000000000000000000000000000000000000000002380000000000000000000000000000000000000000000000000000000000000239000000000000000000000000000000000000000000000000000000000000023a000000000000000000000000000000000000000000000000000000000000023b000000000000000000000000000000000000000000000000000000000000023c000000000000000000000000000000000000000000000000000000000000023d000000000000000000000000000000000000000000000000000000000000023e000000000000000000000000000000000000000000000000000000000000023f3f0000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000030100000000000000000000000000000000000000000000000000000000000003020000000000000000000000000000000000000000000000000000000000000303000000000000000000000000000000000000000000000000000000000000030400000000000000000000000000000000000000000000000000000000000003050000000000000000000000000000000000000000000000000000000000000306000000000000000000000000000000000000000000000000000000000000030700000000000000000000000000000000000000000000000000000000000003080000000000000000000000000000000000000000000000000000000000000309000000000000000000000000000000000000000000000000000000000000030a000000000000000000000000000000000000000000000000000000000000030b000000000000000000000000000000000000000000000000000000000000030c000000000000000000000000000000000000000000000000000000000000030d000000000000000000000000000000000000000000000000000000000000030e000000000000000000000000000000000000000000000000000000000000030f0000000000000000000000000000000000000000000000000000000000000310000000000000000000000000000000000000000000000000000000000000031100000000000000000000000000000000000000000000000000000000000003120000000000000000000000000000000000000000000000000000000000000313000000000000000000000000000000000000000000000000000000000000031400000000000000000000000000000000000000000000000000000000000003150000000000000000000000000000000000000000000000000000000000000316000000000000000000000000000000000000000000000000000000000000031700000000000000000000000000000000000000000000000000000000000003180000000000000000000000000000000000000000000000000000000000000319000000000000000000000000000000000000000000000000000000000000031a000000000000000000000000000000000000000000000000000000000000031b000000000000000000000000000000000000000000000000000000000000031c000000000000000000000000000000000000000000000000000000000000031d000000000000000000000000000000000000000000000000000000000000031e000000000000000000000000000000000000000000000000000000000000031f0000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032100000000000000000000000000000000000000000000000000000000000003220000000000000000000000000000000000000000000000000000000000000323000000000000000000000000000000000000000000000000000000000000032400000000000000000000000000000000000000000000000000000000000003250000000000000000000000000000000000000000000000000000000000000326000000000000000000000000000000000000000000000000000000000000032700000000000000000000000000000000000000000000000000000000000003280000000000000000000000000000000000000000000000000000000000000329000000000000000000000000000000000000000000000000000000000000032a000000000000000000000000000000000000000000000000000000000000032b000000000000000000000000000000000000000000000000000000000000032c000000000000000000000000000000000000000000000000000000000000032d000000000000000000000000000000000000000000000000000000000000032e000000000000000000000000000000000000000000000000000000000000032f0000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000033100000000000000000000000000000000000000000000000000000000000003320000000000000000000000000000000000000000000000000000000000000333000000000000000000000000000000000000000000000000000000000000033400000000000000000000000000000000000000000000000000000000000003350000000000000000000000000000000000000000000000000000000000000336000000000000000000000000000000000000000000000000000000000000033700000000000000000000000000000000000000000000000000000000000003380000000000000000000000000000000000000000000000000000000000000339000000000000000000000000000000000000000000000000000000000000033a000000000000000000000000000000000000000000000000000000000000033b000000000000000000000000000000000000000000000000000000000000033c000000000000000000000000000000000000000000000000000000000000033d000000000000000000000000000000000000000000000000000000000000033e0800c064a9343bfaf1345a5ad188e96aa4c5bad0b4a5590da51a9ff8f3c98ade8d008803d57dd0923c95a01b2bfbee4df6d66dc7baee1d2cd2770575feb86d040200ea64eed9489feb7bdf29bf817a6e8772e549da7b291028852d0dd3810cee9500947e8f904d41be8a4e08b146b4e1f0cd88f55722ef987d1d485c4196ab9f71002e5d9ed5d71bc3bea6edf988c4b9ba7fceb0815180d893852ed343c64ab55c0040f7f519ec89d360d83e242b6c0ce049d2b3356b8cfbf1ff3b733ef0f8a089006f5cfe5fc4a7b87c634f9e253a7cea2052229250d0c0e913eb50b5ef3612de00b759fce0eed36bb653b67255cce111b3529c383bd7d2b758f8cb53a4451f273f0000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060a0000000000000000000000000000000000000000000000000000000000000601000000000000000000000000000000000000000000000000000000000000060b0000000000000000000000000000000000000000000000000000000000000602000000000000000000000000000000000000000000000000000000000000060c0000000000000000000000000000000000000000000000000000000000000603000000000000000000000000000000000000000000000000000000000000060d0000000000000000000000000000000000000000000000000000000000000604000000000000000000000000000000000000000000000000000000000000060e0000000000000000000000000000000000000000000000000000000000000605000000000000000000000000000000000000000000000000000000000000060f00000000000000000000000000000000000000000000000000000000000006060000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000060700000000000000000000000000000000000000000000000000000000000006110000000000000000000000000000000000000000000000000000000000000608000000000000000000000000000000000000000000000000000000000000061200000000000000000000000000000000000000000000000000000000000006090000000000000000000000000000000000000000000000000000000000000613000000000000000000000000000000000000000000000000000000000000060a0000000000000000000000000000000000000000000000000000000000000614000000000000000000000000000000000000000000000000000000000000060b0000000000000000000000000000000000000000000000000000000000000615000000000000000000000000000000000000000000000000000000000000060c0000000000000000000000000000000000000000000000000000000000000616000000000000000000000000000000000000000000000000000000000000060d0000000000000000000000000000000000000000000000000000000000000617000000000000000000000000000000000000000000000000000000000000060e0000000000000000000000000000000000000000000000000000000000000618000000000000000000000000000000000000000000000000000000000000060f00000000000000000000000000000000000000000000000000000000000006190000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000061a0000000000000000000000000000000000000000000000000000000000000611000000000000000000000000000000000000000000000000000000000000061b0000000000000000000000000000000000000000000000000000000000000612000000000000000000000000000000000000000000000000000000000000061c0000000000000000000000000000000000000000000000000000000000000613000000000000000000000000000000000000000000000000000000000000061d0000000000000000000000000000000000000000000000000000000000000614000000000000000000000000000000000000000000000000000000000000061e0000000000000000000000000000000000000000000000000000000000000615000000000000000000000000000000000000000000000000000000000000061f00000000000000000000000000000000000000000000000000000000000006160000000000000000000000000000000000000000000000000000000000000620000000000000000000000000000000000000000000000000000000000000061700000000000000000000000000000000000000000000000000000000000006210000000000000000000000000000000000000000000000000000000000000618000000000000000000000000000000000000000000000000000000000000062200000000000000000000000000000000000000000000000000000000000006190000000000000000000000000000000000000000000000000000000000000623000000000000000000000000000000000000000000000000000000000000061a0000000000000000000000000000000000000000000000000000000000000624000000000000000000000000000000000000000000000000000000000000061b0000000000000000000000000000000000000000000000000000000000000625000000000000000000000000000000000000000000000000000000000000061c0000000000000000000000000000000000000000000000000000000000000626000000000000000000000000000000000000000000000000000000000000061d0000000000000000000000000000000000000000000000000000000000000627000000000000000000000000000000000000000000000000000000000000061e0000000000000000000000000000000000000000000000000000000000000628000000000000000000000000000000000000000000000000000000000000061f00000000000000000000000000000000000000000000000000000000000006290000000000000000000000000000000000000000000000000000000000000620000000000000000000000000000000000000000000000000000000000000062a0000000000000000000000000000000000000000000000000000000000000621000000000000000000000000000000000000000000000000000000000000062b0000000000000000000000000000000000000000000000000000000000000622000000000000000000000000000000000000000000000000000000000000062c0000000000000000000000000000000000000000000000000000000000000623000000000000000000000000000000000000000000000000000000000000062d0000000000000000000000000000000000000000000000000000000000000624000000000000000000000000000000000000000000000000000000000000062e0000000000000000000000000000000000000000000000000000000000000625000000000000000000000000000000000000000000000000000000000000062f00000000000000000000000000000000000000000000000000000000000006260000000000000000000000000000000000000000000000000000000000000630000000000000000000000000000000000000000000000000000000000000062700000000000000000000000000000000000000000000000000000000000006310000000000000000000000000000000000000000000000000000000000000628000000000000000000000000000000000000000000000000000000000000063200000000000000000000000000000000000000000000000000000000000006290000000000000000000000000000000000000000000000000000000000000633000000000000000000000000000000000000000000000000000000000000062a0000000000000000000000000000000000000000000000000000000000000634000000000000000000000000000000000000000000000000000000000000062b0000000000000000000000000000000000000000000000000000000000000635000000000000000000000000000000000000000000000000000000000000062c0000000000000000000000000000000000000000000000000000000000000636000000000000000000000000000000000000000000000000000000000000062d0000000000000000000000000000000000000000000000000000000000000637000000000000000000000000000000000000000000000000000000000000062e0000000000000000000000000000000000000000000000000000000000000638000000000000000000000000000000000000000000000000000000000000062f00000000000000000000000000000000000000000000000000000000000006390000000000000000000000000000000000000000000000000000000000000630000000000000000000000000000000000000000000000000000000000000063a0000000000000000000000000000000000000000000000000000000000000631000000000000000000000000000000000000000000000000000000000000063b0000000000000000000000000000000000000000000000000000000000000632000000000000000000000000000000000000000000000000000000000000063c0000000000000000000000000000000000000000000000000000000000000633000000000000000000000000000000000000000000000000000000000000063d0000000000000000000000000000000000000000000000000000000000000634000000000000000000000000000000000000000000000000000000000000063e0000000000000000000000000000000000000000000000000000000000000635000000000000000000000000000000000000000000000000000000000000063f00000000000000000000000000000000000000000000000000000000000006360000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000063700000000000000000000000000000000000000000000000000000000000006410000000000000000000000000000000000000000000000000000000000000638000000000000000000000000000000000000000000000000000000000000064200000000000000000000000000000000000000000000000000000000000006390000000000000000000000000000000000000000000000000000000000000643000000000000000000000000000000000000000000000000000000000000063a0000000000000000000000000000000000000000000000000000000000000644000000000000000000000000000000000000000000000000000000000000063b0000000000000000000000000000000000000000000000000000000000000645000000000000000000000000000000000000000000000000000000000000063c0000000000000000000000000000000000000000000000000000000000000646000000000000000000000000000000000000000000000000000000000000063d0000000000000000000000000000000000000000000000000000000000000647000000000000000000000000000000000000000000000000000000000000063e0000000000000000000000000000000000000000000000000000000000000648000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "txsEffectsHash": "0x00e7daa0660d17d3ae04747bd24c7238da34e77cb04b0b9dd2843dd08f0fd87b", "decodedHeader": { @@ -71,12 +71,12 @@ }, "globalVariables": { "blockNumber": 1, - "slotNumber": "0x0000000000000000000000000000000000000000000000000000000000000019", + "slotNumber": "0x000000000000000000000000000000000000000000000000000000000000001a", "chainId": 31337, - "timestamp": 1728562434, + "timestamp": 1730233800, "version": 1, - "coinbase": "0xb1e072764484ad800bc7d95ebdb84ea57ec1d5d3", - "feeRecipient": "0x27f79e3e1124a5869838e688cdf225f421845f3b734541c251652fdeb0f74917", + "coinbase": "0x4b727c2ee4030668748766f0695aebffca7eed1a", + "feeRecipient": "0x17e8c896e28967f5b43e53f229edf504b61881c02885750af6b75a3b5cc9d87c", "gasFees": { "feePerDaGas": 0, "feePerL2Gas": 0 @@ -107,8 +107,8 @@ } } }, - "header": "0x1200a06aae1368abe36530b585bd7a4d2ba4de5037b82076412691a187d7621e00000001000000000000000000000000000000000000000000000000000000000000000400e7daa0660d17d3ae04747bd24c7238da34e77cb04b0b9dd2843dd08f0fd87b00089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00d0169cc64b8f1bd695ec8611a5602da48854dc4cc04989c4b63288b339cb1814f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb3000000101a995cda6f326074cf650c6644269e29dbd0532e6a832238345b53ee70c878af000001000deac8396e31bc1196b442ad724bf8f751a245e518147d738cc84b9e1a56b4420000018023866f4c16f3ea1f37dd2ca42d1a635ea909b6c016e45e8434780d3741eb7dbb000001800000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000006707c502b1e072764484ad800bc7d95ebdb84ea57ec1d5d327f79e3e1124a5869838e688cdf225f421845f3b734541c251652fdeb0f74917000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "publicInputsHash": "0x003a792a91c9ec5d6a06b895be65303a3e333e173a643136512d81f4f63e0ceb", + "header": "0x1200a06aae1368abe36530b585bd7a4d2ba4de5037b82076412691a187d7621e00000001000000000000000000000000000000000000000000000000000000000000000400e7daa0660d17d3ae04747bd24c7238da34e77cb04b0b9dd2843dd08f0fd87b00089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00d0169cc64b8f1bd695ec8611a5602da48854dc4cc04989c4b63288b339cb1814f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb3000000101a995cda6f326074cf650c6644269e29dbd0532e6a832238345b53ee70c878af000001000deac8396e31bc1196b442ad724bf8f751a245e518147d738cc84b9e1a56b4420000018023866f4c16f3ea1f37dd2ca42d1a635ea909b6c016e45e8434780d3741eb7dbb000001800000000000000000000000000000000000000000000000000000000000007a6900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000672145c84b727c2ee4030668748766f0695aebffca7eed1a17e8c896e28967f5b43e53f229edf504b61881c02885750af6b75a3b5cc9d87c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "publicInputsHash": "0x0023cd404ff57c5e39baf1f923c48af8edbd256eb8e183515b60dad7e835189b", "numTxs": 4 } } \ No newline at end of file diff --git a/l1-contracts/test/fixtures/mixed_block_2.json b/l1-contracts/test/fixtures/mixed_block_2.json index a58dd39791b..3f02a33aa7f 100644 --- a/l1-contracts/test/fixtures/mixed_block_2.json +++ b/l1-contracts/test/fixtures/mixed_block_2.json @@ -90,25 +90,25 @@ ] }, "block": { - "archive": "0x217afc0a3ea8a6d6e2297e86269a799cc51d995505d6314e57a4a0f014220c5e", - "blockHash": "0x15a14ac9f21ce7db202b0a41c2fabf60757b961b8b26d91307d6ff3e52cbd356", + "archive": "0x207d7efc92c74a8d0896583fe33ce66b214e2a4d3d48530f2bfa828ec7dca041", + "blockHash": "0x176003b50b6f8005c4cc6c3793ba8a7510283c6e093a15bbaf5fbc7a2c80223c", "body": "0x00000008000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014100000000000000000000000000000000000000000000000000000000000001420000000000000000000000000000000000000000000000000000000000000143000000000000000000000000000000000000000000000000000000000000014400000000000000000000000000000000000000000000000000000000000001450000000000000000000000000000000000000000000000000000000000000146000000000000000000000000000000000000000000000000000000000000014700000000000000000000000000000000000000000000000000000000000001480000000000000000000000000000000000000000000000000000000000000149000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014b000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000014d000000000000000000000000000000000000000000000000000000000000014e000000000000000000000000000000000000000000000000000000000000014f0000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000015100000000000000000000000000000000000000000000000000000000000001520000000000000000000000000000000000000000000000000000000000000153000000000000000000000000000000000000000000000000000000000000015400000000000000000000000000000000000000000000000000000000000001550000000000000000000000000000000000000000000000000000000000000156000000000000000000000000000000000000000000000000000000000000015700000000000000000000000000000000000000000000000000000000000001580000000000000000000000000000000000000000000000000000000000000159000000000000000000000000000000000000000000000000000000000000015a000000000000000000000000000000000000000000000000000000000000015b000000000000000000000000000000000000000000000000000000000000015c000000000000000000000000000000000000000000000000000000000000015d000000000000000000000000000000000000000000000000000000000000015e000000000000000000000000000000000000000000000000000000000000015f0000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000001620000000000000000000000000000000000000000000000000000000000000163000000000000000000000000000000000000000000000000000000000000016400000000000000000000000000000000000000000000000000000000000001650000000000000000000000000000000000000000000000000000000000000166000000000000000000000000000000000000000000000000000000000000016700000000000000000000000000000000000000000000000000000000000001680000000000000000000000000000000000000000000000000000000000000169000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016b000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000000016d000000000000000000000000000000000000000000000000000000000000016e000000000000000000000000000000000000000000000000000000000000016f0000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000017100000000000000000000000000000000000000000000000000000000000001720000000000000000000000000000000000000000000000000000000000000173000000000000000000000000000000000000000000000000000000000000017400000000000000000000000000000000000000000000000000000000000001750000000000000000000000000000000000000000000000000000000000000176000000000000000000000000000000000000000000000000000000000000017700000000000000000000000000000000000000000000000000000000000001780000000000000000000000000000000000000000000000000000000000000179000000000000000000000000000000000000000000000000000000000000017a000000000000000000000000000000000000000000000000000000000000017b000000000000000000000000000000000000000000000000000000000000017c000000000000000000000000000000000000000000000000000000000000017d000000000000000000000000000000000000000000000000000000000000017e000000000000000000000000000000000000000000000000000000000000017f3f0000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000024100000000000000000000000000000000000000000000000000000000000002420000000000000000000000000000000000000000000000000000000000000243000000000000000000000000000000000000000000000000000000000000024400000000000000000000000000000000000000000000000000000000000002450000000000000000000000000000000000000000000000000000000000000246000000000000000000000000000000000000000000000000000000000000024700000000000000000000000000000000000000000000000000000000000002480000000000000000000000000000000000000000000000000000000000000249000000000000000000000000000000000000000000000000000000000000024a000000000000000000000000000000000000000000000000000000000000024b000000000000000000000000000000000000000000000000000000000000024c000000000000000000000000000000000000000000000000000000000000024d000000000000000000000000000000000000000000000000000000000000024e000000000000000000000000000000000000000000000000000000000000024f0000000000000000000000000000000000000000000000000000000000000250000000000000000000000000000000000000000000000000000000000000025100000000000000000000000000000000000000000000000000000000000002520000000000000000000000000000000000000000000000000000000000000253000000000000000000000000000000000000000000000000000000000000025400000000000000000000000000000000000000000000000000000000000002550000000000000000000000000000000000000000000000000000000000000256000000000000000000000000000000000000000000000000000000000000025700000000000000000000000000000000000000000000000000000000000002580000000000000000000000000000000000000000000000000000000000000259000000000000000000000000000000000000000000000000000000000000025a000000000000000000000000000000000000000000000000000000000000025b000000000000000000000000000000000000000000000000000000000000025c000000000000000000000000000000000000000000000000000000000000025d000000000000000000000000000000000000000000000000000000000000025e000000000000000000000000000000000000000000000000000000000000025f0000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000026100000000000000000000000000000000000000000000000000000000000002620000000000000000000000000000000000000000000000000000000000000263000000000000000000000000000000000000000000000000000000000000026400000000000000000000000000000000000000000000000000000000000002650000000000000000000000000000000000000000000000000000000000000266000000000000000000000000000000000000000000000000000000000000026700000000000000000000000000000000000000000000000000000000000002680000000000000000000000000000000000000000000000000000000000000269000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000026b000000000000000000000000000000000000000000000000000000000000026c000000000000000000000000000000000000000000000000000000000000026d000000000000000000000000000000000000000000000000000000000000026e000000000000000000000000000000000000000000000000000000000000026f0000000000000000000000000000000000000000000000000000000000000270000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000002720000000000000000000000000000000000000000000000000000000000000273000000000000000000000000000000000000000000000000000000000000027400000000000000000000000000000000000000000000000000000000000002750000000000000000000000000000000000000000000000000000000000000276000000000000000000000000000000000000000000000000000000000000027700000000000000000000000000000000000000000000000000000000000002780000000000000000000000000000000000000000000000000000000000000279000000000000000000000000000000000000000000000000000000000000027a000000000000000000000000000000000000000000000000000000000000027b000000000000000000000000000000000000000000000000000000000000027c000000000000000000000000000000000000000000000000000000000000027d000000000000000000000000000000000000000000000000000000000000027e0800c47667396742a5474f325e2567bff3bb99b7f0bfd2b1a689b635d8b8726cce00284120278895e8d47084ae759f390e9634881e41369a257a36fe99a2369dc800a328b4a4a6ed156325253b4ab2af7ca00e21caf7963f0fba8a88ccdc3512c300705c99df420bface231f6855799db1d0ed7d39419abc47aa8c6efe2ae7aae2009299cc308de6d23788384411e024791a5b2448e455fbdd1d1f28f3ff76631f002d6c03d81ad764de51b0f34584645191cdc2aaae2ca08fb838d142b95d62f5003032f3618b2df0fa335d5fd548d6d85e42b4e7eb5fff9eb687facbbdecb8a60016cab7ddf4d1b440d53d10284c5c82a78b2d4e27dcdb44ef434ef4c6bad6783f0000000000000000000000000000000000000000000000000000000000000540000000000000000000000000000000000000000000000000000000000000054a0000000000000000000000000000000000000000000000000000000000000541000000000000000000000000000000000000000000000000000000000000054b0000000000000000000000000000000000000000000000000000000000000542000000000000000000000000000000000000000000000000000000000000054c0000000000000000000000000000000000000000000000000000000000000543000000000000000000000000000000000000000000000000000000000000054d0000000000000000000000000000000000000000000000000000000000000544000000000000000000000000000000000000000000000000000000000000054e0000000000000000000000000000000000000000000000000000000000000545000000000000000000000000000000000000000000000000000000000000054f00000000000000000000000000000000000000000000000000000000000005460000000000000000000000000000000000000000000000000000000000000550000000000000000000000000000000000000000000000000000000000000054700000000000000000000000000000000000000000000000000000000000005510000000000000000000000000000000000000000000000000000000000000548000000000000000000000000000000000000000000000000000000000000055200000000000000000000000000000000000000000000000000000000000005490000000000000000000000000000000000000000000000000000000000000553000000000000000000000000000000000000000000000000000000000000054a0000000000000000000000000000000000000000000000000000000000000554000000000000000000000000000000000000000000000000000000000000054b0000000000000000000000000000000000000000000000000000000000000555000000000000000000000000000000000000000000000000000000000000054c0000000000000000000000000000000000000000000000000000000000000556000000000000000000000000000000000000000000000000000000000000054d0000000000000000000000000000000000000000000000000000000000000557000000000000000000000000000000000000000000000000000000000000054e0000000000000000000000000000000000000000000000000000000000000558000000000000000000000000000000000000000000000000000000000000054f00000000000000000000000000000000000000000000000000000000000005590000000000000000000000000000000000000000000000000000000000000550000000000000000000000000000000000000000000000000000000000000055a0000000000000000000000000000000000000000000000000000000000000551000000000000000000000000000000000000000000000000000000000000055b0000000000000000000000000000000000000000000000000000000000000552000000000000000000000000000000000000000000000000000000000000055c0000000000000000000000000000000000000000000000000000000000000553000000000000000000000000000000000000000000000000000000000000055d0000000000000000000000000000000000000000000000000000000000000554000000000000000000000000000000000000000000000000000000000000055e0000000000000000000000000000000000000000000000000000000000000555000000000000000000000000000000000000000000000000000000000000055f00000000000000000000000000000000000000000000000000000000000005560000000000000000000000000000000000000000000000000000000000000560000000000000000000000000000000000000000000000000000000000000055700000000000000000000000000000000000000000000000000000000000005610000000000000000000000000000000000000000000000000000000000000558000000000000000000000000000000000000000000000000000000000000056200000000000000000000000000000000000000000000000000000000000005590000000000000000000000000000000000000000000000000000000000000563000000000000000000000000000000000000000000000000000000000000055a0000000000000000000000000000000000000000000000000000000000000564000000000000000000000000000000000000000000000000000000000000055b0000000000000000000000000000000000000000000000000000000000000565000000000000000000000000000000000000000000000000000000000000055c0000000000000000000000000000000000000000000000000000000000000566000000000000000000000000000000000000000000000000000000000000055d0000000000000000000000000000000000000000000000000000000000000567000000000000000000000000000000000000000000000000000000000000055e0000000000000000000000000000000000000000000000000000000000000568000000000000000000000000000000000000000000000000000000000000055f00000000000000000000000000000000000000000000000000000000000005690000000000000000000000000000000000000000000000000000000000000560000000000000000000000000000000000000000000000000000000000000056a0000000000000000000000000000000000000000000000000000000000000561000000000000000000000000000000000000000000000000000000000000056b0000000000000000000000000000000000000000000000000000000000000562000000000000000000000000000000000000000000000000000000000000056c0000000000000000000000000000000000000000000000000000000000000563000000000000000000000000000000000000000000000000000000000000056d0000000000000000000000000000000000000000000000000000000000000564000000000000000000000000000000000000000000000000000000000000056e0000000000000000000000000000000000000000000000000000000000000565000000000000000000000000000000000000000000000000000000000000056f00000000000000000000000000000000000000000000000000000000000005660000000000000000000000000000000000000000000000000000000000000570000000000000000000000000000000000000000000000000000000000000056700000000000000000000000000000000000000000000000000000000000005710000000000000000000000000000000000000000000000000000000000000568000000000000000000000000000000000000000000000000000000000000057200000000000000000000000000000000000000000000000000000000000005690000000000000000000000000000000000000000000000000000000000000573000000000000000000000000000000000000000000000000000000000000056a0000000000000000000000000000000000000000000000000000000000000574000000000000000000000000000000000000000000000000000000000000056b0000000000000000000000000000000000000000000000000000000000000575000000000000000000000000000000000000000000000000000000000000056c0000000000000000000000000000000000000000000000000000000000000576000000000000000000000000000000000000000000000000000000000000056d0000000000000000000000000000000000000000000000000000000000000577000000000000000000000000000000000000000000000000000000000000056e0000000000000000000000000000000000000000000000000000000000000578000000000000000000000000000000000000000000000000000000000000056f00000000000000000000000000000000000000000000000000000000000005790000000000000000000000000000000000000000000000000000000000000570000000000000000000000000000000000000000000000000000000000000057a0000000000000000000000000000000000000000000000000000000000000571000000000000000000000000000000000000000000000000000000000000057b0000000000000000000000000000000000000000000000000000000000000572000000000000000000000000000000000000000000000000000000000000057c0000000000000000000000000000000000000000000000000000000000000573000000000000000000000000000000000000000000000000000000000000057d0000000000000000000000000000000000000000000000000000000000000574000000000000000000000000000000000000000000000000000000000000057e0000000000000000000000000000000000000000000000000000000000000575000000000000000000000000000000000000000000000000000000000000057f00000000000000000000000000000000000000000000000000000000000005760000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000057700000000000000000000000000000000000000000000000000000000000005810000000000000000000000000000000000000000000000000000000000000578000000000000000000000000000000000000000000000000000000000000058200000000000000000000000000000000000000000000000000000000000005790000000000000000000000000000000000000000000000000000000000000583000000000000000000000000000000000000000000000000000000000000057a0000000000000000000000000000000000000000000000000000000000000584000000000000000000000000000000000000000000000000000000000000057b0000000000000000000000000000000000000000000000000000000000000585000000000000000000000000000000000000000000000000000000000000057c0000000000000000000000000000000000000000000000000000000000000586000000000000000000000000000000000000000000000000000000000000057d0000000000000000000000000000000000000000000000000000000000000587000000000000000000000000000000000000000000000000000000000000057e0000000000000000000000000000000000000000000000000000000000000588000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000018100000000000000000000000000000000000000000000000000000000000001820000000000000000000000000000000000000000000000000000000000000183000000000000000000000000000000000000000000000000000000000000018400000000000000000000000000000000000000000000000000000000000001850000000000000000000000000000000000000000000000000000000000000186000000000000000000000000000000000000000000000000000000000000018700000000000000000000000000000000000000000000000000000000000001880000000000000000000000000000000000000000000000000000000000000189000000000000000000000000000000000000000000000000000000000000018a000000000000000000000000000000000000000000000000000000000000018b000000000000000000000000000000000000000000000000000000000000018c000000000000000000000000000000000000000000000000000000000000018d000000000000000000000000000000000000000000000000000000000000018e000000000000000000000000000000000000000000000000000000000000018f0000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000000019100000000000000000000000000000000000000000000000000000000000001920000000000000000000000000000000000000000000000000000000000000193000000000000000000000000000000000000000000000000000000000000019400000000000000000000000000000000000000000000000000000000000001950000000000000000000000000000000000000000000000000000000000000196000000000000000000000000000000000000000000000000000000000000019700000000000000000000000000000000000000000000000000000000000001980000000000000000000000000000000000000000000000000000000000000199000000000000000000000000000000000000000000000000000000000000019a000000000000000000000000000000000000000000000000000000000000019b000000000000000000000000000000000000000000000000000000000000019c000000000000000000000000000000000000000000000000000000000000019d000000000000000000000000000000000000000000000000000000000000019e000000000000000000000000000000000000000000000000000000000000019f00000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001a100000000000000000000000000000000000000000000000000000000000001a200000000000000000000000000000000000000000000000000000000000001a300000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000000001a500000000000000000000000000000000000000000000000000000000000001a600000000000000000000000000000000000000000000000000000000000001a700000000000000000000000000000000000000000000000000000000000001a800000000000000000000000000000000000000000000000000000000000001a900000000000000000000000000000000000000000000000000000000000001aa00000000000000000000000000000000000000000000000000000000000001ab00000000000000000000000000000000000000000000000000000000000001ac00000000000000000000000000000000000000000000000000000000000001ad00000000000000000000000000000000000000000000000000000000000001ae00000000000000000000000000000000000000000000000000000000000001af00000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b100000000000000000000000000000000000000000000000000000000000001b200000000000000000000000000000000000000000000000000000000000001b300000000000000000000000000000000000000000000000000000000000001b400000000000000000000000000000000000000000000000000000000000001b500000000000000000000000000000000000000000000000000000000000001b600000000000000000000000000000000000000000000000000000000000001b700000000000000000000000000000000000000000000000000000000000001b800000000000000000000000000000000000000000000000000000000000001b900000000000000000000000000000000000000000000000000000000000001ba00000000000000000000000000000000000000000000000000000000000001bb00000000000000000000000000000000000000000000000000000000000001bc00000000000000000000000000000000000000000000000000000000000001bd00000000000000000000000000000000000000000000000000000000000001be00000000000000000000000000000000000000000000000000000000000001bf3f0000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000028100000000000000000000000000000000000000000000000000000000000002820000000000000000000000000000000000000000000000000000000000000283000000000000000000000000000000000000000000000000000000000000028400000000000000000000000000000000000000000000000000000000000002850000000000000000000000000000000000000000000000000000000000000286000000000000000000000000000000000000000000000000000000000000028700000000000000000000000000000000000000000000000000000000000002880000000000000000000000000000000000000000000000000000000000000289000000000000000000000000000000000000000000000000000000000000028a000000000000000000000000000000000000000000000000000000000000028b000000000000000000000000000000000000000000000000000000000000028c000000000000000000000000000000000000000000000000000000000000028d000000000000000000000000000000000000000000000000000000000000028e000000000000000000000000000000000000000000000000000000000000028f0000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000000000000000000000000000000029100000000000000000000000000000000000000000000000000000000000002920000000000000000000000000000000000000000000000000000000000000293000000000000000000000000000000000000000000000000000000000000029400000000000000000000000000000000000000000000000000000000000002950000000000000000000000000000000000000000000000000000000000000296000000000000000000000000000000000000000000000000000000000000029700000000000000000000000000000000000000000000000000000000000002980000000000000000000000000000000000000000000000000000000000000299000000000000000000000000000000000000000000000000000000000000029a000000000000000000000000000000000000000000000000000000000000029b000000000000000000000000000000000000000000000000000000000000029c000000000000000000000000000000000000000000000000000000000000029d000000000000000000000000000000000000000000000000000000000000029e000000000000000000000000000000000000000000000000000000000000029f00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002a100000000000000000000000000000000000000000000000000000000000002a200000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000002a400000000000000000000000000000000000000000000000000000000000002a500000000000000000000000000000000000000000000000000000000000002a600000000000000000000000000000000000000000000000000000000000002a700000000000000000000000000000000000000000000000000000000000002a800000000000000000000000000000000000000000000000000000000000002a900000000000000000000000000000000000000000000000000000000000002aa00000000000000000000000000000000000000000000000000000000000002ab00000000000000000000000000000000000000000000000000000000000002ac00000000000000000000000000000000000000000000000000000000000002ad00000000000000000000000000000000000000000000000000000000000002ae00000000000000000000000000000000000000000000000000000000000002af00000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002b100000000000000000000000000000000000000000000000000000000000002b200000000000000000000000000000000000000000000000000000000000002b300000000000000000000000000000000000000000000000000000000000002b400000000000000000000000000000000000000000000000000000000000002b500000000000000000000000000000000000000000000000000000000000002b600000000000000000000000000000000000000000000000000000000000002b700000000000000000000000000000000000000000000000000000000000002b800000000000000000000000000000000000000000000000000000000000002b900000000000000000000000000000000000000000000000000000000000002ba00000000000000000000000000000000000000000000000000000000000002bb00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bd00000000000000000000000000000000000000000000000000000000000002be0800789ff73d7787206612d96dfc2143f2344de21669a3f8cae7fe9a8918631eb00084a17f00bf8793b6851a106e9155543125e0be987ad3c8334456bdda171d0b00a400f8fd336ad84f467465964008238fd1b7f9c51c22912d706cd2b874d24e002c79bdd83c14ff50a46964f838ee207564909e28af79a57fc195810d36f9b20070083c6ef1e4dd88a064e94d2582283b203cf8a2ab1667f4370eda1b4c1fe8005373dffb5b590053d7762efcf9e11280f1486ce82e7996d94ee0f5d7c093bc009eefd90eb40e79c78bac1f71ec78bdc2f8b30041974239bdc765edffed813800ea95742e72792ca7a0f66ce9f55bc47dc09d5ea08c1b9018763102776978303f0000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000058a0000000000000000000000000000000000000000000000000000000000000581000000000000000000000000000000000000000000000000000000000000058b0000000000000000000000000000000000000000000000000000000000000582000000000000000000000000000000000000000000000000000000000000058c0000000000000000000000000000000000000000000000000000000000000583000000000000000000000000000000000000000000000000000000000000058d0000000000000000000000000000000000000000000000000000000000000584000000000000000000000000000000000000000000000000000000000000058e0000000000000000000000000000000000000000000000000000000000000585000000000000000000000000000000000000000000000000000000000000058f00000000000000000000000000000000000000000000000000000000000005860000000000000000000000000000000000000000000000000000000000000590000000000000000000000000000000000000000000000000000000000000058700000000000000000000000000000000000000000000000000000000000005910000000000000000000000000000000000000000000000000000000000000588000000000000000000000000000000000000000000000000000000000000059200000000000000000000000000000000000000000000000000000000000005890000000000000000000000000000000000000000000000000000000000000593000000000000000000000000000000000000000000000000000000000000058a0000000000000000000000000000000000000000000000000000000000000594000000000000000000000000000000000000000000000000000000000000058b0000000000000000000000000000000000000000000000000000000000000595000000000000000000000000000000000000000000000000000000000000058c0000000000000000000000000000000000000000000000000000000000000596000000000000000000000000000000000000000000000000000000000000058d0000000000000000000000000000000000000000000000000000000000000597000000000000000000000000000000000000000000000000000000000000058e0000000000000000000000000000000000000000000000000000000000000598000000000000000000000000000000000000000000000000000000000000058f00000000000000000000000000000000000000000000000000000000000005990000000000000000000000000000000000000000000000000000000000000590000000000000000000000000000000000000000000000000000000000000059a0000000000000000000000000000000000000000000000000000000000000591000000000000000000000000000000000000000000000000000000000000059b0000000000000000000000000000000000000000000000000000000000000592000000000000000000000000000000000000000000000000000000000000059c0000000000000000000000000000000000000000000000000000000000000593000000000000000000000000000000000000000000000000000000000000059d0000000000000000000000000000000000000000000000000000000000000594000000000000000000000000000000000000000000000000000000000000059e0000000000000000000000000000000000000000000000000000000000000595000000000000000000000000000000000000000000000000000000000000059f000000000000000000000000000000000000000000000000000000000000059600000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000059700000000000000000000000000000000000000000000000000000000000005a1000000000000000000000000000000000000000000000000000000000000059800000000000000000000000000000000000000000000000000000000000005a2000000000000000000000000000000000000000000000000000000000000059900000000000000000000000000000000000000000000000000000000000005a3000000000000000000000000000000000000000000000000000000000000059a00000000000000000000000000000000000000000000000000000000000005a4000000000000000000000000000000000000000000000000000000000000059b00000000000000000000000000000000000000000000000000000000000005a5000000000000000000000000000000000000000000000000000000000000059c00000000000000000000000000000000000000000000000000000000000005a6000000000000000000000000000000000000000000000000000000000000059d00000000000000000000000000000000000000000000000000000000000005a7000000000000000000000000000000000000000000000000000000000000059e00000000000000000000000000000000000000000000000000000000000005a8000000000000000000000000000000000000000000000000000000000000059f00000000000000000000000000000000000000000000000000000000000005a900000000000000000000000000000000000000000000000000000000000005a000000000000000000000000000000000000000000000000000000000000005aa00000000000000000000000000000000000000000000000000000000000005a100000000000000000000000000000000000000000000000000000000000005ab00000000000000000000000000000000000000000000000000000000000005a200000000000000000000000000000000000000000000000000000000000005ac00000000000000000000000000000000000000000000000000000000000005a300000000000000000000000000000000000000000000000000000000000005ad00000000000000000000000000000000000000000000000000000000000005a400000000000000000000000000000000000000000000000000000000000005ae00000000000000000000000000000000000000000000000000000000000005a500000000000000000000000000000000000000000000000000000000000005af00000000000000000000000000000000000000000000000000000000000005a600000000000000000000000000000000000000000000000000000000000005b000000000000000000000000000000000000000000000000000000000000005a700000000000000000000000000000000000000000000000000000000000005b100000000000000000000000000000000000000000000000000000000000005a800000000000000000000000000000000000000000000000000000000000005b200000000000000000000000000000000000000000000000000000000000005a900000000000000000000000000000000000000000000000000000000000005b300000000000000000000000000000000000000000000000000000000000005aa00000000000000000000000000000000000000000000000000000000000005b400000000000000000000000000000000000000000000000000000000000005ab00000000000000000000000000000000000000000000000000000000000005b500000000000000000000000000000000000000000000000000000000000005ac00000000000000000000000000000000000000000000000000000000000005b600000000000000000000000000000000000000000000000000000000000005ad00000000000000000000000000000000000000000000000000000000000005b700000000000000000000000000000000000000000000000000000000000005ae00000000000000000000000000000000000000000000000000000000000005b800000000000000000000000000000000000000000000000000000000000005af00000000000000000000000000000000000000000000000000000000000005b900000000000000000000000000000000000000000000000000000000000005b000000000000000000000000000000000000000000000000000000000000005ba00000000000000000000000000000000000000000000000000000000000005b100000000000000000000000000000000000000000000000000000000000005bb00000000000000000000000000000000000000000000000000000000000005b200000000000000000000000000000000000000000000000000000000000005bc00000000000000000000000000000000000000000000000000000000000005b300000000000000000000000000000000000000000000000000000000000005bd00000000000000000000000000000000000000000000000000000000000005b400000000000000000000000000000000000000000000000000000000000005be00000000000000000000000000000000000000000000000000000000000005b500000000000000000000000000000000000000000000000000000000000005bf00000000000000000000000000000000000000000000000000000000000005b600000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000005b700000000000000000000000000000000000000000000000000000000000005c100000000000000000000000000000000000000000000000000000000000005b800000000000000000000000000000000000000000000000000000000000005c200000000000000000000000000000000000000000000000000000000000005b900000000000000000000000000000000000000000000000000000000000005c300000000000000000000000000000000000000000000000000000000000005ba00000000000000000000000000000000000000000000000000000000000005c400000000000000000000000000000000000000000000000000000000000005bb00000000000000000000000000000000000000000000000000000000000005c500000000000000000000000000000000000000000000000000000000000005bc00000000000000000000000000000000000000000000000000000000000005c600000000000000000000000000000000000000000000000000000000000005bd00000000000000000000000000000000000000000000000000000000000005c700000000000000000000000000000000000000000000000000000000000005be00000000000000000000000000000000000000000000000000000000000005c80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001c100000000000000000000000000000000000000000000000000000000000001c200000000000000000000000000000000000000000000000000000000000001c300000000000000000000000000000000000000000000000000000000000001c400000000000000000000000000000000000000000000000000000000000001c500000000000000000000000000000000000000000000000000000000000001c600000000000000000000000000000000000000000000000000000000000001c700000000000000000000000000000000000000000000000000000000000001c800000000000000000000000000000000000000000000000000000000000001c900000000000000000000000000000000000000000000000000000000000001ca00000000000000000000000000000000000000000000000000000000000001cb00000000000000000000000000000000000000000000000000000000000001cc00000000000000000000000000000000000000000000000000000000000001cd00000000000000000000000000000000000000000000000000000000000001ce00000000000000000000000000000000000000000000000000000000000001cf00000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001d100000000000000000000000000000000000000000000000000000000000001d200000000000000000000000000000000000000000000000000000000000001d300000000000000000000000000000000000000000000000000000000000001d400000000000000000000000000000000000000000000000000000000000001d500000000000000000000000000000000000000000000000000000000000001d600000000000000000000000000000000000000000000000000000000000001d700000000000000000000000000000000000000000000000000000000000001d800000000000000000000000000000000000000000000000000000000000001d900000000000000000000000000000000000000000000000000000000000001da00000000000000000000000000000000000000000000000000000000000001db00000000000000000000000000000000000000000000000000000000000001dc00000000000000000000000000000000000000000000000000000000000001dd00000000000000000000000000000000000000000000000000000000000001de00000000000000000000000000000000000000000000000000000000000001df00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001e100000000000000000000000000000000000000000000000000000000000001e200000000000000000000000000000000000000000000000000000000000001e300000000000000000000000000000000000000000000000000000000000001e400000000000000000000000000000000000000000000000000000000000001e500000000000000000000000000000000000000000000000000000000000001e600000000000000000000000000000000000000000000000000000000000001e700000000000000000000000000000000000000000000000000000000000001e800000000000000000000000000000000000000000000000000000000000001e900000000000000000000000000000000000000000000000000000000000001ea00000000000000000000000000000000000000000000000000000000000001eb00000000000000000000000000000000000000000000000000000000000001ec00000000000000000000000000000000000000000000000000000000000001ed00000000000000000000000000000000000000000000000000000000000001ee00000000000000000000000000000000000000000000000000000000000001ef00000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000001f100000000000000000000000000000000000000000000000000000000000001f200000000000000000000000000000000000000000000000000000000000001f300000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000001f600000000000000000000000000000000000000000000000000000000000001f700000000000000000000000000000000000000000000000000000000000001f800000000000000000000000000000000000000000000000000000000000001f900000000000000000000000000000000000000000000000000000000000001fa00000000000000000000000000000000000000000000000000000000000001fb00000000000000000000000000000000000000000000000000000000000001fc00000000000000000000000000000000000000000000000000000000000001fd00000000000000000000000000000000000000000000000000000000000001fe00000000000000000000000000000000000000000000000000000000000001ff3f00000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002c100000000000000000000000000000000000000000000000000000000000002c200000000000000000000000000000000000000000000000000000000000002c300000000000000000000000000000000000000000000000000000000000002c400000000000000000000000000000000000000000000000000000000000002c500000000000000000000000000000000000000000000000000000000000002c600000000000000000000000000000000000000000000000000000000000002c700000000000000000000000000000000000000000000000000000000000002c800000000000000000000000000000000000000000000000000000000000002c900000000000000000000000000000000000000000000000000000000000002ca00000000000000000000000000000000000000000000000000000000000002cb00000000000000000000000000000000000000000000000000000000000002cc00000000000000000000000000000000000000000000000000000000000002cd00000000000000000000000000000000000000000000000000000000000002ce00000000000000000000000000000000000000000000000000000000000002cf00000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002d100000000000000000000000000000000000000000000000000000000000002d200000000000000000000000000000000000000000000000000000000000002d300000000000000000000000000000000000000000000000000000000000002d400000000000000000000000000000000000000000000000000000000000002d500000000000000000000000000000000000000000000000000000000000002d600000000000000000000000000000000000000000000000000000000000002d700000000000000000000000000000000000000000000000000000000000002d800000000000000000000000000000000000000000000000000000000000002d900000000000000000000000000000000000000000000000000000000000002da00000000000000000000000000000000000000000000000000000000000002db00000000000000000000000000000000000000000000000000000000000002dc00000000000000000000000000000000000000000000000000000000000002dd00000000000000000000000000000000000000000000000000000000000002de00000000000000000000000000000000000000000000000000000000000002df00000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002e100000000000000000000000000000000000000000000000000000000000002e200000000000000000000000000000000000000000000000000000000000002e300000000000000000000000000000000000000000000000000000000000002e400000000000000000000000000000000000000000000000000000000000002e500000000000000000000000000000000000000000000000000000000000002e600000000000000000000000000000000000000000000000000000000000002e700000000000000000000000000000000000000000000000000000000000002e800000000000000000000000000000000000000000000000000000000000002e900000000000000000000000000000000000000000000000000000000000002ea00000000000000000000000000000000000000000000000000000000000002eb00000000000000000000000000000000000000000000000000000000000002ec00000000000000000000000000000000000000000000000000000000000002ed00000000000000000000000000000000000000000000000000000000000002ee00000000000000000000000000000000000000000000000000000000000002ef00000000000000000000000000000000000000000000000000000000000002f000000000000000000000000000000000000000000000000000000000000002f100000000000000000000000000000000000000000000000000000000000002f200000000000000000000000000000000000000000000000000000000000002f300000000000000000000000000000000000000000000000000000000000002f400000000000000000000000000000000000000000000000000000000000002f500000000000000000000000000000000000000000000000000000000000002f600000000000000000000000000000000000000000000000000000000000002f700000000000000000000000000000000000000000000000000000000000002f800000000000000000000000000000000000000000000000000000000000002f900000000000000000000000000000000000000000000000000000000000002fa00000000000000000000000000000000000000000000000000000000000002fb00000000000000000000000000000000000000000000000000000000000002fc00000000000000000000000000000000000000000000000000000000000002fd00000000000000000000000000000000000000000000000000000000000002fe0800a68d2df6e48c8b31f4c76529e586de2cd9d0f2f2fdfbc4c523db9e3a29e9b80016c236e57bf17afe324437acd1060772e3f31d4f9e734ad758d0627c4ba2a200d659011ddde95e32886bdd8c9464da1ca144ccadd539f0992b4abb491d812a00c8025bb9006a976ebd6ad940155f15f89ca0bb7312b53841fc257e7ed689c8004165f2c46b70fb183468b38f31bba7aa9d22ce8dfb61fe721470729ce1c6b100afeb60dd983514ebbaee141b2196f3eb3c9c299f576a0097872cc85a79a43f005f6bfee53d20a474901493558419dbceb3aca40e5e18915d38031498f4d2bb008791217ee341ec5dc11f7d7a31160fb1b1f2cf767062970e9526e5751956253f00000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000005ca00000000000000000000000000000000000000000000000000000000000005c100000000000000000000000000000000000000000000000000000000000005cb00000000000000000000000000000000000000000000000000000000000005c200000000000000000000000000000000000000000000000000000000000005cc00000000000000000000000000000000000000000000000000000000000005c300000000000000000000000000000000000000000000000000000000000005cd00000000000000000000000000000000000000000000000000000000000005c400000000000000000000000000000000000000000000000000000000000005ce00000000000000000000000000000000000000000000000000000000000005c500000000000000000000000000000000000000000000000000000000000005cf00000000000000000000000000000000000000000000000000000000000005c600000000000000000000000000000000000000000000000000000000000005d000000000000000000000000000000000000000000000000000000000000005c700000000000000000000000000000000000000000000000000000000000005d100000000000000000000000000000000000000000000000000000000000005c800000000000000000000000000000000000000000000000000000000000005d200000000000000000000000000000000000000000000000000000000000005c900000000000000000000000000000000000000000000000000000000000005d300000000000000000000000000000000000000000000000000000000000005ca00000000000000000000000000000000000000000000000000000000000005d400000000000000000000000000000000000000000000000000000000000005cb00000000000000000000000000000000000000000000000000000000000005d500000000000000000000000000000000000000000000000000000000000005cc00000000000000000000000000000000000000000000000000000000000005d600000000000000000000000000000000000000000000000000000000000005cd00000000000000000000000000000000000000000000000000000000000005d700000000000000000000000000000000000000000000000000000000000005ce00000000000000000000000000000000000000000000000000000000000005d800000000000000000000000000000000000000000000000000000000000005cf00000000000000000000000000000000000000000000000000000000000005d900000000000000000000000000000000000000000000000000000000000005d000000000000000000000000000000000000000000000000000000000000005da00000000000000000000000000000000000000000000000000000000000005d100000000000000000000000000000000000000000000000000000000000005db00000000000000000000000000000000000000000000000000000000000005d200000000000000000000000000000000000000000000000000000000000005dc00000000000000000000000000000000000000000000000000000000000005d300000000000000000000000000000000000000000000000000000000000005dd00000000000000000000000000000000000000000000000000000000000005d400000000000000000000000000000000000000000000000000000000000005de00000000000000000000000000000000000000000000000000000000000005d500000000000000000000000000000000000000000000000000000000000005df00000000000000000000000000000000000000000000000000000000000005d600000000000000000000000000000000000000000000000000000000000005e000000000000000000000000000000000000000000000000000000000000005d700000000000000000000000000000000000000000000000000000000000005e100000000000000000000000000000000000000000000000000000000000005d800000000000000000000000000000000000000000000000000000000000005e200000000000000000000000000000000000000000000000000000000000005d900000000000000000000000000000000000000000000000000000000000005e300000000000000000000000000000000000000000000000000000000000005da00000000000000000000000000000000000000000000000000000000000005e400000000000000000000000000000000000000000000000000000000000005db00000000000000000000000000000000000000000000000000000000000005e500000000000000000000000000000000000000000000000000000000000005dc00000000000000000000000000000000000000000000000000000000000005e600000000000000000000000000000000000000000000000000000000000005dd00000000000000000000000000000000000000000000000000000000000005e700000000000000000000000000000000000000000000000000000000000005de00000000000000000000000000000000000000000000000000000000000005e800000000000000000000000000000000000000000000000000000000000005df00000000000000000000000000000000000000000000000000000000000005e900000000000000000000000000000000000000000000000000000000000005e000000000000000000000000000000000000000000000000000000000000005ea00000000000000000000000000000000000000000000000000000000000005e100000000000000000000000000000000000000000000000000000000000005eb00000000000000000000000000000000000000000000000000000000000005e200000000000000000000000000000000000000000000000000000000000005ec00000000000000000000000000000000000000000000000000000000000005e300000000000000000000000000000000000000000000000000000000000005ed00000000000000000000000000000000000000000000000000000000000005e400000000000000000000000000000000000000000000000000000000000005ee00000000000000000000000000000000000000000000000000000000000005e500000000000000000000000000000000000000000000000000000000000005ef00000000000000000000000000000000000000000000000000000000000005e600000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000005e700000000000000000000000000000000000000000000000000000000000005f100000000000000000000000000000000000000000000000000000000000005e800000000000000000000000000000000000000000000000000000000000005f200000000000000000000000000000000000000000000000000000000000005e900000000000000000000000000000000000000000000000000000000000005f300000000000000000000000000000000000000000000000000000000000005ea00000000000000000000000000000000000000000000000000000000000005f400000000000000000000000000000000000000000000000000000000000005eb00000000000000000000000000000000000000000000000000000000000005f500000000000000000000000000000000000000000000000000000000000005ec00000000000000000000000000000000000000000000000000000000000005f600000000000000000000000000000000000000000000000000000000000005ed00000000000000000000000000000000000000000000000000000000000005f700000000000000000000000000000000000000000000000000000000000005ee00000000000000000000000000000000000000000000000000000000000005f800000000000000000000000000000000000000000000000000000000000005ef00000000000000000000000000000000000000000000000000000000000005f900000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000005fa00000000000000000000000000000000000000000000000000000000000005f100000000000000000000000000000000000000000000000000000000000005fb00000000000000000000000000000000000000000000000000000000000005f200000000000000000000000000000000000000000000000000000000000005fc00000000000000000000000000000000000000000000000000000000000005f300000000000000000000000000000000000000000000000000000000000005fd00000000000000000000000000000000000000000000000000000000000005f400000000000000000000000000000000000000000000000000000000000005fe00000000000000000000000000000000000000000000000000000000000005f500000000000000000000000000000000000000000000000000000000000005ff00000000000000000000000000000000000000000000000000000000000005f6000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000005f7000000000000000000000000000000000000000000000000000000000000060100000000000000000000000000000000000000000000000000000000000005f8000000000000000000000000000000000000000000000000000000000000060200000000000000000000000000000000000000000000000000000000000005f9000000000000000000000000000000000000000000000000000000000000060300000000000000000000000000000000000000000000000000000000000005fa000000000000000000000000000000000000000000000000000000000000060400000000000000000000000000000000000000000000000000000000000005fb000000000000000000000000000000000000000000000000000000000000060500000000000000000000000000000000000000000000000000000000000005fc000000000000000000000000000000000000000000000000000000000000060600000000000000000000000000000000000000000000000000000000000005fd000000000000000000000000000000000000000000000000000000000000060700000000000000000000000000000000000000000000000000000000000005fe0000000000000000000000000000000000000000000000000000000000000608000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020100000000000000000000000000000000000000000000000000000000000002020000000000000000000000000000000000000000000000000000000000000203000000000000000000000000000000000000000000000000000000000000020400000000000000000000000000000000000000000000000000000000000002050000000000000000000000000000000000000000000000000000000000000206000000000000000000000000000000000000000000000000000000000000020700000000000000000000000000000000000000000000000000000000000002080000000000000000000000000000000000000000000000000000000000000209000000000000000000000000000000000000000000000000000000000000020a000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000020c000000000000000000000000000000000000000000000000000000000000020d000000000000000000000000000000000000000000000000000000000000020e000000000000000000000000000000000000000000000000000000000000020f0000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000021100000000000000000000000000000000000000000000000000000000000002120000000000000000000000000000000000000000000000000000000000000213000000000000000000000000000000000000000000000000000000000000021400000000000000000000000000000000000000000000000000000000000002150000000000000000000000000000000000000000000000000000000000000216000000000000000000000000000000000000000000000000000000000000021700000000000000000000000000000000000000000000000000000000000002180000000000000000000000000000000000000000000000000000000000000219000000000000000000000000000000000000000000000000000000000000021a000000000000000000000000000000000000000000000000000000000000021b000000000000000000000000000000000000000000000000000000000000021c000000000000000000000000000000000000000000000000000000000000021d000000000000000000000000000000000000000000000000000000000000021e000000000000000000000000000000000000000000000000000000000000021f0000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000022100000000000000000000000000000000000000000000000000000000000002220000000000000000000000000000000000000000000000000000000000000223000000000000000000000000000000000000000000000000000000000000022400000000000000000000000000000000000000000000000000000000000002250000000000000000000000000000000000000000000000000000000000000226000000000000000000000000000000000000000000000000000000000000022700000000000000000000000000000000000000000000000000000000000002280000000000000000000000000000000000000000000000000000000000000229000000000000000000000000000000000000000000000000000000000000022a000000000000000000000000000000000000000000000000000000000000022b000000000000000000000000000000000000000000000000000000000000022c000000000000000000000000000000000000000000000000000000000000022d000000000000000000000000000000000000000000000000000000000000022e000000000000000000000000000000000000000000000000000000000000022f0000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000023100000000000000000000000000000000000000000000000000000000000002320000000000000000000000000000000000000000000000000000000000000233000000000000000000000000000000000000000000000000000000000000023400000000000000000000000000000000000000000000000000000000000002350000000000000000000000000000000000000000000000000000000000000236000000000000000000000000000000000000000000000000000000000000023700000000000000000000000000000000000000000000000000000000000002380000000000000000000000000000000000000000000000000000000000000239000000000000000000000000000000000000000000000000000000000000023a000000000000000000000000000000000000000000000000000000000000023b000000000000000000000000000000000000000000000000000000000000023c000000000000000000000000000000000000000000000000000000000000023d000000000000000000000000000000000000000000000000000000000000023e000000000000000000000000000000000000000000000000000000000000023f3f0000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000030100000000000000000000000000000000000000000000000000000000000003020000000000000000000000000000000000000000000000000000000000000303000000000000000000000000000000000000000000000000000000000000030400000000000000000000000000000000000000000000000000000000000003050000000000000000000000000000000000000000000000000000000000000306000000000000000000000000000000000000000000000000000000000000030700000000000000000000000000000000000000000000000000000000000003080000000000000000000000000000000000000000000000000000000000000309000000000000000000000000000000000000000000000000000000000000030a000000000000000000000000000000000000000000000000000000000000030b000000000000000000000000000000000000000000000000000000000000030c000000000000000000000000000000000000000000000000000000000000030d000000000000000000000000000000000000000000000000000000000000030e000000000000000000000000000000000000000000000000000000000000030f0000000000000000000000000000000000000000000000000000000000000310000000000000000000000000000000000000000000000000000000000000031100000000000000000000000000000000000000000000000000000000000003120000000000000000000000000000000000000000000000000000000000000313000000000000000000000000000000000000000000000000000000000000031400000000000000000000000000000000000000000000000000000000000003150000000000000000000000000000000000000000000000000000000000000316000000000000000000000000000000000000000000000000000000000000031700000000000000000000000000000000000000000000000000000000000003180000000000000000000000000000000000000000000000000000000000000319000000000000000000000000000000000000000000000000000000000000031a000000000000000000000000000000000000000000000000000000000000031b000000000000000000000000000000000000000000000000000000000000031c000000000000000000000000000000000000000000000000000000000000031d000000000000000000000000000000000000000000000000000000000000031e000000000000000000000000000000000000000000000000000000000000031f0000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032100000000000000000000000000000000000000000000000000000000000003220000000000000000000000000000000000000000000000000000000000000323000000000000000000000000000000000000000000000000000000000000032400000000000000000000000000000000000000000000000000000000000003250000000000000000000000000000000000000000000000000000000000000326000000000000000000000000000000000000000000000000000000000000032700000000000000000000000000000000000000000000000000000000000003280000000000000000000000000000000000000000000000000000000000000329000000000000000000000000000000000000000000000000000000000000032a000000000000000000000000000000000000000000000000000000000000032b000000000000000000000000000000000000000000000000000000000000032c000000000000000000000000000000000000000000000000000000000000032d000000000000000000000000000000000000000000000000000000000000032e000000000000000000000000000000000000000000000000000000000000032f0000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000033100000000000000000000000000000000000000000000000000000000000003320000000000000000000000000000000000000000000000000000000000000333000000000000000000000000000000000000000000000000000000000000033400000000000000000000000000000000000000000000000000000000000003350000000000000000000000000000000000000000000000000000000000000336000000000000000000000000000000000000000000000000000000000000033700000000000000000000000000000000000000000000000000000000000003380000000000000000000000000000000000000000000000000000000000000339000000000000000000000000000000000000000000000000000000000000033a000000000000000000000000000000000000000000000000000000000000033b000000000000000000000000000000000000000000000000000000000000033c000000000000000000000000000000000000000000000000000000000000033d000000000000000000000000000000000000000000000000000000000000033e0800c064a9343bfaf1345a5ad188e96aa4c5bad0b4a5590da51a9ff8f3c98ade8d008803d57dd0923c95a01b2bfbee4df6d66dc7baee1d2cd2770575feb86d040200ea64eed9489feb7bdf29bf817a6e8772e549da7b291028852d0dd3810cee9500947e8f904d41be8a4e08b146b4e1f0cd88f55722ef987d1d485c4196ab9f71002e5d9ed5d71bc3bea6edf988c4b9ba7fceb0815180d893852ed343c64ab55c0040f7f519ec89d360d83e242b6c0ce049d2b3356b8cfbf1ff3b733ef0f8a089006f5cfe5fc4a7b87c634f9e253a7cea2052229250d0c0e913eb50b5ef3612de00b759fce0eed36bb653b67255cce111b3529c383bd7d2b758f8cb53a4451f273f0000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060a0000000000000000000000000000000000000000000000000000000000000601000000000000000000000000000000000000000000000000000000000000060b0000000000000000000000000000000000000000000000000000000000000602000000000000000000000000000000000000000000000000000000000000060c0000000000000000000000000000000000000000000000000000000000000603000000000000000000000000000000000000000000000000000000000000060d0000000000000000000000000000000000000000000000000000000000000604000000000000000000000000000000000000000000000000000000000000060e0000000000000000000000000000000000000000000000000000000000000605000000000000000000000000000000000000000000000000000000000000060f00000000000000000000000000000000000000000000000000000000000006060000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000060700000000000000000000000000000000000000000000000000000000000006110000000000000000000000000000000000000000000000000000000000000608000000000000000000000000000000000000000000000000000000000000061200000000000000000000000000000000000000000000000000000000000006090000000000000000000000000000000000000000000000000000000000000613000000000000000000000000000000000000000000000000000000000000060a0000000000000000000000000000000000000000000000000000000000000614000000000000000000000000000000000000000000000000000000000000060b0000000000000000000000000000000000000000000000000000000000000615000000000000000000000000000000000000000000000000000000000000060c0000000000000000000000000000000000000000000000000000000000000616000000000000000000000000000000000000000000000000000000000000060d0000000000000000000000000000000000000000000000000000000000000617000000000000000000000000000000000000000000000000000000000000060e0000000000000000000000000000000000000000000000000000000000000618000000000000000000000000000000000000000000000000000000000000060f00000000000000000000000000000000000000000000000000000000000006190000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000061a0000000000000000000000000000000000000000000000000000000000000611000000000000000000000000000000000000000000000000000000000000061b0000000000000000000000000000000000000000000000000000000000000612000000000000000000000000000000000000000000000000000000000000061c0000000000000000000000000000000000000000000000000000000000000613000000000000000000000000000000000000000000000000000000000000061d0000000000000000000000000000000000000000000000000000000000000614000000000000000000000000000000000000000000000000000000000000061e0000000000000000000000000000000000000000000000000000000000000615000000000000000000000000000000000000000000000000000000000000061f00000000000000000000000000000000000000000000000000000000000006160000000000000000000000000000000000000000000000000000000000000620000000000000000000000000000000000000000000000000000000000000061700000000000000000000000000000000000000000000000000000000000006210000000000000000000000000000000000000000000000000000000000000618000000000000000000000000000000000000000000000000000000000000062200000000000000000000000000000000000000000000000000000000000006190000000000000000000000000000000000000000000000000000000000000623000000000000000000000000000000000000000000000000000000000000061a0000000000000000000000000000000000000000000000000000000000000624000000000000000000000000000000000000000000000000000000000000061b0000000000000000000000000000000000000000000000000000000000000625000000000000000000000000000000000000000000000000000000000000061c0000000000000000000000000000000000000000000000000000000000000626000000000000000000000000000000000000000000000000000000000000061d0000000000000000000000000000000000000000000000000000000000000627000000000000000000000000000000000000000000000000000000000000061e0000000000000000000000000000000000000000000000000000000000000628000000000000000000000000000000000000000000000000000000000000061f00000000000000000000000000000000000000000000000000000000000006290000000000000000000000000000000000000000000000000000000000000620000000000000000000000000000000000000000000000000000000000000062a0000000000000000000000000000000000000000000000000000000000000621000000000000000000000000000000000000000000000000000000000000062b0000000000000000000000000000000000000000000000000000000000000622000000000000000000000000000000000000000000000000000000000000062c0000000000000000000000000000000000000000000000000000000000000623000000000000000000000000000000000000000000000000000000000000062d0000000000000000000000000000000000000000000000000000000000000624000000000000000000000000000000000000000000000000000000000000062e0000000000000000000000000000000000000000000000000000000000000625000000000000000000000000000000000000000000000000000000000000062f00000000000000000000000000000000000000000000000000000000000006260000000000000000000000000000000000000000000000000000000000000630000000000000000000000000000000000000000000000000000000000000062700000000000000000000000000000000000000000000000000000000000006310000000000000000000000000000000000000000000000000000000000000628000000000000000000000000000000000000000000000000000000000000063200000000000000000000000000000000000000000000000000000000000006290000000000000000000000000000000000000000000000000000000000000633000000000000000000000000000000000000000000000000000000000000062a0000000000000000000000000000000000000000000000000000000000000634000000000000000000000000000000000000000000000000000000000000062b0000000000000000000000000000000000000000000000000000000000000635000000000000000000000000000000000000000000000000000000000000062c0000000000000000000000000000000000000000000000000000000000000636000000000000000000000000000000000000000000000000000000000000062d0000000000000000000000000000000000000000000000000000000000000637000000000000000000000000000000000000000000000000000000000000062e0000000000000000000000000000000000000000000000000000000000000638000000000000000000000000000000000000000000000000000000000000062f00000000000000000000000000000000000000000000000000000000000006390000000000000000000000000000000000000000000000000000000000000630000000000000000000000000000000000000000000000000000000000000063a0000000000000000000000000000000000000000000000000000000000000631000000000000000000000000000000000000000000000000000000000000063b0000000000000000000000000000000000000000000000000000000000000632000000000000000000000000000000000000000000000000000000000000063c0000000000000000000000000000000000000000000000000000000000000633000000000000000000000000000000000000000000000000000000000000063d0000000000000000000000000000000000000000000000000000000000000634000000000000000000000000000000000000000000000000000000000000063e0000000000000000000000000000000000000000000000000000000000000635000000000000000000000000000000000000000000000000000000000000063f00000000000000000000000000000000000000000000000000000000000006360000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000063700000000000000000000000000000000000000000000000000000000000006410000000000000000000000000000000000000000000000000000000000000638000000000000000000000000000000000000000000000000000000000000064200000000000000000000000000000000000000000000000000000000000006390000000000000000000000000000000000000000000000000000000000000643000000000000000000000000000000000000000000000000000000000000063a0000000000000000000000000000000000000000000000000000000000000644000000000000000000000000000000000000000000000000000000000000063b0000000000000000000000000000000000000000000000000000000000000645000000000000000000000000000000000000000000000000000000000000063c0000000000000000000000000000000000000000000000000000000000000646000000000000000000000000000000000000000000000000000000000000063d0000000000000000000000000000000000000000000000000000000000000647000000000000000000000000000000000000000000000000000000000000063e0000000000000000000000000000000000000000000000000000000000000648000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000024100000000000000000000000000000000000000000000000000000000000002420000000000000000000000000000000000000000000000000000000000000243000000000000000000000000000000000000000000000000000000000000024400000000000000000000000000000000000000000000000000000000000002450000000000000000000000000000000000000000000000000000000000000246000000000000000000000000000000000000000000000000000000000000024700000000000000000000000000000000000000000000000000000000000002480000000000000000000000000000000000000000000000000000000000000249000000000000000000000000000000000000000000000000000000000000024a000000000000000000000000000000000000000000000000000000000000024b000000000000000000000000000000000000000000000000000000000000024c000000000000000000000000000000000000000000000000000000000000024d000000000000000000000000000000000000000000000000000000000000024e000000000000000000000000000000000000000000000000000000000000024f0000000000000000000000000000000000000000000000000000000000000250000000000000000000000000000000000000000000000000000000000000025100000000000000000000000000000000000000000000000000000000000002520000000000000000000000000000000000000000000000000000000000000253000000000000000000000000000000000000000000000000000000000000025400000000000000000000000000000000000000000000000000000000000002550000000000000000000000000000000000000000000000000000000000000256000000000000000000000000000000000000000000000000000000000000025700000000000000000000000000000000000000000000000000000000000002580000000000000000000000000000000000000000000000000000000000000259000000000000000000000000000000000000000000000000000000000000025a000000000000000000000000000000000000000000000000000000000000025b000000000000000000000000000000000000000000000000000000000000025c000000000000000000000000000000000000000000000000000000000000025d000000000000000000000000000000000000000000000000000000000000025e000000000000000000000000000000000000000000000000000000000000025f0000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000026100000000000000000000000000000000000000000000000000000000000002620000000000000000000000000000000000000000000000000000000000000263000000000000000000000000000000000000000000000000000000000000026400000000000000000000000000000000000000000000000000000000000002650000000000000000000000000000000000000000000000000000000000000266000000000000000000000000000000000000000000000000000000000000026700000000000000000000000000000000000000000000000000000000000002680000000000000000000000000000000000000000000000000000000000000269000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000026b000000000000000000000000000000000000000000000000000000000000026c000000000000000000000000000000000000000000000000000000000000026d000000000000000000000000000000000000000000000000000000000000026e000000000000000000000000000000000000000000000000000000000000026f0000000000000000000000000000000000000000000000000000000000000270000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000002720000000000000000000000000000000000000000000000000000000000000273000000000000000000000000000000000000000000000000000000000000027400000000000000000000000000000000000000000000000000000000000002750000000000000000000000000000000000000000000000000000000000000276000000000000000000000000000000000000000000000000000000000000027700000000000000000000000000000000000000000000000000000000000002780000000000000000000000000000000000000000000000000000000000000279000000000000000000000000000000000000000000000000000000000000027a000000000000000000000000000000000000000000000000000000000000027b000000000000000000000000000000000000000000000000000000000000027c000000000000000000000000000000000000000000000000000000000000027d000000000000000000000000000000000000000000000000000000000000027e000000000000000000000000000000000000000000000000000000000000027f3f0000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000003420000000000000000000000000000000000000000000000000000000000000343000000000000000000000000000000000000000000000000000000000000034400000000000000000000000000000000000000000000000000000000000003450000000000000000000000000000000000000000000000000000000000000346000000000000000000000000000000000000000000000000000000000000034700000000000000000000000000000000000000000000000000000000000003480000000000000000000000000000000000000000000000000000000000000349000000000000000000000000000000000000000000000000000000000000034a000000000000000000000000000000000000000000000000000000000000034b000000000000000000000000000000000000000000000000000000000000034c000000000000000000000000000000000000000000000000000000000000034d000000000000000000000000000000000000000000000000000000000000034e000000000000000000000000000000000000000000000000000000000000034f0000000000000000000000000000000000000000000000000000000000000350000000000000000000000000000000000000000000000000000000000000035100000000000000000000000000000000000000000000000000000000000003520000000000000000000000000000000000000000000000000000000000000353000000000000000000000000000000000000000000000000000000000000035400000000000000000000000000000000000000000000000000000000000003550000000000000000000000000000000000000000000000000000000000000356000000000000000000000000000000000000000000000000000000000000035700000000000000000000000000000000000000000000000000000000000003580000000000000000000000000000000000000000000000000000000000000359000000000000000000000000000000000000000000000000000000000000035a000000000000000000000000000000000000000000000000000000000000035b000000000000000000000000000000000000000000000000000000000000035c000000000000000000000000000000000000000000000000000000000000035d000000000000000000000000000000000000000000000000000000000000035e000000000000000000000000000000000000000000000000000000000000035f0000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000036100000000000000000000000000000000000000000000000000000000000003620000000000000000000000000000000000000000000000000000000000000363000000000000000000000000000000000000000000000000000000000000036400000000000000000000000000000000000000000000000000000000000003650000000000000000000000000000000000000000000000000000000000000366000000000000000000000000000000000000000000000000000000000000036700000000000000000000000000000000000000000000000000000000000003680000000000000000000000000000000000000000000000000000000000000369000000000000000000000000000000000000000000000000000000000000036a000000000000000000000000000000000000000000000000000000000000036b000000000000000000000000000000000000000000000000000000000000036c000000000000000000000000000000000000000000000000000000000000036d000000000000000000000000000000000000000000000000000000000000036e000000000000000000000000000000000000000000000000000000000000036f0000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000037100000000000000000000000000000000000000000000000000000000000003720000000000000000000000000000000000000000000000000000000000000373000000000000000000000000000000000000000000000000000000000000037400000000000000000000000000000000000000000000000000000000000003750000000000000000000000000000000000000000000000000000000000000376000000000000000000000000000000000000000000000000000000000000037700000000000000000000000000000000000000000000000000000000000003780000000000000000000000000000000000000000000000000000000000000379000000000000000000000000000000000000000000000000000000000000037a000000000000000000000000000000000000000000000000000000000000037b000000000000000000000000000000000000000000000000000000000000037c000000000000000000000000000000000000000000000000000000000000037d000000000000000000000000000000000000000000000000000000000000037e08003bbb1177505f3433bb062787fcac6585d30fa1a7e0b809ca5eef1cecc56cd0002d5b86280022d106b72e4425ea49f927ca2b8138fc13b3d9feeaf36ae61fb100adab9d66b73ae23a6e9127ebe0bcd963ef4312dd66878a6be131d39a7ee01c00d48f30dbb82c96c734c8abe33f4f9f75e6d0ba4462ee07d73c5335e04a02e900f31a3a4e7333ac6043a929fca12f5347e9e8bf1d67f5993860a774f10b77bc00230e3132bfa5df23e2e018b20cd4e0c75555825ee7924da73f017a279d39cd0095e2affd6b67c6ecbf77fa1e638139498e1642abb468c58ca8ce275260ea6100362e0941035fd171fab926caf55d18b22f7de99d60ac6f454386a9cce4a1ff3f0000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064a0000000000000000000000000000000000000000000000000000000000000641000000000000000000000000000000000000000000000000000000000000064b0000000000000000000000000000000000000000000000000000000000000642000000000000000000000000000000000000000000000000000000000000064c0000000000000000000000000000000000000000000000000000000000000643000000000000000000000000000000000000000000000000000000000000064d0000000000000000000000000000000000000000000000000000000000000644000000000000000000000000000000000000000000000000000000000000064e0000000000000000000000000000000000000000000000000000000000000645000000000000000000000000000000000000000000000000000000000000064f00000000000000000000000000000000000000000000000000000000000006460000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000000064700000000000000000000000000000000000000000000000000000000000006510000000000000000000000000000000000000000000000000000000000000648000000000000000000000000000000000000000000000000000000000000065200000000000000000000000000000000000000000000000000000000000006490000000000000000000000000000000000000000000000000000000000000653000000000000000000000000000000000000000000000000000000000000064a0000000000000000000000000000000000000000000000000000000000000654000000000000000000000000000000000000000000000000000000000000064b0000000000000000000000000000000000000000000000000000000000000655000000000000000000000000000000000000000000000000000000000000064c0000000000000000000000000000000000000000000000000000000000000656000000000000000000000000000000000000000000000000000000000000064d0000000000000000000000000000000000000000000000000000000000000657000000000000000000000000000000000000000000000000000000000000064e0000000000000000000000000000000000000000000000000000000000000658000000000000000000000000000000000000000000000000000000000000064f00000000000000000000000000000000000000000000000000000000000006590000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000000065a0000000000000000000000000000000000000000000000000000000000000651000000000000000000000000000000000000000000000000000000000000065b0000000000000000000000000000000000000000000000000000000000000652000000000000000000000000000000000000000000000000000000000000065c0000000000000000000000000000000000000000000000000000000000000653000000000000000000000000000000000000000000000000000000000000065d0000000000000000000000000000000000000000000000000000000000000654000000000000000000000000000000000000000000000000000000000000065e0000000000000000000000000000000000000000000000000000000000000655000000000000000000000000000000000000000000000000000000000000065f00000000000000000000000000000000000000000000000000000000000006560000000000000000000000000000000000000000000000000000000000000660000000000000000000000000000000000000000000000000000000000000065700000000000000000000000000000000000000000000000000000000000006610000000000000000000000000000000000000000000000000000000000000658000000000000000000000000000000000000000000000000000000000000066200000000000000000000000000000000000000000000000000000000000006590000000000000000000000000000000000000000000000000000000000000663000000000000000000000000000000000000000000000000000000000000065a0000000000000000000000000000000000000000000000000000000000000664000000000000000000000000000000000000000000000000000000000000065b0000000000000000000000000000000000000000000000000000000000000665000000000000000000000000000000000000000000000000000000000000065c0000000000000000000000000000000000000000000000000000000000000666000000000000000000000000000000000000000000000000000000000000065d0000000000000000000000000000000000000000000000000000000000000667000000000000000000000000000000000000000000000000000000000000065e0000000000000000000000000000000000000000000000000000000000000668000000000000000000000000000000000000000000000000000000000000065f00000000000000000000000000000000000000000000000000000000000006690000000000000000000000000000000000000000000000000000000000000660000000000000000000000000000000000000000000000000000000000000066a0000000000000000000000000000000000000000000000000000000000000661000000000000000000000000000000000000000000000000000000000000066b0000000000000000000000000000000000000000000000000000000000000662000000000000000000000000000000000000000000000000000000000000066c0000000000000000000000000000000000000000000000000000000000000663000000000000000000000000000000000000000000000000000000000000066d0000000000000000000000000000000000000000000000000000000000000664000000000000000000000000000000000000000000000000000000000000066e0000000000000000000000000000000000000000000000000000000000000665000000000000000000000000000000000000000000000000000000000000066f00000000000000000000000000000000000000000000000000000000000006660000000000000000000000000000000000000000000000000000000000000670000000000000000000000000000000000000000000000000000000000000066700000000000000000000000000000000000000000000000000000000000006710000000000000000000000000000000000000000000000000000000000000668000000000000000000000000000000000000000000000000000000000000067200000000000000000000000000000000000000000000000000000000000006690000000000000000000000000000000000000000000000000000000000000673000000000000000000000000000000000000000000000000000000000000066a0000000000000000000000000000000000000000000000000000000000000674000000000000000000000000000000000000000000000000000000000000066b0000000000000000000000000000000000000000000000000000000000000675000000000000000000000000000000000000000000000000000000000000066c0000000000000000000000000000000000000000000000000000000000000676000000000000000000000000000000000000000000000000000000000000066d0000000000000000000000000000000000000000000000000000000000000677000000000000000000000000000000000000000000000000000000000000066e0000000000000000000000000000000000000000000000000000000000000678000000000000000000000000000000000000000000000000000000000000066f00000000000000000000000000000000000000000000000000000000000006790000000000000000000000000000000000000000000000000000000000000670000000000000000000000000000000000000000000000000000000000000067a0000000000000000000000000000000000000000000000000000000000000671000000000000000000000000000000000000000000000000000000000000067b0000000000000000000000000000000000000000000000000000000000000672000000000000000000000000000000000000000000000000000000000000067c0000000000000000000000000000000000000000000000000000000000000673000000000000000000000000000000000000000000000000000000000000067d0000000000000000000000000000000000000000000000000000000000000674000000000000000000000000000000000000000000000000000000000000067e0000000000000000000000000000000000000000000000000000000000000675000000000000000000000000000000000000000000000000000000000000067f00000000000000000000000000000000000000000000000000000000000006760000000000000000000000000000000000000000000000000000000000000680000000000000000000000000000000000000000000000000000000000000067700000000000000000000000000000000000000000000000000000000000006810000000000000000000000000000000000000000000000000000000000000678000000000000000000000000000000000000000000000000000000000000068200000000000000000000000000000000000000000000000000000000000006790000000000000000000000000000000000000000000000000000000000000683000000000000000000000000000000000000000000000000000000000000067a0000000000000000000000000000000000000000000000000000000000000684000000000000000000000000000000000000000000000000000000000000067b0000000000000000000000000000000000000000000000000000000000000685000000000000000000000000000000000000000000000000000000000000067c0000000000000000000000000000000000000000000000000000000000000686000000000000000000000000000000000000000000000000000000000000067d0000000000000000000000000000000000000000000000000000000000000687000000000000000000000000000000000000000000000000000000000000067e0000000000000000000000000000000000000000000000000000000000000688000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000028100000000000000000000000000000000000000000000000000000000000002820000000000000000000000000000000000000000000000000000000000000283000000000000000000000000000000000000000000000000000000000000028400000000000000000000000000000000000000000000000000000000000002850000000000000000000000000000000000000000000000000000000000000286000000000000000000000000000000000000000000000000000000000000028700000000000000000000000000000000000000000000000000000000000002880000000000000000000000000000000000000000000000000000000000000289000000000000000000000000000000000000000000000000000000000000028a000000000000000000000000000000000000000000000000000000000000028b000000000000000000000000000000000000000000000000000000000000028c000000000000000000000000000000000000000000000000000000000000028d000000000000000000000000000000000000000000000000000000000000028e000000000000000000000000000000000000000000000000000000000000028f0000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000000000000000000000000000000029100000000000000000000000000000000000000000000000000000000000002920000000000000000000000000000000000000000000000000000000000000293000000000000000000000000000000000000000000000000000000000000029400000000000000000000000000000000000000000000000000000000000002950000000000000000000000000000000000000000000000000000000000000296000000000000000000000000000000000000000000000000000000000000029700000000000000000000000000000000000000000000000000000000000002980000000000000000000000000000000000000000000000000000000000000299000000000000000000000000000000000000000000000000000000000000029a000000000000000000000000000000000000000000000000000000000000029b000000000000000000000000000000000000000000000000000000000000029c000000000000000000000000000000000000000000000000000000000000029d000000000000000000000000000000000000000000000000000000000000029e000000000000000000000000000000000000000000000000000000000000029f00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002a100000000000000000000000000000000000000000000000000000000000002a200000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000002a400000000000000000000000000000000000000000000000000000000000002a500000000000000000000000000000000000000000000000000000000000002a600000000000000000000000000000000000000000000000000000000000002a700000000000000000000000000000000000000000000000000000000000002a800000000000000000000000000000000000000000000000000000000000002a900000000000000000000000000000000000000000000000000000000000002aa00000000000000000000000000000000000000000000000000000000000002ab00000000000000000000000000000000000000000000000000000000000002ac00000000000000000000000000000000000000000000000000000000000002ad00000000000000000000000000000000000000000000000000000000000002ae00000000000000000000000000000000000000000000000000000000000002af00000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002b100000000000000000000000000000000000000000000000000000000000002b200000000000000000000000000000000000000000000000000000000000002b300000000000000000000000000000000000000000000000000000000000002b400000000000000000000000000000000000000000000000000000000000002b500000000000000000000000000000000000000000000000000000000000002b600000000000000000000000000000000000000000000000000000000000002b700000000000000000000000000000000000000000000000000000000000002b800000000000000000000000000000000000000000000000000000000000002b900000000000000000000000000000000000000000000000000000000000002ba00000000000000000000000000000000000000000000000000000000000002bb00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bd00000000000000000000000000000000000000000000000000000000000002be00000000000000000000000000000000000000000000000000000000000002bf3f0000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000038100000000000000000000000000000000000000000000000000000000000003820000000000000000000000000000000000000000000000000000000000000383000000000000000000000000000000000000000000000000000000000000038400000000000000000000000000000000000000000000000000000000000003850000000000000000000000000000000000000000000000000000000000000386000000000000000000000000000000000000000000000000000000000000038700000000000000000000000000000000000000000000000000000000000003880000000000000000000000000000000000000000000000000000000000000389000000000000000000000000000000000000000000000000000000000000038a000000000000000000000000000000000000000000000000000000000000038b000000000000000000000000000000000000000000000000000000000000038c000000000000000000000000000000000000000000000000000000000000038d000000000000000000000000000000000000000000000000000000000000038e000000000000000000000000000000000000000000000000000000000000038f0000000000000000000000000000000000000000000000000000000000000390000000000000000000000000000000000000000000000000000000000000039100000000000000000000000000000000000000000000000000000000000003920000000000000000000000000000000000000000000000000000000000000393000000000000000000000000000000000000000000000000000000000000039400000000000000000000000000000000000000000000000000000000000003950000000000000000000000000000000000000000000000000000000000000396000000000000000000000000000000000000000000000000000000000000039700000000000000000000000000000000000000000000000000000000000003980000000000000000000000000000000000000000000000000000000000000399000000000000000000000000000000000000000000000000000000000000039a000000000000000000000000000000000000000000000000000000000000039b000000000000000000000000000000000000000000000000000000000000039c000000000000000000000000000000000000000000000000000000000000039d000000000000000000000000000000000000000000000000000000000000039e000000000000000000000000000000000000000000000000000000000000039f00000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003a100000000000000000000000000000000000000000000000000000000000003a200000000000000000000000000000000000000000000000000000000000003a300000000000000000000000000000000000000000000000000000000000003a400000000000000000000000000000000000000000000000000000000000003a500000000000000000000000000000000000000000000000000000000000003a600000000000000000000000000000000000000000000000000000000000003a700000000000000000000000000000000000000000000000000000000000003a800000000000000000000000000000000000000000000000000000000000003a900000000000000000000000000000000000000000000000000000000000003aa00000000000000000000000000000000000000000000000000000000000003ab00000000000000000000000000000000000000000000000000000000000003ac00000000000000000000000000000000000000000000000000000000000003ad00000000000000000000000000000000000000000000000000000000000003ae00000000000000000000000000000000000000000000000000000000000003af00000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003b100000000000000000000000000000000000000000000000000000000000003b200000000000000000000000000000000000000000000000000000000000003b300000000000000000000000000000000000000000000000000000000000003b400000000000000000000000000000000000000000000000000000000000003b500000000000000000000000000000000000000000000000000000000000003b600000000000000000000000000000000000000000000000000000000000003b700000000000000000000000000000000000000000000000000000000000003b800000000000000000000000000000000000000000000000000000000000003b900000000000000000000000000000000000000000000000000000000000003ba00000000000000000000000000000000000000000000000000000000000003bb00000000000000000000000000000000000000000000000000000000000003bc00000000000000000000000000000000000000000000000000000000000003bd00000000000000000000000000000000000000000000000000000000000003be08004a32502b5d2a0cf5d776c92ef74de61a28e7882bdadeb3b0530f472704604300808c2412e2725ecaa6a6bd77e3159349e238dc7817f906ba32afd40b3cb3cb00d7d4e3b313c4cce9bd98c317ea715847a92d795b82a6f8b8144b7c61bee64200b76f07678c289f41378029b1353a73e1afbe241612a97c23e7fc059099f49d00c72046b39a9dc902cee36db5214c72315636bd824abfabdf80b1c5e6a01fd7006e0a74fec166a12f5a62954776784f01cba3be7ab876eef2e49a2ab7a5b0f900c83ddd8f6b91086bc83485adbe8056212b4d33b91840cd3dc649f9736881460022441e76225010acce7f429dc754fb3260ae1d387937978f69c67a879ed0733f0000000000000000000000000000000000000000000000000000000000000680000000000000000000000000000000000000000000000000000000000000068a0000000000000000000000000000000000000000000000000000000000000681000000000000000000000000000000000000000000000000000000000000068b0000000000000000000000000000000000000000000000000000000000000682000000000000000000000000000000000000000000000000000000000000068c0000000000000000000000000000000000000000000000000000000000000683000000000000000000000000000000000000000000000000000000000000068d0000000000000000000000000000000000000000000000000000000000000684000000000000000000000000000000000000000000000000000000000000068e0000000000000000000000000000000000000000000000000000000000000685000000000000000000000000000000000000000000000000000000000000068f00000000000000000000000000000000000000000000000000000000000006860000000000000000000000000000000000000000000000000000000000000690000000000000000000000000000000000000000000000000000000000000068700000000000000000000000000000000000000000000000000000000000006910000000000000000000000000000000000000000000000000000000000000688000000000000000000000000000000000000000000000000000000000000069200000000000000000000000000000000000000000000000000000000000006890000000000000000000000000000000000000000000000000000000000000693000000000000000000000000000000000000000000000000000000000000068a0000000000000000000000000000000000000000000000000000000000000694000000000000000000000000000000000000000000000000000000000000068b0000000000000000000000000000000000000000000000000000000000000695000000000000000000000000000000000000000000000000000000000000068c0000000000000000000000000000000000000000000000000000000000000696000000000000000000000000000000000000000000000000000000000000068d0000000000000000000000000000000000000000000000000000000000000697000000000000000000000000000000000000000000000000000000000000068e0000000000000000000000000000000000000000000000000000000000000698000000000000000000000000000000000000000000000000000000000000068f00000000000000000000000000000000000000000000000000000000000006990000000000000000000000000000000000000000000000000000000000000690000000000000000000000000000000000000000000000000000000000000069a0000000000000000000000000000000000000000000000000000000000000691000000000000000000000000000000000000000000000000000000000000069b0000000000000000000000000000000000000000000000000000000000000692000000000000000000000000000000000000000000000000000000000000069c0000000000000000000000000000000000000000000000000000000000000693000000000000000000000000000000000000000000000000000000000000069d0000000000000000000000000000000000000000000000000000000000000694000000000000000000000000000000000000000000000000000000000000069e0000000000000000000000000000000000000000000000000000000000000695000000000000000000000000000000000000000000000000000000000000069f000000000000000000000000000000000000000000000000000000000000069600000000000000000000000000000000000000000000000000000000000006a0000000000000000000000000000000000000000000000000000000000000069700000000000000000000000000000000000000000000000000000000000006a1000000000000000000000000000000000000000000000000000000000000069800000000000000000000000000000000000000000000000000000000000006a2000000000000000000000000000000000000000000000000000000000000069900000000000000000000000000000000000000000000000000000000000006a3000000000000000000000000000000000000000000000000000000000000069a00000000000000000000000000000000000000000000000000000000000006a4000000000000000000000000000000000000000000000000000000000000069b00000000000000000000000000000000000000000000000000000000000006a5000000000000000000000000000000000000000000000000000000000000069c00000000000000000000000000000000000000000000000000000000000006a6000000000000000000000000000000000000000000000000000000000000069d00000000000000000000000000000000000000000000000000000000000006a7000000000000000000000000000000000000000000000000000000000000069e00000000000000000000000000000000000000000000000000000000000006a8000000000000000000000000000000000000000000000000000000000000069f00000000000000000000000000000000000000000000000000000000000006a900000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000006aa00000000000000000000000000000000000000000000000000000000000006a100000000000000000000000000000000000000000000000000000000000006ab00000000000000000000000000000000000000000000000000000000000006a200000000000000000000000000000000000000000000000000000000000006ac00000000000000000000000000000000000000000000000000000000000006a300000000000000000000000000000000000000000000000000000000000006ad00000000000000000000000000000000000000000000000000000000000006a400000000000000000000000000000000000000000000000000000000000006ae00000000000000000000000000000000000000000000000000000000000006a500000000000000000000000000000000000000000000000000000000000006af00000000000000000000000000000000000000000000000000000000000006a600000000000000000000000000000000000000000000000000000000000006b000000000000000000000000000000000000000000000000000000000000006a700000000000000000000000000000000000000000000000000000000000006b100000000000000000000000000000000000000000000000000000000000006a800000000000000000000000000000000000000000000000000000000000006b200000000000000000000000000000000000000000000000000000000000006a900000000000000000000000000000000000000000000000000000000000006b300000000000000000000000000000000000000000000000000000000000006aa00000000000000000000000000000000000000000000000000000000000006b400000000000000000000000000000000000000000000000000000000000006ab00000000000000000000000000000000000000000000000000000000000006b500000000000000000000000000000000000000000000000000000000000006ac00000000000000000000000000000000000000000000000000000000000006b600000000000000000000000000000000000000000000000000000000000006ad00000000000000000000000000000000000000000000000000000000000006b700000000000000000000000000000000000000000000000000000000000006ae00000000000000000000000000000000000000000000000000000000000006b800000000000000000000000000000000000000000000000000000000000006af00000000000000000000000000000000000000000000000000000000000006b900000000000000000000000000000000000000000000000000000000000006b000000000000000000000000000000000000000000000000000000000000006ba00000000000000000000000000000000000000000000000000000000000006b100000000000000000000000000000000000000000000000000000000000006bb00000000000000000000000000000000000000000000000000000000000006b200000000000000000000000000000000000000000000000000000000000006bc00000000000000000000000000000000000000000000000000000000000006b300000000000000000000000000000000000000000000000000000000000006bd00000000000000000000000000000000000000000000000000000000000006b400000000000000000000000000000000000000000000000000000000000006be00000000000000000000000000000000000000000000000000000000000006b500000000000000000000000000000000000000000000000000000000000006bf00000000000000000000000000000000000000000000000000000000000006b600000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000006b700000000000000000000000000000000000000000000000000000000000006c100000000000000000000000000000000000000000000000000000000000006b800000000000000000000000000000000000000000000000000000000000006c200000000000000000000000000000000000000000000000000000000000006b900000000000000000000000000000000000000000000000000000000000006c300000000000000000000000000000000000000000000000000000000000006ba00000000000000000000000000000000000000000000000000000000000006c400000000000000000000000000000000000000000000000000000000000006bb00000000000000000000000000000000000000000000000000000000000006c500000000000000000000000000000000000000000000000000000000000006bc00000000000000000000000000000000000000000000000000000000000006c600000000000000000000000000000000000000000000000000000000000006bd00000000000000000000000000000000000000000000000000000000000006c700000000000000000000000000000000000000000000000000000000000006be00000000000000000000000000000000000000000000000000000000000006c80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002c100000000000000000000000000000000000000000000000000000000000002c200000000000000000000000000000000000000000000000000000000000002c300000000000000000000000000000000000000000000000000000000000002c400000000000000000000000000000000000000000000000000000000000002c500000000000000000000000000000000000000000000000000000000000002c600000000000000000000000000000000000000000000000000000000000002c700000000000000000000000000000000000000000000000000000000000002c800000000000000000000000000000000000000000000000000000000000002c900000000000000000000000000000000000000000000000000000000000002ca00000000000000000000000000000000000000000000000000000000000002cb00000000000000000000000000000000000000000000000000000000000002cc00000000000000000000000000000000000000000000000000000000000002cd00000000000000000000000000000000000000000000000000000000000002ce00000000000000000000000000000000000000000000000000000000000002cf00000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002d100000000000000000000000000000000000000000000000000000000000002d200000000000000000000000000000000000000000000000000000000000002d300000000000000000000000000000000000000000000000000000000000002d400000000000000000000000000000000000000000000000000000000000002d500000000000000000000000000000000000000000000000000000000000002d600000000000000000000000000000000000000000000000000000000000002d700000000000000000000000000000000000000000000000000000000000002d800000000000000000000000000000000000000000000000000000000000002d900000000000000000000000000000000000000000000000000000000000002da00000000000000000000000000000000000000000000000000000000000002db00000000000000000000000000000000000000000000000000000000000002dc00000000000000000000000000000000000000000000000000000000000002dd00000000000000000000000000000000000000000000000000000000000002de00000000000000000000000000000000000000000000000000000000000002df00000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002e100000000000000000000000000000000000000000000000000000000000002e200000000000000000000000000000000000000000000000000000000000002e300000000000000000000000000000000000000000000000000000000000002e400000000000000000000000000000000000000000000000000000000000002e500000000000000000000000000000000000000000000000000000000000002e600000000000000000000000000000000000000000000000000000000000002e700000000000000000000000000000000000000000000000000000000000002e800000000000000000000000000000000000000000000000000000000000002e900000000000000000000000000000000000000000000000000000000000002ea00000000000000000000000000000000000000000000000000000000000002eb00000000000000000000000000000000000000000000000000000000000002ec00000000000000000000000000000000000000000000000000000000000002ed00000000000000000000000000000000000000000000000000000000000002ee00000000000000000000000000000000000000000000000000000000000002ef00000000000000000000000000000000000000000000000000000000000002f000000000000000000000000000000000000000000000000000000000000002f100000000000000000000000000000000000000000000000000000000000002f200000000000000000000000000000000000000000000000000000000000002f300000000000000000000000000000000000000000000000000000000000002f400000000000000000000000000000000000000000000000000000000000002f500000000000000000000000000000000000000000000000000000000000002f600000000000000000000000000000000000000000000000000000000000002f700000000000000000000000000000000000000000000000000000000000002f800000000000000000000000000000000000000000000000000000000000002f900000000000000000000000000000000000000000000000000000000000002fa00000000000000000000000000000000000000000000000000000000000002fb00000000000000000000000000000000000000000000000000000000000002fc00000000000000000000000000000000000000000000000000000000000002fd00000000000000000000000000000000000000000000000000000000000002fe00000000000000000000000000000000000000000000000000000000000002ff3f00000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003c100000000000000000000000000000000000000000000000000000000000003c200000000000000000000000000000000000000000000000000000000000003c300000000000000000000000000000000000000000000000000000000000003c400000000000000000000000000000000000000000000000000000000000003c500000000000000000000000000000000000000000000000000000000000003c600000000000000000000000000000000000000000000000000000000000003c700000000000000000000000000000000000000000000000000000000000003c800000000000000000000000000000000000000000000000000000000000003c900000000000000000000000000000000000000000000000000000000000003ca00000000000000000000000000000000000000000000000000000000000003cb00000000000000000000000000000000000000000000000000000000000003cc00000000000000000000000000000000000000000000000000000000000003cd00000000000000000000000000000000000000000000000000000000000003ce00000000000000000000000000000000000000000000000000000000000003cf00000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003d100000000000000000000000000000000000000000000000000000000000003d200000000000000000000000000000000000000000000000000000000000003d300000000000000000000000000000000000000000000000000000000000003d400000000000000000000000000000000000000000000000000000000000003d500000000000000000000000000000000000000000000000000000000000003d600000000000000000000000000000000000000000000000000000000000003d700000000000000000000000000000000000000000000000000000000000003d800000000000000000000000000000000000000000000000000000000000003d900000000000000000000000000000000000000000000000000000000000003da00000000000000000000000000000000000000000000000000000000000003db00000000000000000000000000000000000000000000000000000000000003dc00000000000000000000000000000000000000000000000000000000000003dd00000000000000000000000000000000000000000000000000000000000003de00000000000000000000000000000000000000000000000000000000000003df00000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003e100000000000000000000000000000000000000000000000000000000000003e200000000000000000000000000000000000000000000000000000000000003e300000000000000000000000000000000000000000000000000000000000003e400000000000000000000000000000000000000000000000000000000000003e500000000000000000000000000000000000000000000000000000000000003e600000000000000000000000000000000000000000000000000000000000003e700000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e900000000000000000000000000000000000000000000000000000000000003ea00000000000000000000000000000000000000000000000000000000000003eb00000000000000000000000000000000000000000000000000000000000003ec00000000000000000000000000000000000000000000000000000000000003ed00000000000000000000000000000000000000000000000000000000000003ee00000000000000000000000000000000000000000000000000000000000003ef00000000000000000000000000000000000000000000000000000000000003f000000000000000000000000000000000000000000000000000000000000003f100000000000000000000000000000000000000000000000000000000000003f200000000000000000000000000000000000000000000000000000000000003f300000000000000000000000000000000000000000000000000000000000003f400000000000000000000000000000000000000000000000000000000000003f500000000000000000000000000000000000000000000000000000000000003f600000000000000000000000000000000000000000000000000000000000003f700000000000000000000000000000000000000000000000000000000000003f800000000000000000000000000000000000000000000000000000000000003f900000000000000000000000000000000000000000000000000000000000003fa00000000000000000000000000000000000000000000000000000000000003fb00000000000000000000000000000000000000000000000000000000000003fc00000000000000000000000000000000000000000000000000000000000003fd00000000000000000000000000000000000000000000000000000000000003fe0800bb07f923e24c36227d717557d58d32b99370b81f7a70d1835becc1114f7d7700624291eff6ab801e5668bc6619a3df132f8b392b063f09dfe8a69e38224eb200bd6b195f8716ab3dc93c44037cac579d25d0e77c2782b5aa62534ee25d36bc008c99d470d2c53624a8c5bedfeffdcc5356f6da89ee0e372b3ea3fa4f8cd652009944e00a3f9a7d2e8a535d3a210c3271d5732518037704d67ef5d42a8b82c200523014cb6eabe4366c6e599ba96534fc15ecc804a13fbaaacf8717e1b0a8d20005621252c4b36c113f21ad6c13b99e5cef514bdd98ef0aae4075b5cb5a000a00d80cb2a60aae6c3d2e7d27fb1519a708a18bb5b5085f78684bdee7512856a93f00000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000006ca00000000000000000000000000000000000000000000000000000000000006c100000000000000000000000000000000000000000000000000000000000006cb00000000000000000000000000000000000000000000000000000000000006c200000000000000000000000000000000000000000000000000000000000006cc00000000000000000000000000000000000000000000000000000000000006c300000000000000000000000000000000000000000000000000000000000006cd00000000000000000000000000000000000000000000000000000000000006c400000000000000000000000000000000000000000000000000000000000006ce00000000000000000000000000000000000000000000000000000000000006c500000000000000000000000000000000000000000000000000000000000006cf00000000000000000000000000000000000000000000000000000000000006c600000000000000000000000000000000000000000000000000000000000006d000000000000000000000000000000000000000000000000000000000000006c700000000000000000000000000000000000000000000000000000000000006d100000000000000000000000000000000000000000000000000000000000006c800000000000000000000000000000000000000000000000000000000000006d200000000000000000000000000000000000000000000000000000000000006c900000000000000000000000000000000000000000000000000000000000006d300000000000000000000000000000000000000000000000000000000000006ca00000000000000000000000000000000000000000000000000000000000006d400000000000000000000000000000000000000000000000000000000000006cb00000000000000000000000000000000000000000000000000000000000006d500000000000000000000000000000000000000000000000000000000000006cc00000000000000000000000000000000000000000000000000000000000006d600000000000000000000000000000000000000000000000000000000000006cd00000000000000000000000000000000000000000000000000000000000006d700000000000000000000000000000000000000000000000000000000000006ce00000000000000000000000000000000000000000000000000000000000006d800000000000000000000000000000000000000000000000000000000000006cf00000000000000000000000000000000000000000000000000000000000006d900000000000000000000000000000000000000000000000000000000000006d000000000000000000000000000000000000000000000000000000000000006da00000000000000000000000000000000000000000000000000000000000006d100000000000000000000000000000000000000000000000000000000000006db00000000000000000000000000000000000000000000000000000000000006d200000000000000000000000000000000000000000000000000000000000006dc00000000000000000000000000000000000000000000000000000000000006d300000000000000000000000000000000000000000000000000000000000006dd00000000000000000000000000000000000000000000000000000000000006d400000000000000000000000000000000000000000000000000000000000006de00000000000000000000000000000000000000000000000000000000000006d500000000000000000000000000000000000000000000000000000000000006df00000000000000000000000000000000000000000000000000000000000006d600000000000000000000000000000000000000000000000000000000000006e000000000000000000000000000000000000000000000000000000000000006d700000000000000000000000000000000000000000000000000000000000006e100000000000000000000000000000000000000000000000000000000000006d800000000000000000000000000000000000000000000000000000000000006e200000000000000000000000000000000000000000000000000000000000006d900000000000000000000000000000000000000000000000000000000000006e300000000000000000000000000000000000000000000000000000000000006da00000000000000000000000000000000000000000000000000000000000006e400000000000000000000000000000000000000000000000000000000000006db00000000000000000000000000000000000000000000000000000000000006e500000000000000000000000000000000000000000000000000000000000006dc00000000000000000000000000000000000000000000000000000000000006e600000000000000000000000000000000000000000000000000000000000006dd00000000000000000000000000000000000000000000000000000000000006e700000000000000000000000000000000000000000000000000000000000006de00000000000000000000000000000000000000000000000000000000000006e800000000000000000000000000000000000000000000000000000000000006df00000000000000000000000000000000000000000000000000000000000006e900000000000000000000000000000000000000000000000000000000000006e000000000000000000000000000000000000000000000000000000000000006ea00000000000000000000000000000000000000000000000000000000000006e100000000000000000000000000000000000000000000000000000000000006eb00000000000000000000000000000000000000000000000000000000000006e200000000000000000000000000000000000000000000000000000000000006ec00000000000000000000000000000000000000000000000000000000000006e300000000000000000000000000000000000000000000000000000000000006ed00000000000000000000000000000000000000000000000000000000000006e400000000000000000000000000000000000000000000000000000000000006ee00000000000000000000000000000000000000000000000000000000000006e500000000000000000000000000000000000000000000000000000000000006ef00000000000000000000000000000000000000000000000000000000000006e600000000000000000000000000000000000000000000000000000000000006f000000000000000000000000000000000000000000000000000000000000006e700000000000000000000000000000000000000000000000000000000000006f100000000000000000000000000000000000000000000000000000000000006e800000000000000000000000000000000000000000000000000000000000006f200000000000000000000000000000000000000000000000000000000000006e900000000000000000000000000000000000000000000000000000000000006f300000000000000000000000000000000000000000000000000000000000006ea00000000000000000000000000000000000000000000000000000000000006f400000000000000000000000000000000000000000000000000000000000006eb00000000000000000000000000000000000000000000000000000000000006f500000000000000000000000000000000000000000000000000000000000006ec00000000000000000000000000000000000000000000000000000000000006f600000000000000000000000000000000000000000000000000000000000006ed00000000000000000000000000000000000000000000000000000000000006f700000000000000000000000000000000000000000000000000000000000006ee00000000000000000000000000000000000000000000000000000000000006f800000000000000000000000000000000000000000000000000000000000006ef00000000000000000000000000000000000000000000000000000000000006f900000000000000000000000000000000000000000000000000000000000006f000000000000000000000000000000000000000000000000000000000000006fa00000000000000000000000000000000000000000000000000000000000006f100000000000000000000000000000000000000000000000000000000000006fb00000000000000000000000000000000000000000000000000000000000006f200000000000000000000000000000000000000000000000000000000000006fc00000000000000000000000000000000000000000000000000000000000006f300000000000000000000000000000000000000000000000000000000000006fd00000000000000000000000000000000000000000000000000000000000006f400000000000000000000000000000000000000000000000000000000000006fe00000000000000000000000000000000000000000000000000000000000006f500000000000000000000000000000000000000000000000000000000000006ff00000000000000000000000000000000000000000000000000000000000006f6000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000006f7000000000000000000000000000000000000000000000000000000000000070100000000000000000000000000000000000000000000000000000000000006f8000000000000000000000000000000000000000000000000000000000000070200000000000000000000000000000000000000000000000000000000000006f9000000000000000000000000000000000000000000000000000000000000070300000000000000000000000000000000000000000000000000000000000006fa000000000000000000000000000000000000000000000000000000000000070400000000000000000000000000000000000000000000000000000000000006fb000000000000000000000000000000000000000000000000000000000000070500000000000000000000000000000000000000000000000000000000000006fc000000000000000000000000000000000000000000000000000000000000070600000000000000000000000000000000000000000000000000000000000006fd000000000000000000000000000000000000000000000000000000000000070700000000000000000000000000000000000000000000000000000000000006fe0000000000000000000000000000000000000000000000000000000000000708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000030100000000000000000000000000000000000000000000000000000000000003020000000000000000000000000000000000000000000000000000000000000303000000000000000000000000000000000000000000000000000000000000030400000000000000000000000000000000000000000000000000000000000003050000000000000000000000000000000000000000000000000000000000000306000000000000000000000000000000000000000000000000000000000000030700000000000000000000000000000000000000000000000000000000000003080000000000000000000000000000000000000000000000000000000000000309000000000000000000000000000000000000000000000000000000000000030a000000000000000000000000000000000000000000000000000000000000030b000000000000000000000000000000000000000000000000000000000000030c000000000000000000000000000000000000000000000000000000000000030d000000000000000000000000000000000000000000000000000000000000030e000000000000000000000000000000000000000000000000000000000000030f0000000000000000000000000000000000000000000000000000000000000310000000000000000000000000000000000000000000000000000000000000031100000000000000000000000000000000000000000000000000000000000003120000000000000000000000000000000000000000000000000000000000000313000000000000000000000000000000000000000000000000000000000000031400000000000000000000000000000000000000000000000000000000000003150000000000000000000000000000000000000000000000000000000000000316000000000000000000000000000000000000000000000000000000000000031700000000000000000000000000000000000000000000000000000000000003180000000000000000000000000000000000000000000000000000000000000319000000000000000000000000000000000000000000000000000000000000031a000000000000000000000000000000000000000000000000000000000000031b000000000000000000000000000000000000000000000000000000000000031c000000000000000000000000000000000000000000000000000000000000031d000000000000000000000000000000000000000000000000000000000000031e000000000000000000000000000000000000000000000000000000000000031f0000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032100000000000000000000000000000000000000000000000000000000000003220000000000000000000000000000000000000000000000000000000000000323000000000000000000000000000000000000000000000000000000000000032400000000000000000000000000000000000000000000000000000000000003250000000000000000000000000000000000000000000000000000000000000326000000000000000000000000000000000000000000000000000000000000032700000000000000000000000000000000000000000000000000000000000003280000000000000000000000000000000000000000000000000000000000000329000000000000000000000000000000000000000000000000000000000000032a000000000000000000000000000000000000000000000000000000000000032b000000000000000000000000000000000000000000000000000000000000032c000000000000000000000000000000000000000000000000000000000000032d000000000000000000000000000000000000000000000000000000000000032e000000000000000000000000000000000000000000000000000000000000032f0000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000033100000000000000000000000000000000000000000000000000000000000003320000000000000000000000000000000000000000000000000000000000000333000000000000000000000000000000000000000000000000000000000000033400000000000000000000000000000000000000000000000000000000000003350000000000000000000000000000000000000000000000000000000000000336000000000000000000000000000000000000000000000000000000000000033700000000000000000000000000000000000000000000000000000000000003380000000000000000000000000000000000000000000000000000000000000339000000000000000000000000000000000000000000000000000000000000033a000000000000000000000000000000000000000000000000000000000000033b000000000000000000000000000000000000000000000000000000000000033c000000000000000000000000000000000000000000000000000000000000033d000000000000000000000000000000000000000000000000000000000000033e000000000000000000000000000000000000000000000000000000000000033f3f0000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040100000000000000000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000403000000000000000000000000000000000000000000000000000000000000040400000000000000000000000000000000000000000000000000000000000004050000000000000000000000000000000000000000000000000000000000000406000000000000000000000000000000000000000000000000000000000000040700000000000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000409000000000000000000000000000000000000000000000000000000000000040a000000000000000000000000000000000000000000000000000000000000040b000000000000000000000000000000000000000000000000000000000000040c000000000000000000000000000000000000000000000000000000000000040d000000000000000000000000000000000000000000000000000000000000040e000000000000000000000000000000000000000000000000000000000000040f0000000000000000000000000000000000000000000000000000000000000410000000000000000000000000000000000000000000000000000000000000041100000000000000000000000000000000000000000000000000000000000004120000000000000000000000000000000000000000000000000000000000000413000000000000000000000000000000000000000000000000000000000000041400000000000000000000000000000000000000000000000000000000000004150000000000000000000000000000000000000000000000000000000000000416000000000000000000000000000000000000000000000000000000000000041700000000000000000000000000000000000000000000000000000000000004180000000000000000000000000000000000000000000000000000000000000419000000000000000000000000000000000000000000000000000000000000041a000000000000000000000000000000000000000000000000000000000000041b000000000000000000000000000000000000000000000000000000000000041c000000000000000000000000000000000000000000000000000000000000041d000000000000000000000000000000000000000000000000000000000000041e000000000000000000000000000000000000000000000000000000000000041f0000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000042100000000000000000000000000000000000000000000000000000000000004220000000000000000000000000000000000000000000000000000000000000423000000000000000000000000000000000000000000000000000000000000042400000000000000000000000000000000000000000000000000000000000004250000000000000000000000000000000000000000000000000000000000000426000000000000000000000000000000000000000000000000000000000000042700000000000000000000000000000000000000000000000000000000000004280000000000000000000000000000000000000000000000000000000000000429000000000000000000000000000000000000000000000000000000000000042a000000000000000000000000000000000000000000000000000000000000042b000000000000000000000000000000000000000000000000000000000000042c000000000000000000000000000000000000000000000000000000000000042d000000000000000000000000000000000000000000000000000000000000042e000000000000000000000000000000000000000000000000000000000000042f0000000000000000000000000000000000000000000000000000000000000430000000000000000000000000000000000000000000000000000000000000043100000000000000000000000000000000000000000000000000000000000004320000000000000000000000000000000000000000000000000000000000000433000000000000000000000000000000000000000000000000000000000000043400000000000000000000000000000000000000000000000000000000000004350000000000000000000000000000000000000000000000000000000000000436000000000000000000000000000000000000000000000000000000000000043700000000000000000000000000000000000000000000000000000000000004380000000000000000000000000000000000000000000000000000000000000439000000000000000000000000000000000000000000000000000000000000043a000000000000000000000000000000000000000000000000000000000000043b000000000000000000000000000000000000000000000000000000000000043c000000000000000000000000000000000000000000000000000000000000043d000000000000000000000000000000000000000000000000000000000000043e0800a38df4d53fe5da3d48c97dd0f58e7a52faade40c8f50bc0f6408a2b5b3829800a3d4b994758c6630518cce3116391874b9b3b8a1bbcb36485d2702f05af359004c06e9dd14418667d7c66384699665f9222ed08be87c67293a2f1b6b4c41aa006b77b11703f0e3d21aa68c9f6d2ae8ad94ecafdb56be0c4605a0c6723e9ddc0049a087e215b640d8360f8dd98e07c77e4d2025a0dcdcaf78bc00bd6fde09680014741a52013a10622de90b1a7854f1203af5c97121a4b3774c249fba6e0dba00c783bd01c4a4cafe83fd3b4d1ce0c555be4b6d1f772cccccf86049f9af0bbe002ff3ac4d7e2f1caf1fb30e1d20eba5be2b6a42dd54690dee988a934026491e3f0000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000070a0000000000000000000000000000000000000000000000000000000000000701000000000000000000000000000000000000000000000000000000000000070b0000000000000000000000000000000000000000000000000000000000000702000000000000000000000000000000000000000000000000000000000000070c0000000000000000000000000000000000000000000000000000000000000703000000000000000000000000000000000000000000000000000000000000070d0000000000000000000000000000000000000000000000000000000000000704000000000000000000000000000000000000000000000000000000000000070e0000000000000000000000000000000000000000000000000000000000000705000000000000000000000000000000000000000000000000000000000000070f00000000000000000000000000000000000000000000000000000000000007060000000000000000000000000000000000000000000000000000000000000710000000000000000000000000000000000000000000000000000000000000070700000000000000000000000000000000000000000000000000000000000007110000000000000000000000000000000000000000000000000000000000000708000000000000000000000000000000000000000000000000000000000000071200000000000000000000000000000000000000000000000000000000000007090000000000000000000000000000000000000000000000000000000000000713000000000000000000000000000000000000000000000000000000000000070a0000000000000000000000000000000000000000000000000000000000000714000000000000000000000000000000000000000000000000000000000000070b0000000000000000000000000000000000000000000000000000000000000715000000000000000000000000000000000000000000000000000000000000070c0000000000000000000000000000000000000000000000000000000000000716000000000000000000000000000000000000000000000000000000000000070d0000000000000000000000000000000000000000000000000000000000000717000000000000000000000000000000000000000000000000000000000000070e0000000000000000000000000000000000000000000000000000000000000718000000000000000000000000000000000000000000000000000000000000070f00000000000000000000000000000000000000000000000000000000000007190000000000000000000000000000000000000000000000000000000000000710000000000000000000000000000000000000000000000000000000000000071a0000000000000000000000000000000000000000000000000000000000000711000000000000000000000000000000000000000000000000000000000000071b0000000000000000000000000000000000000000000000000000000000000712000000000000000000000000000000000000000000000000000000000000071c0000000000000000000000000000000000000000000000000000000000000713000000000000000000000000000000000000000000000000000000000000071d0000000000000000000000000000000000000000000000000000000000000714000000000000000000000000000000000000000000000000000000000000071e0000000000000000000000000000000000000000000000000000000000000715000000000000000000000000000000000000000000000000000000000000071f00000000000000000000000000000000000000000000000000000000000007160000000000000000000000000000000000000000000000000000000000000720000000000000000000000000000000000000000000000000000000000000071700000000000000000000000000000000000000000000000000000000000007210000000000000000000000000000000000000000000000000000000000000718000000000000000000000000000000000000000000000000000000000000072200000000000000000000000000000000000000000000000000000000000007190000000000000000000000000000000000000000000000000000000000000723000000000000000000000000000000000000000000000000000000000000071a0000000000000000000000000000000000000000000000000000000000000724000000000000000000000000000000000000000000000000000000000000071b0000000000000000000000000000000000000000000000000000000000000725000000000000000000000000000000000000000000000000000000000000071c0000000000000000000000000000000000000000000000000000000000000726000000000000000000000000000000000000000000000000000000000000071d0000000000000000000000000000000000000000000000000000000000000727000000000000000000000000000000000000000000000000000000000000071e0000000000000000000000000000000000000000000000000000000000000728000000000000000000000000000000000000000000000000000000000000071f00000000000000000000000000000000000000000000000000000000000007290000000000000000000000000000000000000000000000000000000000000720000000000000000000000000000000000000000000000000000000000000072a0000000000000000000000000000000000000000000000000000000000000721000000000000000000000000000000000000000000000000000000000000072b0000000000000000000000000000000000000000000000000000000000000722000000000000000000000000000000000000000000000000000000000000072c0000000000000000000000000000000000000000000000000000000000000723000000000000000000000000000000000000000000000000000000000000072d0000000000000000000000000000000000000000000000000000000000000724000000000000000000000000000000000000000000000000000000000000072e0000000000000000000000000000000000000000000000000000000000000725000000000000000000000000000000000000000000000000000000000000072f00000000000000000000000000000000000000000000000000000000000007260000000000000000000000000000000000000000000000000000000000000730000000000000000000000000000000000000000000000000000000000000072700000000000000000000000000000000000000000000000000000000000007310000000000000000000000000000000000000000000000000000000000000728000000000000000000000000000000000000000000000000000000000000073200000000000000000000000000000000000000000000000000000000000007290000000000000000000000000000000000000000000000000000000000000733000000000000000000000000000000000000000000000000000000000000072a0000000000000000000000000000000000000000000000000000000000000734000000000000000000000000000000000000000000000000000000000000072b0000000000000000000000000000000000000000000000000000000000000735000000000000000000000000000000000000000000000000000000000000072c0000000000000000000000000000000000000000000000000000000000000736000000000000000000000000000000000000000000000000000000000000072d0000000000000000000000000000000000000000000000000000000000000737000000000000000000000000000000000000000000000000000000000000072e0000000000000000000000000000000000000000000000000000000000000738000000000000000000000000000000000000000000000000000000000000072f00000000000000000000000000000000000000000000000000000000000007390000000000000000000000000000000000000000000000000000000000000730000000000000000000000000000000000000000000000000000000000000073a0000000000000000000000000000000000000000000000000000000000000731000000000000000000000000000000000000000000000000000000000000073b0000000000000000000000000000000000000000000000000000000000000732000000000000000000000000000000000000000000000000000000000000073c0000000000000000000000000000000000000000000000000000000000000733000000000000000000000000000000000000000000000000000000000000073d0000000000000000000000000000000000000000000000000000000000000734000000000000000000000000000000000000000000000000000000000000073e0000000000000000000000000000000000000000000000000000000000000735000000000000000000000000000000000000000000000000000000000000073f00000000000000000000000000000000000000000000000000000000000007360000000000000000000000000000000000000000000000000000000000000740000000000000000000000000000000000000000000000000000000000000073700000000000000000000000000000000000000000000000000000000000007410000000000000000000000000000000000000000000000000000000000000738000000000000000000000000000000000000000000000000000000000000074200000000000000000000000000000000000000000000000000000000000007390000000000000000000000000000000000000000000000000000000000000743000000000000000000000000000000000000000000000000000000000000073a0000000000000000000000000000000000000000000000000000000000000744000000000000000000000000000000000000000000000000000000000000073b0000000000000000000000000000000000000000000000000000000000000745000000000000000000000000000000000000000000000000000000000000073c0000000000000000000000000000000000000000000000000000000000000746000000000000000000000000000000000000000000000000000000000000073d0000000000000000000000000000000000000000000000000000000000000747000000000000000000000000000000000000000000000000000000000000073e0000000000000000000000000000000000000000000000000000000000000748000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "txsEffectsHash": "0x00e73bbc5444fa184c4c8bdd7f3ae7e3060c0b9a1a0ad96fe7472a7854786778", "decodedHeader": { "contentCommitment": { - "inHash": "0x00212ff46db74e06c26240f9a92fb6fea84709380935d657361bbd5bcb891937", + "inHash": "0x00e1371045bd7d2c3e1f19cba5f536f0e82042ba4bc257d4ba19c146215e8242", "outHash": "0x00b581181fdd29a9e20363313973f1545a94d0157e542d9b116ff7ae3f58a428", "numTxs": 8, "txsEffectsHash": "0x00e73bbc5444fa184c4c8bdd7f3ae7e3060c0b9a1a0ad96fe7472a7854786778" }, "globalVariables": { "blockNumber": 2, - "slotNumber": "0x0000000000000000000000000000000000000000000000000000000000000022", + "slotNumber": "0x0000000000000000000000000000000000000000000000000000000000000023", "chainId": 31337, - "timestamp": 1728562650, + "timestamp": 1730234016, "version": 1, - "coinbase": "0xb1e072764484ad800bc7d95ebdb84ea57ec1d5d3", - "feeRecipient": "0x27f79e3e1124a5869838e688cdf225f421845f3b734541c251652fdeb0f74917", + "coinbase": "0x4b727c2ee4030668748766f0695aebffca7eed1a", + "feeRecipient": "0x17e8c896e28967f5b43e53f229edf504b61881c02885750af6b75a3b5cc9d87c", "gasFees": { "feePerDaGas": 0, "feePerL2Gas": 0 @@ -116,12 +116,12 @@ }, "lastArchive": { "nextAvailableLeafIndex": 2, - "root": "0x24c11def12fd4dbe0974daf2b642f44404b96f4bfac4ab9c990c911e7bd96eaa" + "root": "0x12e65b031a1821b8ed8d2934a32482f660c0fb9d10557f476efb616c0651c75b" }, "stateReference": { "l1ToL2MessageTree": { "nextAvailableLeafIndex": 32, - "root": "0x224c43ed89fb9404e06e7382170d1e279a53211bab61876f38d8a4180390b7ad" + "root": "0x2cd1079160767e99ff3e43a4e56a0b79c7d28239245ac3240935dfb98a4eee29" }, "partialStateReference": { "noteHashTree": { @@ -139,8 +139,8 @@ } } }, - "header": "0x24c11def12fd4dbe0974daf2b642f44404b96f4bfac4ab9c990c911e7bd96eaa00000002000000000000000000000000000000000000000000000000000000000000000800e73bbc5444fa184c4c8bdd7f3ae7e3060c0b9a1a0ad96fe7472a785478677800212ff46db74e06c26240f9a92fb6fea84709380935d657361bbd5bcb89193700b581181fdd29a9e20363313973f1545a94d0157e542d9b116ff7ae3f58a428224c43ed89fb9404e06e7382170d1e279a53211bab61876f38d8a4180390b7ad0000002017752a4346cf34b18277458ace73be4895316cb1c3cbce628d573d5d10cde7ce00000200152db065a479b5630768d6c5250bb6233e71729f857c16cffa98569acf90a2bf000002800a020b31737a919cbd6b0c0fe25d466a11e2186eb8038cd63a5e7d2900473d53000002800000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000006707c5dab1e072764484ad800bc7d95ebdb84ea57ec1d5d327f79e3e1124a5869838e688cdf225f421845f3b734541c251652fdeb0f74917000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "publicInputsHash": "0x00e844cf75bddf47717e37193790124c73cba7b5c35d80841aacaad877ef579d", + "header": "0x12e65b031a1821b8ed8d2934a32482f660c0fb9d10557f476efb616c0651c75b00000002000000000000000000000000000000000000000000000000000000000000000800e73bbc5444fa184c4c8bdd7f3ae7e3060c0b9a1a0ad96fe7472a785478677800e1371045bd7d2c3e1f19cba5f536f0e82042ba4bc257d4ba19c146215e824200b581181fdd29a9e20363313973f1545a94d0157e542d9b116ff7ae3f58a4282cd1079160767e99ff3e43a4e56a0b79c7d28239245ac3240935dfb98a4eee290000002017752a4346cf34b18277458ace73be4895316cb1c3cbce628d573d5d10cde7ce00000200152db065a479b5630768d6c5250bb6233e71729f857c16cffa98569acf90a2bf000002800a020b31737a919cbd6b0c0fe25d466a11e2186eb8038cd63a5e7d2900473d53000002800000000000000000000000000000000000000000000000000000000000007a6900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002300000000000000000000000000000000000000000000000000000000672146a04b727c2ee4030668748766f0695aebffca7eed1a17e8c896e28967f5b43e53f229edf504b61881c02885750af6b75a3b5cc9d87c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "publicInputsHash": "0x009c999658e0df4745b03a4e5e099524ad8709803c0e49e233ecccf2b1c7daa0", "numTxs": 8 } } \ No newline at end of file diff --git a/l1-contracts/test/harnesses/InboxHarness.sol b/l1-contracts/test/harnesses/InboxHarness.sol index 420960467c2..f88f266270d 100644 --- a/l1-contracts/test/harnesses/InboxHarness.sol +++ b/l1-contracts/test/harnesses/InboxHarness.sol @@ -36,4 +36,10 @@ contract InboxHarness is Inbox { // -INITIAL_L2_BLOCK_NUM because tree number INITIAL_L2_BLOCK_NUM is not real return inProgress - Constants.INITIAL_L2_BLOCK_NUM; } + + function getNextMessageIndex() external view returns (uint256) { + FrontierLib.Tree storage currentTree = trees[inProgress]; + uint256 index = (inProgress - Constants.INITIAL_L2_BLOCK_NUM) * SIZE + currentTree.nextIndex; + return index; + } } diff --git a/l1-contracts/test/portals/TokenPortal.sol b/l1-contracts/test/portals/TokenPortal.sol index b6d0a503e14..674b007ce95 100644 --- a/l1-contracts/test/portals/TokenPortal.sol +++ b/l1-contracts/test/portals/TokenPortal.sol @@ -17,6 +17,18 @@ import {Hash} from "@aztec/core/libraries/crypto/Hash.sol"; contract TokenPortal { using SafeERC20 for IERC20; + event DepositToAztecPublic( + bytes32 to, uint256 amount, bytes32 secretHash, bytes32 key, uint256 index + ); + + event DepositToAztecPrivate( + bytes32 secretHashForRedeemingMintedNotes, + uint256 amount, + bytes32 secretHashForL2MessageConsumption, + bytes32 key, + uint256 index + ); + IRegistry public registry; IERC20 public underlying; bytes32 public l2Bridge; @@ -34,11 +46,11 @@ contract TokenPortal { * @param _to - The aztec address of the recipient * @param _amount - The amount to deposit * @param _secretHash - The hash of the secret consumable message. The hash should be 254 bits (so it can fit in a Field element) - * @return The key of the entry in the Inbox + * @return The key of the entry in the Inbox and its leaf index */ function depositToAztecPublic(bytes32 _to, uint256 _amount, bytes32 _secretHash) external - returns (bytes32) + returns (bytes32, uint256) { // Preamble IInbox inbox = IRollup(registry.getRollup()).INBOX(); @@ -52,7 +64,12 @@ contract TokenPortal { underlying.safeTransferFrom(msg.sender, address(this), _amount); // Send message to rollup - return inbox.sendL2Message(actor, contentHash, _secretHash); + (bytes32 key, uint256 index) = inbox.sendL2Message(actor, contentHash, _secretHash); + + // Emit event + emit DepositToAztecPublic(_to, _amount, _secretHash, key, index); + + return (key, index); } // docs:end:deposit_public @@ -62,13 +79,13 @@ contract TokenPortal { * @param _secretHashForRedeemingMintedNotes - The hash of the secret to redeem minted notes privately on Aztec. The hash should be 254 bits (so it can fit in a Field element) * @param _amount - The amount to deposit * @param _secretHashForL2MessageConsumption - The hash of the secret consumable L1 to L2 message. The hash should be 254 bits (so it can fit in a Field element) - * @return The key of the entry in the Inbox + * @return The key of the entry in the Inbox and its leaf index */ function depositToAztecPrivate( bytes32 _secretHashForRedeemingMintedNotes, uint256 _amount, bytes32 _secretHashForL2MessageConsumption - ) external returns (bytes32) { + ) external returns (bytes32, uint256) { // Preamble IInbox inbox = IRollup(registry.getRollup()).INBOX(); DataStructures.L2Actor memory actor = DataStructures.L2Actor(l2Bridge, 1); @@ -84,7 +101,15 @@ contract TokenPortal { underlying.safeTransferFrom(msg.sender, address(this), _amount); // Send message to rollup - return inbox.sendL2Message(actor, contentHash, _secretHashForL2MessageConsumption); + (bytes32 key, uint256 index) = + inbox.sendL2Message(actor, contentHash, _secretHashForL2MessageConsumption); + + // Emit event + emit DepositToAztecPrivate( + _secretHashForRedeemingMintedNotes, _amount, _secretHashForL2MessageConsumption, key, index + ); + + return (key, index); } // docs:end:deposit_private diff --git a/l1-contracts/test/portals/TokenPortal.t.sol b/l1-contracts/test/portals/TokenPortal.t.sol index 60672b8e081..d462f0e16aa 100644 --- a/l1-contracts/test/portals/TokenPortal.t.sol +++ b/l1-contracts/test/portals/TokenPortal.t.sol @@ -81,7 +81,7 @@ contract TokenPortalTest is Test { vm.deal(address(this), 100 ether); } - function _createExpectedMintPrivateL1ToL2Message() + function _createExpectedMintPrivateL1ToL2Message(uint256 _index) internal view returns (DataStructures.L1ToL2Msg memory) @@ -94,11 +94,12 @@ contract TokenPortalTest is Test { "mint_private(bytes32,uint256)", secretHashForRedeemingMintedNotes, amount ) ), - secretHash: secretHashForL2MessageConsumption + secretHash: secretHashForL2MessageConsumption, + index: _index }); } - function _createExpectedMintPublicL1ToL2Message() + function _createExpectedMintPublicL1ToL2Message(uint256 _index) internal view returns (DataStructures.L1ToL2Msg memory) @@ -107,7 +108,8 @@ contract TokenPortalTest is Test { sender: DataStructures.L1Actor(address(tokenPortal), block.chainid), recipient: DataStructures.L2Actor(l2TokenAddress, 1), content: Hash.sha256ToField(abi.encodeWithSignature("mint_public(bytes32,uint256)", to, amount)), - secretHash: secretHashForL2MessageConsumption + secretHash: secretHashForL2MessageConsumption, + index: _index }); } @@ -117,23 +119,25 @@ contract TokenPortalTest is Test { testERC20.approve(address(tokenPortal), mintAmount); // Check for the expected message - DataStructures.L1ToL2Msg memory expectedMessage = _createExpectedMintPrivateL1ToL2Message(); + uint256 expectedIndex = (FIRST_REAL_TREE_NUM - 1) * L1_TO_L2_MSG_SUBTREE_SIZE; + DataStructures.L1ToL2Msg memory expectedMessage = + _createExpectedMintPrivateL1ToL2Message(expectedIndex); bytes32 expectedLeaf = expectedMessage.sha256ToField(); // Check the event was emitted vm.expectEmit(true, true, true, true); // event we expect - uint256 globalLeafIndex = (FIRST_REAL_TREE_NUM - 1) * L1_TO_L2_MSG_SUBTREE_SIZE; - emit IInbox.MessageSent(FIRST_REAL_TREE_NUM, globalLeafIndex, expectedLeaf); + emit IInbox.MessageSent(FIRST_REAL_TREE_NUM, expectedIndex, expectedLeaf); // event we will get // Perform op - bytes32 leaf = tokenPortal.depositToAztecPrivate( + (bytes32 leaf, uint256 index) = tokenPortal.depositToAztecPrivate( secretHashForRedeemingMintedNotes, amount, secretHashForL2MessageConsumption ); assertEq(leaf, expectedLeaf, "returned leaf and calculated leaf should match"); + assertEq(index, expectedIndex, "returned index and calculated index should match"); return leaf; } @@ -144,19 +148,22 @@ contract TokenPortalTest is Test { testERC20.approve(address(tokenPortal), mintAmount); // Check for the expected message - DataStructures.L1ToL2Msg memory expectedMessage = _createExpectedMintPublicL1ToL2Message(); + uint256 expectedIndex = (FIRST_REAL_TREE_NUM - 1) * L1_TO_L2_MSG_SUBTREE_SIZE; + DataStructures.L1ToL2Msg memory expectedMessage = + _createExpectedMintPublicL1ToL2Message(expectedIndex); bytes32 expectedLeaf = expectedMessage.sha256ToField(); // Check the event was emitted vm.expectEmit(true, true, true, true); // event we expect - uint256 globalLeafIndex = (FIRST_REAL_TREE_NUM - 1) * L1_TO_L2_MSG_SUBTREE_SIZE; - emit IInbox.MessageSent(FIRST_REAL_TREE_NUM, globalLeafIndex, expectedLeaf); + emit IInbox.MessageSent(FIRST_REAL_TREE_NUM, expectedIndex, expectedLeaf); // Perform op - bytes32 leaf = tokenPortal.depositToAztecPublic(to, amount, secretHashForL2MessageConsumption); + (bytes32 leaf, uint256 index) = + tokenPortal.depositToAztecPublic(to, amount, secretHashForL2MessageConsumption); assertEq(leaf, expectedLeaf, "returned leaf and calculated leaf should match"); + assertEq(index, expectedIndex, "returned index and calculated index should match"); return leaf; } diff --git a/l1-contracts/test/portals/UniswapPortal.sol b/l1-contracts/test/portals/UniswapPortal.sol index 9d8688b255e..fc6bebd74a4 100644 --- a/l1-contracts/test/portals/UniswapPortal.sol +++ b/l1-contracts/test/portals/UniswapPortal.sol @@ -67,7 +67,7 @@ contract UniswapPortal { bool _withCaller, // Avoiding stack too deep PortalDataStructures.OutboxMessageMetadata[2] calldata _outboxMessageMetadata - ) public returns (bytes32) { + ) public returns (bytes32, uint256) { LocalSwapVars memory vars; vars.inputAsset = TokenPortal(_inputTokenPortal).underlying(); @@ -174,7 +174,7 @@ contract UniswapPortal { bool _withCaller, // Avoiding stack too deep PortalDataStructures.OutboxMessageMetadata[2] calldata _outboxMessageMetadata - ) public returns (bytes32) { + ) public returns (bytes32, uint256) { LocalSwapVars memory vars; vars.inputAsset = TokenPortal(_inputTokenPortal).underlying(); diff --git a/noir-projects/aztec-nr/.gitrepo b/noir-projects/aztec-nr/.gitrepo index 98e5f67280c..bd0aaf5b715 100644 --- a/noir-projects/aztec-nr/.gitrepo +++ b/noir-projects/aztec-nr/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/AztecProtocol/aztec-nr branch = master - commit = cf243a618874253bffea679ad13507809b807e69 + commit = 2562cf7ac21eaf8c59fb8d6ee010b49d6ca7ac53 method = merge cmdver = 0.4.6 - parent = 7a33b40716ab9bef91c5f6a5c27cec32e4b461d1 + parent = c6889c8ae0895d093222a70fadb2c1d38be8c42b diff --git a/noir-projects/aztec-nr/README.md b/noir-projects/aztec-nr/README.md index 3a944619c61..21e009bcc8c 100644 --- a/noir-projects/aztec-nr/README.md +++ b/noir-projects/aztec-nr/README.md @@ -1,5 +1,5 @@
- +

Aztec.nr

diff --git a/noir-projects/aztec-nr/address-note/src/address_note.nr b/noir-projects/aztec-nr/address-note/src/address_note.nr index c3e5f3bc4f8..bdaf172a402 100644 --- a/noir-projects/aztec-nr/address-note/src/address_note.nr +++ b/noir-projects/aztec-nr/address-note/src/address_note.nr @@ -20,7 +20,6 @@ use dep::aztec::{ #[derive(Serialize)] pub struct AddressNote { address: AztecAddress, - // The nullifying public key hash is used with the nsk_app to ensure that the note can be privately spent. owner: AztecAddress, randomness: Field, } diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index 1465b61e8c6..b637afe6464 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -276,7 +276,13 @@ impl PrivateContext { // docs:start:context_consume_l1_to_l2_message // docs:start:consume_l1_to_l2_message - pub fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress) { + pub fn consume_l1_to_l2_message( + &mut self, + content: Field, + secret: Field, + sender: EthAddress, + leaf_index: Field, + ) { // docs:end:context_consume_l1_to_l2_message let nullifier = process_l1_to_l2_message( self.historical_header.state.l1_to_l2_message_tree.root, @@ -286,6 +292,7 @@ impl PrivateContext { self.version(), content, secret, + leaf_index, ); // Push nullifier (and the "commitment" corresponding to this can be "empty") diff --git a/noir-projects/aztec-nr/aztec/src/context/public_context.nr b/noir-projects/aztec-nr/aztec/src/context/public_context.nr index e7b55e86299..9730495b753 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -1,5 +1,7 @@ use crate::context::gas::GasOpts; -use crate::hash::{compute_message_hash, compute_message_nullifier, compute_secret_hash}; +use crate::hash::{ + compute_l1_to_l2_message_hash, compute_l1_to_l2_message_nullifier, compute_secret_hash, +}; use dep::protocol_types::abis::function_selector::FunctionSelector; use dep::protocol_types::address::{AztecAddress, EthAddress}; use dep::protocol_types::constants::{MAX_FIELD_VALUE, PUBLIC_DISPATCH_SELECTOR}; @@ -42,7 +44,7 @@ impl PublicContext { leaf_index: Field, ) { let secret_hash = compute_secret_hash(secret); - let message_hash = compute_message_hash( + let message_hash = compute_l1_to_l2_message_hash( sender, self.chain_id(), /*recipient=*/ @@ -50,8 +52,9 @@ impl PublicContext { self.version(), content, secret_hash, + leaf_index, ); - let nullifier = compute_message_nullifier(message_hash, secret, leaf_index); + let nullifier = compute_l1_to_l2_message_nullifier(message_hash, secret); assert( !self.nullifier_exists(nullifier, self.this_address()), diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_event_emission.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_event_emission.nr index 1ebb4b5e195..c8e2bfe6ebe 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_event_emission.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_event_emission.nr @@ -2,7 +2,10 @@ use crate::{ context::PrivateContext, encrypted_logs::payload::compute_private_log_payload, event::event_interface::EventInterface, keys::getters::get_ovsk_app, oracle::random::random, }; -use dep::protocol_types::{address::AztecAddress, hash::sha256_to_field, public_keys::OvpkM}; +use dep::protocol_types::{ + address::AztecAddress, constants::PRIVATE_LOG_SIZE_IN_BYTES, hash::sha256_to_field, + public_keys::OvpkM, +}; /// Computes private event log payload and a log hash fn compute_payload_and_hash( @@ -12,21 +15,21 @@ fn compute_payload_and_hash( ovsk_app: Field, ovpk: OvpkM, recipient: AztecAddress, -) -> ([u8; 416 + N * 32], Field) + sender: AztecAddress, +) -> ([u8; PRIVATE_LOG_SIZE_IN_BYTES], Field) where Event: EventInterface, { let contract_address: AztecAddress = context.this_address(); let plaintext = event.private_to_be_bytes(randomness); - // For event logs we never include public values prefix as there are never any public values - let encrypted_log: [u8; 416 + N * 32] = compute_private_log_payload( + let encrypted_log = compute_private_log_payload( contract_address, ovsk_app, ovpk, recipient, + sender, plaintext, - false, ); let log_hash = sha256_to_field(encrypted_log); (encrypted_log, log_hash) @@ -38,19 +41,29 @@ unconstrained fn compute_payload_and_hash_unconstrained( randomness: Field, ovpk: OvpkM, recipient: AztecAddress, -) -> ([u8; 416 + N * 32], Field) + sender: AztecAddress, +) -> ([u8; PRIVATE_LOG_SIZE_IN_BYTES], Field) where Event: EventInterface, { let ovsk_app = get_ovsk_app(ovpk.hash()); - compute_payload_and_hash(context, event, randomness, ovsk_app, ovpk, recipient) + compute_payload_and_hash( + context, + event, + randomness, + ovsk_app, + ovpk, + recipient, + sender, + ) } pub fn encode_and_encrypt_event( context: &mut PrivateContext, ovpk: OvpkM, recipient: AztecAddress, -) -> fn[(&mut PrivateContext, OvpkM, AztecAddress)](Event) -> () + sender: AztecAddress, +) -> fn[(&mut PrivateContext, OvpkM, AztecAddress, AztecAddress)](Event) -> () where Event: EventInterface, { @@ -62,7 +75,7 @@ where let randomness = unsafe { random() }; let ovsk_app: Field = context.request_ovsk_app(ovpk.hash()); let (encrypted_log, log_hash) = - compute_payload_and_hash(*context, e, randomness, ovsk_app, ovpk, recipient); + compute_payload_and_hash(*context, e, randomness, ovsk_app, ovpk, recipient, sender); context.emit_raw_event_log_with_masked_address(randomness, encrypted_log, log_hash); } } @@ -71,7 +84,8 @@ pub fn encode_and_encrypt_event_unconstrained( context: &mut PrivateContext, ovpk: OvpkM, recipient: AztecAddress, -) -> fn[(&mut PrivateContext, OvpkM, AztecAddress)](Event) -> () + sender: AztecAddress, +) -> fn[(&mut PrivateContext, OvpkM, AztecAddress, AztecAddress)](Event) -> () where Event: EventInterface, { @@ -82,7 +96,7 @@ where // value generation. let randomness = unsafe { random() }; let (encrypted_log, log_hash) = unsafe { - compute_payload_and_hash_unconstrained(*context, e, randomness, ovpk, recipient) + compute_payload_and_hash_unconstrained(*context, e, randomness, ovpk, recipient, sender) }; context.emit_raw_event_log_with_masked_address(randomness, encrypted_log, log_hash); } @@ -96,14 +110,15 @@ pub fn encode_and_encrypt_event_with_randomness( randomness: Field, ovpk: OvpkM, recipient: AztecAddress, -) -> fn[(&mut PrivateContext, OvpkM, Field, AztecAddress)](Event) -> () + sender: AztecAddress, +) -> fn[(&mut PrivateContext, OvpkM, Field, AztecAddress, AztecAddress)](Event) -> () where Event: EventInterface, { |e: Event| { let ovsk_app: Field = context.request_ovsk_app(ovpk.hash()); let (encrypted_log, log_hash) = - compute_payload_and_hash(*context, e, randomness, ovsk_app, ovpk, recipient); + compute_payload_and_hash(*context, e, randomness, ovsk_app, ovpk, recipient, sender); context.emit_raw_event_log_with_masked_address(randomness, encrypted_log, log_hash); } } @@ -113,7 +128,8 @@ pub fn encode_and_encrypt_event_with_randomness_unconstrained randomness: Field, ovpk: OvpkM, recipient: AztecAddress, -) -> fn[(&mut PrivateContext, Field, OvpkM, AztecAddress)](Event) -> () + sender: AztecAddress, +) -> fn[(&mut PrivateContext, Field, OvpkM, AztecAddress, AztecAddress)](Event) -> () where Event: EventInterface, { @@ -133,7 +149,7 @@ where // return the log from this function to the app, otherwise it could try to do stuff with it and then that might // be wrong. let (encrypted_log, log_hash) = unsafe { - compute_payload_and_hash_unconstrained(*context, e, randomness, ovpk, recipient) + compute_payload_and_hash_unconstrained(*context, e, randomness, ovpk, recipient, sender) }; context.emit_raw_event_log_with_masked_address(randomness, encrypted_log, log_hash); } diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr index 44f3791616b..2b7f58b41bb 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr @@ -5,7 +5,8 @@ use crate::{ note::{note_emission::NoteEmission, note_interface::NoteInterface}, }; use dep::protocol_types::{ - abis::note_hash::NoteHash, address::AztecAddress, hash::sha256_to_field, public_keys::OvpkM, + abis::note_hash::NoteHash, address::AztecAddress, constants::PRIVATE_LOG_SIZE_IN_BYTES, + hash::sha256_to_field, public_keys::OvpkM, }; /// Computes private note log payload and a log hash @@ -15,7 +16,8 @@ fn compute_payload_and_hash( ovsk_app: Field, ovpk: OvpkM, recipient: AztecAddress, -) -> (u32, [u8; 417 + N * 32], Field) + sender: AztecAddress, +) -> (u32, [u8; PRIVATE_LOG_SIZE_IN_BYTES], Field) where Note: NoteInterface, { @@ -31,9 +33,14 @@ where let plaintext = note.to_be_bytes(storage_slot); - // For note logs we always include public values prefix - let encrypted_log: [u8; 417 + N * 32] = - compute_private_log_payload(contract_address, ovsk_app, ovpk, recipient, plaintext, true); + let encrypted_log = compute_private_log_payload( + contract_address, + ovsk_app, + ovpk, + recipient, + sender, + plaintext, + ); let log_hash = sha256_to_field(encrypted_log); (note_hash_counter, encrypted_log, log_hash) @@ -44,12 +51,13 @@ unconstrained fn compute_payload_and_hash_unconstrained( note: Note, ovpk: OvpkM, recipient: AztecAddress, -) -> (u32, [u8; 417 + N * 32], Field) + sender: AztecAddress, +) -> (u32, [u8; PRIVATE_LOG_SIZE_IN_BYTES], Field) where Note: NoteInterface, { let ovsk_app = get_ovsk_app(ovpk.hash()); - compute_payload_and_hash(context, note, ovsk_app, ovpk, recipient) + compute_payload_and_hash(context, note, ovsk_app, ovpk, recipient, sender) } // This function seems to be affected by the following Noir bug: @@ -59,7 +67,9 @@ pub fn encode_and_encrypt_note( context: &mut PrivateContext, ovpk: OvpkM, recipient: AztecAddress, -) -> fn[(&mut PrivateContext, OvpkM, AztecAddress)](NoteEmission) -> () + // TODO: We need this because to compute a tagging secret, we require a sender. Should we have the tagging secret oracle take a ovpk_m as input instead of the address? + sender: AztecAddress, +) -> fn[(&mut PrivateContext, OvpkM, AztecAddress, AztecAddress)](NoteEmission) -> () where Note: NoteInterface, { @@ -67,7 +77,7 @@ where let ovsk_app: Field = context.request_ovsk_app(ovpk.hash()); let (note_hash_counter, encrypted_log, log_hash) = - compute_payload_and_hash(*context, e.note, ovsk_app, ovpk, recipient); + compute_payload_and_hash(*context, e.note, ovsk_app, ovpk, recipient, sender); context.emit_raw_note_log(note_hash_counter, encrypted_log, log_hash); } } @@ -76,7 +86,9 @@ pub fn encode_and_encrypt_note_unconstrained( context: &mut PrivateContext, ovpk: OvpkM, recipient: AztecAddress, -) -> fn[(&mut PrivateContext, OvpkM, AztecAddress)](NoteEmission) -> () + // TODO: We need this because to compute a tagging secret, we require a sender. Should we have the tagging secret oracle take a ovpk_m as input instead of the address? + sender: AztecAddress, +) -> fn[(&mut PrivateContext, OvpkM, AztecAddress, AztecAddress)](NoteEmission) -> () where Note: NoteInterface, { @@ -100,8 +112,9 @@ where // for the log to be deleted when it shouldn't have (which is fine - they can already make the content be // whatever), or cause for the log to not be deleted when it should have (which is also fine - it'll be a log // for a note that doesn't exist). - let (note_hash_counter, encrypted_log, log_hash) = - unsafe { compute_payload_and_hash_unconstrained(*context, e.note, ovpk, recipient) }; + let (note_hash_counter, encrypted_log, log_hash) = unsafe { + compute_payload_and_hash_unconstrained(*context, e.note, ovpk, recipient, sender) + }; context.emit_raw_note_log(note_hash_counter, encrypted_log, log_hash); } } diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr index fc2b0c34507..c57a0e34f5d 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr @@ -1,6 +1,10 @@ use dep::protocol_types::{ - address::AztecAddress, constants::GENERATOR_INDEX__SYMMETRIC_KEY, - hash::poseidon2_hash_with_separator, point::Point, public_keys::OvpkM, scalar::Scalar, + address::AztecAddress, + constants::{GENERATOR_INDEX__SYMMETRIC_KEY, PRIVATE_LOG_SIZE_IN_BYTES}, + hash::{poseidon2_hash, poseidon2_hash_with_separator}, + point::Point, + public_keys::{AddressPoint, OvpkM}, + scalar::Scalar, }; use std::{ aes128::aes128_encrypt, embedded_curve_ops::fixed_base_scalar_mul as derive_public_key, @@ -9,18 +13,102 @@ use std::{ use crate::{ encrypted_logs::header::EncryptedLogHeader, - keys::point_to_symmetric_key::point_to_symmetric_key, oracle::random::random, + keys::point_to_symmetric_key::point_to_symmetric_key, + oracle::{notes::{get_app_tagging_secret, increment_app_tagging_secret}, random::random}, utils::point::point_to_bytes, }; -use protocol_types::public_keys::AddressPoint; -fn compute_private_log_payload( +pub comptime global PRIVATE_LOG_OVERHEAD_IN_BYTES: u32 = 304; + +// 1 byte for storage slot, 1 byte for note type id, allowing 6 bytes for custom note fields. +global MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES: u32 = 8 * 32; + +global MAX_PRIVATE_EVENT_LOG_PLAINTEXT_SIZE_IN_BYTES: u32 = + MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES - 32; // Reserve 1 field for address tag. + +// PRIVATE_LOG_SIZE_IN_BYTES +// - PRIVATE_LOG_OVERHEAD_IN_BYTES, consisting of: +// - 32 bytes for incoming_tag +// - 32 bytes for eph_pk +// - 48 bytes for incoming_header +// - 48 bytes for outgoing_header +// - 144 bytes for outgoing_body +// - 16 + MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES for incoming_body, consisting of: +// - 1 byte for plaintext length +// - MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES for the actual plaintext and padded random values +// - 15 bytes for AES padding + +// Note: Update PRIVATE_LOG_SIZE_IN_BYTES in `constants.nr` if any of the above fields change. +// This value ideally should be set by the protocol, allowing users (or `aztec-nr`) to fit data within the defined size limits. +// Currently, we adjust this value as the structure changes, then update `constants.nr` to match. +// Once the structure is finalized with defined overhead and max note field sizes, this value will be fixed and should remain unaffected by further payload composition changes. + +pub fn compute_private_log_payload( + contract_address: AztecAddress, + ovsk_app: Field, + ovpk: OvpkM, + recipient: AztecAddress, + sender: AztecAddress, + plaintext: [u8; P], +) -> [u8; PRIVATE_LOG_SIZE_IN_BYTES] { + let extended_plaintext: [u8; MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES + 1] = + extend_private_log_plaintext(plaintext); + compute_encrypted_log( + contract_address, + ovsk_app, + ovpk, + recipient, + sender, + extended_plaintext, + ) +} + +pub fn compute_event_log_payload( + contract_address: AztecAddress, + ovsk_app: Field, + ovpk: OvpkM, + recipient: AztecAddress, + sender: AztecAddress, + plaintext: [u8; P], +) -> [u8; PRIVATE_LOG_SIZE_IN_BYTES] { + let extended_plaintext: [u8; MAX_PRIVATE_EVENT_LOG_PLAINTEXT_SIZE_IN_BYTES + 1] = + extend_private_log_plaintext(plaintext); + compute_encrypted_log( + contract_address, + ovsk_app, + ovpk, + recipient, + sender, + extended_plaintext, + ) +} + +pub fn compute_partial_public_log_payload( + contract_address: AztecAddress, + ovsk_app: Field, + ovpk: OvpkM, + recipient: AztecAddress, + sender: AztecAddress, + plaintext: [u8; P], +) -> [u8; M] { + let extended_plaintext: [u8; P + 1] = extend_private_log_plaintext(plaintext); + compute_encrypted_log( + contract_address, + ovsk_app, + ovpk, + recipient, + sender, + extended_plaintext, + ) +} + +fn compute_encrypted_log( contract_address: AztecAddress, ovsk_app: Field, ovpk: OvpkM, recipient: AztecAddress, + sender: AztecAddress, plaintext: [u8; P], - include_public_values_prefix: bool, ) -> [u8; M] { let (eph_sk, eph_pk) = generate_ephemeral_key_pair(); @@ -34,30 +122,43 @@ fn compute_private_log_payload( let outgoing_body_ciphertext: [u8; 144] = compute_outgoing_body_ciphertext(recipient, fr_to_fq(ovsk_app), eph_sk, eph_pk); - // If we include the prefix for number of public values, we need to add 1 byte to the offset - let mut offset = if include_public_values_prefix { 1 } else { 0 }; + let mut encrypted_bytes = [0; M]; + let mut offset = 0; - let mut encrypted_bytes: [u8; M] = [0; M]; - // @todo We ignore the tags for now - offset += 64; + let tagging_secret = unsafe { get_app_tagging_secret(sender, recipient) }; + unsafe { increment_app_tagging_secret(sender, recipient); }; + + let tag = tagging_secret.compute_tag(); + let tag_bytes: [u8; 32] = tag.to_be_bytes(); + + for i in 0..32 { + encrypted_bytes[offset + i] = tag_bytes[i]; + } + offset += 32; + + // eph_pk let eph_pk_bytes = point_to_bytes(eph_pk); for i in 0..32 { encrypted_bytes[offset + i] = eph_pk_bytes[i]; } - offset += 32; + + // incoming_header + // outgoing_header for i in 0..48 { encrypted_bytes[offset + i] = incoming_header_ciphertext[i]; encrypted_bytes[offset + 48 + i] = outgoing_header_ciphertext[i]; } - offset += 48 * 2; + + // outgoing_body for i in 0..144 { encrypted_bytes[offset + i] = outgoing_body_ciphertext[i]; } - offset += 144; + + // incoming_body // Then we fill in the rest as the incoming body ciphertext let size = M - offset; assert_eq(size, incoming_body_ciphertext.len(), "ciphertext length mismatch"); @@ -65,19 +166,35 @@ fn compute_private_log_payload( encrypted_bytes[offset + i] = incoming_body_ciphertext[i]; } - // Current unoptimized size of the encrypted log - // empty_prefix (1 byte) - // incoming_tag (32 bytes) - // outgoing_tag (32 bytes) - // eph_pk (32 bytes) - // incoming_header (48 bytes) - // outgoing_header (48 bytes) - // outgoing_body (144 bytes) - // incoming_body_fixed (64 bytes) - // incoming_body_variable (P + 16 bytes padding) encrypted_bytes } +// Prepend the plaintext length as the first byte, then copy the plaintext itself starting from the second byte. +// Fill the remaining bytes with random values to reach a fixed length of N. +fn extend_private_log_plaintext(plaintext: [u8; P]) -> [u8; N] { + let mut padded = unsafe { get_random_bytes() }; + padded[0] = P as u8; + for i in 0..P { + padded[i + 1] = plaintext[i]; + } + padded +} + +unconstrained fn get_random_bytes() -> [u8; N] { + let mut bytes = [0; N]; + let mut idx = 32; + let mut randomness = [0; 32]; + for i in 0..N { + if idx == 32 { + randomness = random().to_be_bytes(); + idx = 1; // Skip the first byte as it's always 0. + } + bytes[i] = randomness[idx]; + idx += 1; + } + bytes +} + /// Converts a base field element to scalar field element. /// This is fine because modulus of the base field is smaller than the modulus of the scalar field. fn fr_to_fq(r: Field) -> Scalar { @@ -166,7 +283,7 @@ pub fn compute_outgoing_body_ciphertext( mod test { use crate::encrypted_logs::payload::{ compute_incoming_body_ciphertext, compute_outgoing_body_ciphertext, - compute_private_log_payload, + compute_private_log_payload, MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES, }; use dep::protocol_types::{ address::AztecAddress, point::Point, public_keys::OvpkM, scalar::Scalar, @@ -177,7 +294,7 @@ mod test { #[test] unconstrained fn test_encrypted_log_matches_typescript() { - // All the values in this test were copied over from `tagged_log.test.ts` + // All the values in this test were copied over from `encrypted_log_payload.test.ts` let contract_address = AztecAddress::from_field( 0x10f48cd9eff7ae5b209c557c70de2e657ee79166868676b787e9417e19260e04, ); @@ -199,48 +316,73 @@ mod test { 101, 153, 0, 0, 16, 39, ]; + let randomness = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + let _ = OracleMock::mock("getRandomField").returns(randomness).times( + (MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES as u64 + 1 + 30) / 31, + ); + let eph_sk = 0x1358d15019d4639393d62b97e1588c095957ce74a1c32d6ec7d62fe6705d9538; - let _ = OracleMock::mock("getRandomField").returns(eph_sk); + let _ = OracleMock::mock("getRandomField").returns(eph_sk).times(1); let recipient = AztecAddress::from_field( 0x25afb798ea6d0b8c1618e50fdeafa463059415013d3b7c75d46abf5e242be70c, ); - let log: [u8; 448] = compute_private_log_payload( + let sender = AztecAddress::from_field( + 0x25afb798ea6d0b8c1618e50fdeafa463059415013d3b7c75d46abf5e242be70c, + ); + + let _ = OracleMock::mock("getAppTaggingSecret").returns([ + 69420, + 0x25afb798ea6d0b8c1618e50fdeafa463059415013d3b7c75d46abf5e242be70c, + 1337, + ]); + + let _ = OracleMock::mock("incrementAppTaggingSecret"); + + let log = compute_private_log_payload( contract_address, ovsk_app, ovpk_m, recipient, + sender, plaintext, - false, ); // The following value was generated by `encrypted_log_payload.test.ts` // --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data. let encrypted_log_from_typescript = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 141, 70, 12, 14, 67, 77, 132, 110, 193, 234, 40, 110, 64, 144, 235, - 86, 55, 111, 242, 123, 221, 193, 170, 202, 225, 216, 86, 84, 159, 112, 31, 167, 5, 119, - 121, 10, 234, 188, 194, 216, 30, 200, 208, 201, 158, 127, 93, 43, 242, 241, 69, 32, 37, - 220, 119, 122, 23, 132, 4, 248, 81, 217, 61, 232, 24, 146, 63, 133, 24, 120, 113, 217, - 155, 223, 149, 214, 149, 239, 240, 169, 224, 155, 161, 81, 83, 252, 155, 77, 34, 75, - 110, 30, 113, 223, 189, 202, 171, 6, 192, 157, 91, 60, 116, 155, 254, 190, 28, 4, 7, - 236, 205, 4, 245, 27, 187, 89, 20, 38, 128, 200, 160, 145, 185, 127, 198, 203, 207, 97, - 246, 194, 175, 155, 142, 188, 143, 120, 83, 122, 178, 63, 208, 197, 232, 24, 228, 212, - 45, 69, 157, 38, 90, 219, 119, 194, 239, 130, 155, 246, 143, 135, 242, 196, 123, 71, - 139, 181, 122, 231, 228, 26, 7, 100, 63, 101, 195, 83, 8, 61, 85, 123, 148, 227, 29, - 164, 162, 161, 49, 39, 73, 141, 46, 179, 240, 52, 109, 165, 238, 210, 233, 188, 36, 90, - 175, 2, 42, 149, 78, 208, 176, 145, 50, 180, 152, 245, 55, 112, 40, 153, 180, 78, 54, - 102, 119, 98, 56, 235, 246, 51, 179, 86, 45, 127, 18, 77, 187, 168, 41, 24, 232, 113, - 149, 138, 148, 33, 143, 215, 150, 188, 105, 131, 254, 236, 199, 206, 56, 44, 130, 134, - 29, 99, 254, 69, 153, 146, 68, 234, 148, 148, 178, 38, 221, 182, 148, 178, 100, 13, 206, - 0, 91, 71, 58, 207, 26, 227, 190, 21, 143, 85, 138, 209, 202, 34, 142, 159, 121, 61, 9, - 57, 2, 48, 162, 89, 126, 14, 83, 173, 40, 247, 170, 154, 112, 12, 204, 48, 38, 7, 173, - 108, 38, 234, 20, 16, 115, 91, 106, 140, 121, 63, 99, 23, 247, 0, 148, 9, 163, 145, 43, - 21, 238, 47, 40, 204, 241, 124, 246, 201, 75, 114, 3, 1, 229, 197, 130, 109, 227, 158, - 133, 188, 125, 179, 220, 51, 170, 121, 175, 202, 243, 37, 103, 13, 27, 53, 157, 8, 177, - 11, 208, 120, 64, 211, 148, 201, 240, 56, + 14, 156, 255, 195, 221, 215, 70, 175, 251, 2, 65, 13, 143, 10, 130, 62, 137, 147, 151, + 133, 188, 200, 232, 142, 228, 243, 202, 224, 94, 115, 124, 54, 141, 70, 12, 14, 67, 77, + 132, 110, 193, 234, 40, 110, 64, 144, 235, 86, 55, 111, 242, 123, 221, 193, 170, 202, + 225, 216, 86, 84, 159, 112, 31, 167, 5, 119, 121, 10, 234, 188, 194, 216, 30, 200, 208, + 201, 158, 127, 93, 43, 242, 241, 69, 32, 37, 220, 119, 122, 23, 132, 4, 248, 81, 217, + 61, 232, 24, 146, 63, 133, 24, 120, 113, 217, 155, 223, 149, 214, 149, 239, 240, 169, + 224, 155, 161, 81, 83, 252, 155, 77, 34, 75, 110, 30, 113, 223, 189, 202, 171, 6, 192, + 157, 91, 60, 116, 155, 254, 190, 28, 4, 7, 236, 205, 4, 245, 27, 187, 89, 20, 38, 128, + 200, 160, 145, 185, 127, 198, 203, 207, 97, 246, 194, 175, 155, 142, 188, 143, 120, 83, + 122, 178, 63, 208, 197, 232, 24, 228, 212, 45, 69, 157, 38, 90, 219, 119, 194, 239, 130, + 155, 246, 143, 135, 242, 196, 123, 71, 139, 181, 122, 231, 228, 26, 7, 100, 63, 101, + 195, 83, 8, 61, 85, 123, 148, 227, 29, 164, 162, 161, 49, 39, 73, 141, 46, 179, 240, 52, + 109, 165, 238, 210, 233, 188, 36, 90, 175, 2, 42, 149, 78, 208, 176, 145, 50, 180, 152, + 245, 55, 112, 40, 153, 180, 78, 54, 102, 119, 98, 56, 235, 246, 51, 179, 86, 45, 127, + 18, 77, 187, 168, 41, 24, 232, 113, 149, 138, 148, 33, 143, 215, 150, 188, 105, 131, + 254, 236, 199, 206, 56, 44, 130, 134, 29, 99, 254, 69, 153, 146, 68, 234, 148, 148, 178, + 38, 221, 182, 103, 252, 139, 7, 246, 132, 29, 232, 78, 102, 126, 28, 136, 8, 219, 180, + 162, 14, 62, 71, 118, 40, 147, 93, 87, 188, 231, 32, 93, 56, 193, 194, 197, 120, 153, + 164, 139, 114, 18, 149, 2, 226, 19, 170, 250, 249, 128, 56, 236, 93, 14, 101, 115, 20, + 173, 73, 192, 53, 229, 7, 23, 59, 11, 176, 9, 147, 175, 168, 206, 48, 127, 126, 76, 51, + 211, 66, 232, 16, 132, 243, 14, 196, 181, 118, 12, 71, 236, 250, 253, 71, 249, 122, 30, + 23, 23, 19, 89, 47, 193, 69, 240, 164, 34, 128, 110, 13, 133, 198, 7, 165, 14, 31, 239, + 210, 146, 78, 67, 86, 32, 159, 244, 214, 246, 121, 246, 233, 252, 20, 131, 221, 28, 146, + 222, 119, 222, 162, 250, 252, 189, 18, 147, 12, 142, 177, 222, 178, 122, 248, 113, 197, + 40, 199, 152, 251, 91, 81, 243, 25, 156, 241, 141, 60, 12, 99, 103, 169, 97, 32, 112, + 37, 244, 255, 126, 46, 114, 226, 113, 223, 249, 27, 3, 31, 41, 233, 28, 8, 23, 84, 99, + 25, 186, 65, 33, 9, 35, 74, 16, 52, 169, 48, 161, 134, 233, 242, 136, 39, 162, 105, 205, + 43, 253, 183, 36, 138, 186, 87, 31, 7, 248, 125, 227, 193, 172, 155, 98, 33, 61, 186, + 158, 241, 192, 23, 28, 186, 100, 222, 174, 19, 64, 224, 113, 251, 143, 45, 152, 81, 67, + 116, 16, 95, 189, 83, 31, 124, 39, 155, 142, 66, 0, 120, 197, 221, 161, 62, 75, 192, + 255, 186, 200, 10, 135, 7, ]; assert_eq(encrypted_log_from_typescript, log); } diff --git a/noir-projects/aztec-nr/aztec/src/hash.nr b/noir-projects/aztec-nr/aztec/src/hash.nr index 4e5b9111463..e60eb7d229c 100644 --- a/noir-projects/aztec-nr/aztec/src/hash.nr +++ b/noir-projects/aztec-nr/aztec/src/hash.nr @@ -41,21 +41,23 @@ pub fn compute_unencrypted_log_hash( sha256_to_field(hash_bytes) } -pub fn compute_message_hash( +pub fn compute_l1_to_l2_message_hash( sender: EthAddress, chain_id: Field, recipient: AztecAddress, version: Field, content: Field, secret_hash: Field, + leaf_index: Field, ) -> Field { - let mut hash_bytes = [0 as u8; 192]; + let mut hash_bytes = [0 as u8; 224]; let sender_bytes: [u8; 32] = sender.to_field().to_be_bytes(); let chain_id_bytes: [u8; 32] = chain_id.to_be_bytes(); let recipient_bytes: [u8; 32] = recipient.to_field().to_be_bytes(); let version_bytes: [u8; 32] = version.to_be_bytes(); let content_bytes: [u8; 32] = content.to_be_bytes(); let secret_hash_bytes: [u8; 32] = secret_hash.to_be_bytes(); + let leaf_index_bytes: [u8; 32] = leaf_index.to_be_bytes(); for i in 0..32 { hash_bytes[i] = sender_bytes[i]; @@ -64,18 +66,15 @@ pub fn compute_message_hash( hash_bytes[i + 96] = version_bytes[i]; hash_bytes[i + 128] = content_bytes[i]; hash_bytes[i + 160] = secret_hash_bytes[i]; + hash_bytes[i + 192] = leaf_index_bytes[i]; } sha256_to_field(hash_bytes) } -// The nullifier of a l1 to l2 message is the hash of the message salted with the secret and index of the message hash -// in the L1 to L2 message tree -pub fn compute_message_nullifier(message_hash: Field, secret: Field, leaf_index: Field) -> Field { - poseidon2_hash_with_separator( - [message_hash, secret, leaf_index], - GENERATOR_INDEX__MESSAGE_NULLIFIER, - ) +// The nullifier of a l1 to l2 message is the hash of the message salted with the secret +pub fn compute_l1_to_l2_message_nullifier(message_hash: Field, secret: Field) -> Field { + poseidon2_hash_with_separator([message_hash, secret], GENERATOR_INDEX__MESSAGE_NULLIFIER) } pub struct ArgsHasher { diff --git a/noir-projects/aztec-nr/aztec/src/macros/functions/mod.nr b/noir-projects/aztec-nr/aztec/src/macros/functions/mod.nr index bc74d225c19..ed363f2c807 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/functions/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/functions/mod.nr @@ -213,7 +213,6 @@ pub comptime fn private(f: FunctionDefinition) -> Quoted { }; let modified_body = modify_fn_body(body, to_prepend, to_append); f.set_body(modified_body); - f.add_attribute("recursive"); f.set_return_type( quote { dep::protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs } .as_type(), diff --git a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr index 15526c653ca..0c10b6377c3 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr @@ -1,4 +1,8 @@ -use crate::{note::{note_getter_options::PropertySelector, note_header::NoteHeader}, prelude::Point}; +use crate::{ + encrypted_logs::payload::PRIVATE_LOG_OVERHEAD_IN_BYTES, + note::{note_getter_options::PropertySelector, note_header::NoteHeader}, + prelude::Point, +}; use protocol_types::meta::{flatten_to_fields, pack_from_fields}; use std::{ collections::umap::UHashMap, @@ -7,8 +11,6 @@ use std::{ }; comptime global NOTE_HEADER_TYPE = type_of(NoteHeader::empty()); -// The following is a fixed ciphertext overhead as defined by `compute_private_log_payload` -comptime global NOTE_CIPHERTEXT_OVERHEAD: u32 = 353; /// A map from note type to (note_struct_definition, serialized_note_length, note_type_id, fields). /// `fields` is an array of tuples where each tuple contains the name of the field/struct member (e.g. `amount` @@ -392,7 +394,7 @@ comptime fn generate_multi_scalar_mul( /// fn encrypt_log(self, context: &mut PrivateContext, recipient_keys: aztec::protocol_types::public_keys::PublicKeys, recipient: aztec::protocol_types::address::AztecAddress) -> [Field; 17] { /// let ovsk_app: Field = context.request_ovsk_app(recipient_keys.ovpk_m.hash()); /// -/// let encrypted_log_bytes: [u8; 513] = aztec::encrypted_logs::payload::compute_private_log_payload( +/// let encrypted_log_bytes: [u8; 513] = aztec::encrypted_logs::payload::compute_partial_public_log_payload( /// context.this_address(), /// ovsk_app, /// recipient_keys.ovpk_m, @@ -436,7 +438,10 @@ comptime fn generate_setup_payload( get_setup_log_plaintext_body(s, log_plaintext_length, indexed_nullable_fields); // Then we compute values for `encrypt_log(...)` function - let encrypted_log_byte_length = NOTE_CIPHERTEXT_OVERHEAD + log_plaintext_length; + let encrypted_log_byte_length = PRIVATE_LOG_OVERHEAD_IN_BYTES + + log_plaintext_length /* log_plaintext */ + + 1 /* log_plaintext_length */ + + 15 /* AES padding */; // Each field contains 31 bytes so the length in fields is computed as ceil(encrypted_log_byte_length / 31) // --> we achieve rouding by adding 30 and then dividing without remainder let encrypted_log_field_length = (encrypted_log_byte_length + 30) / 31; @@ -463,16 +468,16 @@ comptime fn generate_setup_payload( } } - fn encrypt_log(self, context: &mut PrivateContext, ovpk: aztec::protocol_types::public_keys::OvpkM, recipient: aztec::protocol_types::address::AztecAddress) -> [Field; $encrypted_log_field_length] { + fn encrypt_log(self, context: &mut PrivateContext, ovpk: aztec::protocol_types::public_keys::OvpkM, recipient: aztec::protocol_types::address::AztecAddress, sender: aztec::protocol_types::address::AztecAddress) -> [Field; $encrypted_log_field_length] { let ovsk_app: Field = context.request_ovsk_app(ovpk.hash()); - let encrypted_log_bytes: [u8; $encrypted_log_byte_length] = aztec::encrypted_logs::payload::compute_private_log_payload( + let encrypted_log_bytes: [u8; $encrypted_log_byte_length] = aztec::encrypted_logs::payload::compute_partial_public_log_payload( context.this_address(), ovsk_app, ovpk, recipient, + sender, self.log_plaintext, - true ); aztec::utils::bytes::bytes_to_fields(encrypted_log_bytes) @@ -656,11 +661,16 @@ comptime fn generate_finalization_payload( // Then we compute values for `encrypt_log(...)` function let setup_log_plaintext_length = indexed_fixed_fields.len() * 32 + 64; - let setup_log_byte_length = NOTE_CIPHERTEXT_OVERHEAD + setup_log_plaintext_length; + let setup_log_byte_length = PRIVATE_LOG_OVERHEAD_IN_BYTES + + setup_log_plaintext_length + + 1 /* log_plaintext_length */ + + 15 /* AES padding */; // Each field contains 31 bytes so the length in fields is computed as ceil(setup_log_byte_length / 31) // --> we achieve rouding by adding 30 and then dividing without remainder let setup_log_field_length = (setup_log_byte_length + 30) / 31; - let finalization_log_byte_length = setup_log_byte_length + public_values_length * 32; + let public_values_field_length = public_values_length * 32; + let finalization_log_byte_length = + 1 /* public_values_length */ + setup_log_byte_length + public_values_field_length; ( quote { @@ -720,22 +730,23 @@ comptime fn generate_finalization_payload( // We append the public value to the log and emit it as unencrypted log let mut finalization_log = [0; $finalization_log_byte_length]; + // Iterate over the partial log and copy it to the final log - for i in 1..setup_log.len() { - finalization_log[i] = setup_log[i]; + for i in 0..setup_log.len() { + finalization_log[i + 1] = setup_log[i]; } - // Now we populate the first byte with number of public values - finalization_log[0] = $public_values_length; - // Iterate over the public values and append them to the log for i in 0..$public_values_length { let public_value_bytes: [u8; 32] = self.public_values[i].to_be_bytes(); for j in 0..public_value_bytes.len() { - finalization_log[$setup_log_byte_length + i * 32 + j] = public_value_bytes[j]; + finalization_log[1 + $setup_log_byte_length + i * 32 + j] = public_value_bytes[j]; } } + // Populate the first byte with number of public values + finalization_log[0] = $public_values_length; + // We emit the finalization log via the unencrypted logs stream self.context.emit_unencrypted_log(finalization_log); diff --git a/noir-projects/aztec-nr/aztec/src/messaging.nr b/noir-projects/aztec-nr/aztec/src/messaging.nr index 6679fe0671e..98a4110e628 100644 --- a/noir-projects/aztec-nr/aztec/src/messaging.nr +++ b/noir-projects/aztec-nr/aztec/src/messaging.nr @@ -1,5 +1,5 @@ use crate::{ - hash::{compute_message_hash, compute_message_nullifier, compute_secret_hash}, + hash::{compute_l1_to_l2_message_hash, compute_l1_to_l2_message_nullifier, compute_secret_hash}, oracle::get_l1_to_l2_membership_witness::get_l1_to_l2_membership_witness, }; @@ -16,24 +16,26 @@ pub fn process_l1_to_l2_message( version: Field, content: Field, secret: Field, + leaf_index: Field, ) -> Field { let secret_hash = compute_secret_hash(secret); - let message_hash = compute_message_hash( + let message_hash = compute_l1_to_l2_message_hash( portal_contract_address, chain_id, contract_address, version, content, secret_hash, + leaf_index, ); // We prove that `message_hash` is in the tree by showing the derivation of the tree root, using a merkle path we // get from an oracle. - let (leaf_index, sibling_path) = + let (_leaf_index, sibling_path) = unsafe { get_l1_to_l2_membership_witness(contract_address, message_hash, secret) }; let root = root_from_sibling_path(message_hash, leaf_index, sibling_path); assert(root == l1_to_l2_root, "Message not in state"); - compute_message_nullifier(message_hash, secret, leaf_index) + compute_l1_to_l2_message_nullifier(message_hash, secret) } diff --git a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr index 00bb2f14ab1..c13d768425f 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr @@ -221,6 +221,16 @@ unconstrained fn get_app_tagging_secret_oracle( _recipient: AztecAddress, ) -> [Field; INDEXED_TAGGING_SECRET_LENGTH] {} +pub unconstrained fn increment_app_tagging_secret(sender: AztecAddress, recipient: AztecAddress) { + increment_app_tagging_secret_oracle(sender, recipient); +} + +#[oracle(incrementAppTaggingSecret)] +unconstrained fn increment_app_tagging_secret_oracle( + _sender: AztecAddress, + _recipient: AztecAddress, +) {} + /// Returns the tagging secrets for a given recipient and all the senders in PXE's address book, // siloed for the current contract address. /// Includes the last known index used for tagging with this secret. @@ -231,11 +241,11 @@ pub unconstrained fn get_app_tagging_secrets_for_senders( let results = get_app_tagging_secrets_for_senders_oracle(recipient); let mut indexed_tagging_secrets = &[]; for i in 0..results.len() { - if i % 2 != 0 { + if i % 3 != 0 { continue; } indexed_tagging_secrets = indexed_tagging_secrets.push_back( - IndexedTaggingSecret::deserialize([results[i], results[i + 1]]), + IndexedTaggingSecret::deserialize([results[i], results[i + 1], results[i + 2]]), ); } indexed_tagging_secrets diff --git a/noir-projects/aztec-nr/easy-private-state/src/easy_private_uint.nr b/noir-projects/aztec-nr/easy-private-state/src/easy_private_uint.nr index dbbaebb2859..d42e7c6d7ef 100644 --- a/noir-projects/aztec-nr/easy-private-state/src/easy_private_uint.nr +++ b/noir-projects/aztec-nr/easy-private-state/src/easy_private_uint.nr @@ -22,7 +22,13 @@ impl EasyPrivateUint { impl EasyPrivateUint<&mut PrivateContext> { // Very similar to `value_note::utils::increment`. - pub fn add(self, addend: u64, owner: AztecAddress, outgoing_viewer: AztecAddress) { + pub fn add( + self, + addend: u64, + owner: AztecAddress, + outgoing_viewer: AztecAddress, + sender: AztecAddress, + ) { let outgoing_viewer_keys = get_public_keys(outgoing_viewer); // Creates new note for the owner. let mut addend_note = ValueNote::new(addend as Field, owner); @@ -33,12 +39,19 @@ impl EasyPrivateUint<&mut PrivateContext> { self.context, outgoing_viewer_keys.ovpk_m, owner, + sender, )); // docs:end:insert } // Very similar to `value_note::utils::decrement`. - pub fn sub(self, subtrahend: u64, owner: AztecAddress, outgoing_viewer: AztecAddress) { + pub fn sub( + self, + subtrahend: u64, + owner: AztecAddress, + outgoing_viewer: AztecAddress, + sender: AztecAddress, + ) { let outgoing_viewer_keys = get_public_keys(outgoing_viewer); // docs:start:pop_notes @@ -63,6 +76,7 @@ impl EasyPrivateUint<&mut PrivateContext> { self.context, outgoing_viewer_keys.ovpk_m, owner, + sender, )); } } diff --git a/noir-projects/aztec-nr/value-note/src/utils.nr b/noir-projects/aztec-nr/value-note/src/utils.nr index fd7bb7c41c8..06eaca64a06 100644 --- a/noir-projects/aztec-nr/value-note/src/utils.nr +++ b/noir-projects/aztec-nr/value-note/src/utils.nr @@ -23,6 +23,7 @@ pub fn increment( amount: Field, recipient: AztecAddress, outgoing_viewer: AztecAddress, // docs:end:increment_args + sender: AztecAddress, ) { let outgoing_viewer_ovpk_m = get_public_keys(outgoing_viewer).ovpk_m; @@ -32,6 +33,7 @@ pub fn increment( balance.context, outgoing_viewer_ovpk_m, recipient, + sender, )); } @@ -44,8 +46,9 @@ pub fn decrement( amount: Field, owner: AztecAddress, outgoing_viewer: AztecAddress, + sender: AztecAddress, ) { - let sum = decrement_by_at_most(balance, amount, owner, outgoing_viewer); + let sum = decrement_by_at_most(balance, amount, owner, outgoing_viewer, sender); assert(sum == amount, "Balance too low"); } @@ -62,6 +65,7 @@ pub fn decrement_by_at_most( max_amount: Field, owner: AztecAddress, outgoing_viewer: AztecAddress, + sender: AztecAddress, ) -> Field { let options = create_note_getter_options_for_decreasing_balance(max_amount); let notes = balance.pop_notes(options); @@ -80,7 +84,7 @@ pub fn decrement_by_at_most( change_value = decremented - max_amount; decremented -= change_value; } - increment(balance, change_value, owner, outgoing_viewer); + increment(balance, change_value, owner, outgoing_viewer, sender); decremented } diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr index 2e9f3e9299d..fd11c9a9c5d 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr @@ -53,6 +53,7 @@ contract AppSubscription { &mut context, keys.ovpk_m, user_address, + user_address, )); context.set_as_fee_payer(); @@ -116,7 +117,12 @@ contract AppSubscription { let mut subscription_note = SubscriptionNote::new(subscriber, expiry_block_number, tx_count); storage.subscriptions.at(subscriber).initialize_or_replace(&mut subscription_note).emit( - encode_and_encrypt_note(&mut context, msg_sender_ovpk_m, subscriber), + encode_and_encrypt_note( + &mut context, + msg_sender_ovpk_m, + subscriber, + context.msg_sender(), + ), ); } diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr index b3525e74152..65f1cdbbab5 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr @@ -10,7 +10,6 @@ use dep::aztec::{ #[note] pub struct SubscriptionNote { - // The nullifying public key hash is used with the nsk_app to ensure that the note can be privately spent. owner: AztecAddress, expiry_block_number: Field, remaining_txs: Field, diff --git a/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr b/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr index 436617354af..e04c1c2ffb9 100644 --- a/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr @@ -22,7 +22,13 @@ contract Benchmarking { #[private] fn create_note(owner: AztecAddress, outgoing_viewer: AztecAddress, value: Field) { // docs:start:increment_valuenote - increment(storage.notes.at(owner), value, owner, outgoing_viewer); + increment( + storage.notes.at(owner), + value, + owner, + outgoing_viewer, + outgoing_viewer, + ); // docs:end:increment_valuenote } // Deletes a note at a specific index in the set and creates a new one with the same value. @@ -36,7 +42,13 @@ contract Benchmarking { let mut getter_options = NoteGetterOptions::new(); let notes = owner_notes.pop_notes(getter_options.set_limit(1).set_offset(index)); let note = notes.get(0); - increment(owner_notes, note.value, owner, outgoing_viewer); + increment( + owner_notes, + note.value, + owner, + outgoing_viewer, + outgoing_viewer, + ); } // Reads and writes to public storage and enqueues a call to another public function. diff --git a/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr b/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr index 2cc91c195db..34edfa95d37 100644 --- a/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr +++ b/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr @@ -118,6 +118,7 @@ impl Deck<&mut PrivateContext> { self.set.context, msg_sender_ovpk_m, owner, + self.set.context.msg_sender(), )); inserted_cards = inserted_cards.push_back(card_note); } diff --git a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr index a07237b535a..7bde2038e7e 100644 --- a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr @@ -63,6 +63,7 @@ contract Child { &mut context, owner_ovpk_m, owner, + owner, )); new_value } diff --git a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr index 965de3140a5..04e34bdec90 100644 --- a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr @@ -24,7 +24,7 @@ contract Counter { // We can name our initializer anything we want as long as it's marked as aztec(initializer) fn initialize(headstart: u64, owner: AztecAddress, outgoing_viewer: AztecAddress) { let counters = storage.counters; - counters.at(owner).add(headstart, owner, outgoing_viewer); + counters.at(owner).add(headstart, owner, outgoing_viewer, context.msg_sender()); } // docs:end:constructor @@ -38,7 +38,7 @@ contract Counter { ); } let counters = storage.counters; - counters.at(owner).add(1, owner, outgoing_viewer); + counters.at(owner).add(1, owner, outgoing_viewer, outgoing_viewer); } // docs:end:increment // docs:start:get_counter diff --git a/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr b/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr index d59d4c89e0c..92e89729e74 100644 --- a/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr @@ -91,6 +91,7 @@ contract Crowdfunding { &mut context, donor_ovpk_m, donor, + donor, )); } // docs:end:donate diff --git a/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr b/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr index 9422490654b..dada8fd336b 100644 --- a/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr @@ -181,6 +181,7 @@ contract DocsExample { &mut context, msg_sender_ovpk_m, context.msg_sender(), + context.msg_sender(), )); } // docs:end:initialize-private-mutable @@ -196,6 +197,7 @@ contract DocsExample { &mut context, msg_sender_ovpk_m, context.msg_sender(), + context.msg_sender(), )); } @@ -209,6 +211,7 @@ contract DocsExample { &mut context, msg_sender_ovpk_m, context.msg_sender(), + context.msg_sender(), )); } } @@ -221,6 +224,7 @@ contract DocsExample { &mut context, msg_sender_ovpk_m, context.msg_sender(), + context.msg_sender(), )); } // docs:start:state_vars-NoteGetterOptionsComparatorExampleNoir @@ -238,6 +242,7 @@ contract DocsExample { &mut context, msg_sender_ovpk_m, context.msg_sender(), + context.msg_sender(), )); DocsExample::at(context.this_address()).update_leader(context.msg_sender(), points).enqueue( &mut context, @@ -259,6 +264,7 @@ contract DocsExample { &mut context, msg_sender_ovpk_m, context.msg_sender(), + context.msg_sender(), )); // docs:end:state_vars-PrivateMutableReplace DocsExample::at(context.this_address()).update_leader(context.msg_sender(), points).enqueue( diff --git a/noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr index 0544a592f73..8abf2a3be45 100644 --- a/noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr @@ -21,7 +21,7 @@ contract EasyPrivateToken { fn constructor(initial_supply: u64, owner: AztecAddress, outgoing_viewer: AztecAddress) { let balances = storage.balances; - balances.at(owner).add(initial_supply, owner, outgoing_viewer); + balances.at(owner).add(initial_supply, owner, outgoing_viewer, context.msg_sender()); } // Mints `amount` of tokens to `owner`. @@ -29,7 +29,7 @@ contract EasyPrivateToken { fn mint(amount: u64, owner: AztecAddress, outgoing_viewer: AztecAddress) { let balances = storage.balances; - balances.at(owner).add(amount, owner, outgoing_viewer); + balances.at(owner).add(amount, owner, outgoing_viewer, context.msg_sender()); } // Transfers `amount` of tokens from `sender` to a `recipient`. @@ -42,8 +42,8 @@ contract EasyPrivateToken { ) { let balances = storage.balances; - balances.at(sender).sub(amount, sender, outgoing_viewer); - balances.at(recipient).add(amount, recipient, outgoing_viewer); + balances.at(sender).sub(amount, sender, outgoing_viewer, sender); + balances.at(recipient).add(amount, recipient, outgoing_viewer, sender); } // Helper function to get the balance of a user ("unconstrained" is a Noir alternative of Solidity's "view" function). diff --git a/noir-projects/noir-contracts/contracts/ecdsa_k_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/ecdsa_k_account_contract/src/main.nr index 8f2dc055f33..5a486330c9b 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_k_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_k_account_contract/src/main.nr @@ -39,6 +39,7 @@ contract EcdsaKAccount { &mut context, this_ovpk_m, this, + this, )); } diff --git a/noir-projects/noir-contracts/contracts/ecdsa_r_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/ecdsa_r_account_contract/src/main.nr index b8e2e18b08a..94e86352fda 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_r_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_r_account_contract/src/main.nr @@ -37,6 +37,7 @@ contract EcdsaRAccount { &mut context, this_ovpk_m, this, + this, )); } diff --git a/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr b/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr index b5438144c2a..4aa3759433a 100644 --- a/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr @@ -32,6 +32,7 @@ contract Escrow { &mut context, msg_sender_ovpk_m, owner, + context.msg_sender(), )); } diff --git a/noir-projects/noir-contracts/contracts/fee_juice_contract/src/main.nr b/noir-projects/noir-contracts/contracts/fee_juice_contract/src/main.nr index 3f0cdbaa78e..065427f87d1 100644 --- a/noir-projects/noir-contracts/contracts/fee_juice_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fee_juice_contract/src/main.nr @@ -48,13 +48,13 @@ contract FeeJuice { } #[private] - fn claim(to: AztecAddress, amount: Field, secret: Field) { + fn claim(to: AztecAddress, amount: Field, secret: Field, message_leaf_index: Field) { let content_hash = get_bridge_gas_msg_hash(to, amount); let portal_address = storage.portal_address.read_private(); assert(!portal_address.is_zero()); // Consume message and emit nullifier - context.consume_l1_to_l2_message(content_hash, secret, portal_address); + context.consume_l1_to_l2_message(content_hash, secret, portal_address, message_leaf_index); // TODO(palla/gas) Emit an unencrypted log to announce which L1 to L2 message has been claimed // Otherwise, we cannot trace L1 deposits to their corresponding claims on L2 diff --git a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr index 708f24fe0b8..05118734198 100644 --- a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr @@ -31,9 +31,9 @@ contract FPC { nonce: Field, ) { assert(asset == storage.other_asset.read_private()); - Token::at(asset).unshield(context.msg_sender(), context.this_address(), amount, nonce).call( - &mut context, - ); + Token::at(asset) + .transfer_to_public(context.msg_sender(), context.this_address(), amount, nonce) + .call(&mut context); context.set_as_fee_payer(); // Would like to get back to // FPC::at(context.this_address()).pay_refund_with_shielded_rebate(amount, asset, secret_hash).set_public_teardown_function(&mut context); diff --git a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index cdb13bbe4a6..8be4ea12343 100644 --- a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -40,6 +40,7 @@ contract InclusionProofs { &mut context, msg_sender_ovpk_m, owner, + context.msg_sender(), )); } // docs:end:create_note diff --git a/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr b/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr index 077be6fea40..938d403c803 100644 --- a/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr @@ -112,7 +112,7 @@ contract Lending { let on_behalf_of = compute_identifier(secret, on_behalf_of, context.msg_sender().to_field()); let _res = Token::at(collateral_asset) - .unshield(from, context.this_address(), amount, nonce) + .transfer_to_public(from, context.this_address(), amount, nonce) .call(&mut context); // docs:start:enqueue_public Lending::at(context.this_address()) diff --git a/noir-projects/noir-contracts/contracts/nft_contract/src/main.nr b/noir-projects/noir-contracts/contracts/nft_contract/src/main.nr index f058088abc7..401e5b015ed 100644 --- a/noir-projects/noir-contracts/contracts/nft_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/nft_contract/src/main.nr @@ -188,7 +188,8 @@ contract NFT { // We set the ovpk to the message sender's ovpk and we encrypt the log. let from_ovpk = get_public_keys(context.msg_sender()).ovpk_m; - let setup_log = note_setup_payload.encrypt_log(context, from_ovpk, to); + let setup_log = + note_setup_payload.encrypt_log(context, from_ovpk, to, context.msg_sender()); // Using the x-coordinate as a hiding point slot is safe against someone else interfering with it because // we have a guarantee that the public functions of the transaction are executed right after the private ones @@ -227,7 +228,7 @@ contract NFT { fn _store_payload_in_transient_storage_unsafe( slot: Field, point: Point, - setup_log: [Field; 16], + setup_log: [Field; 15], ) { context.storage_write(slot, point); context.storage_write(slot + aztec::protocol_types::point::POINT_LENGTH as Field, setup_log); @@ -256,7 +257,7 @@ contract NFT { fn _finalize_transfer_to_private( from: AztecAddress, token_id: Field, - note_transient_storage_slot: Field, + hiding_point_slot: Field, context: &mut PublicContext, storage: Storage<&mut PublicContext>, ) { @@ -268,7 +269,7 @@ contract NFT { // Finalize the partial note with the `token_id` let finalization_payload = - NFTNote::finalization_payload().new(context, note_transient_storage_slot, token_id); + NFTNote::finalization_payload().new(context, hiding_point_slot, token_id); // At last we emit the note hash and the final log finalization_payload.emit(); @@ -307,6 +308,7 @@ contract NFT { &mut context, from_ovpk_m, to, + from, )); } diff --git a/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr b/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr index 025127127a6..27edabea0c9 100644 --- a/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr +++ b/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr @@ -16,7 +16,7 @@ use dep::aztec::{ pub struct NFTNote { // ID of the token token_id: Field, - // The nullifying public key hash is used with the nsk_app to ensure that the note can be privately spent. + // The owner of the note owner: AztecAddress, // Randomness of the note to hide its contents randomness: Field, diff --git a/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr b/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr index 5858192c8d8..f1211f87b3b 100644 --- a/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr @@ -47,6 +47,7 @@ contract PendingNoteHashes { &mut context, outgoing_viewer_ovpk_m, owner, + context.msg_sender(), )); let options = NoteGetterOptions::with_filter(filter_notes_min_sum, amount); @@ -96,6 +97,7 @@ contract PendingNoteHashes { &mut context, outgoing_viewer_ovpk_m, owner, + context.msg_sender(), )); } @@ -120,6 +122,7 @@ contract PendingNoteHashes { &mut context, outgoing_viewer_ovpk_m, owner, + context.msg_sender(), )); } @@ -136,10 +139,20 @@ contract PendingNoteHashes { // Insert note let emission = owner_balance.insert(&mut note); - emission.emit(encode_and_encrypt_note(&mut context, outgoing_viewer_ovpk_m, owner)); + emission.emit(encode_and_encrypt_note( + &mut context, + outgoing_viewer_ovpk_m, + owner, + context.msg_sender(), + )); // Emit note again - emission.emit(encode_and_encrypt_note(&mut context, outgoing_viewer_ovpk_m, owner)); + emission.emit(encode_and_encrypt_note( + &mut context, + outgoing_viewer_ovpk_m, + owner, + context.msg_sender(), + )); } // Nested/inner function to get a note and confirm it matches the expected value @@ -359,6 +372,7 @@ contract PendingNoteHashes { &mut context, outgoing_viewer_ovpk_m, owner, + context.msg_sender(), )); // We will emit a note log with an incorrect preimage to ensure the pxe throws @@ -372,6 +386,7 @@ contract PendingNoteHashes { &mut context, outgoing_viewer_ovpk_m, owner, + context.msg_sender(), )); } @@ -392,6 +407,7 @@ contract PendingNoteHashes { context, outgoing_viewer_ovpk_m, owner, + context.msg_sender(), )); } } diff --git a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr index 325932f6206..84379b702a1 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr @@ -44,6 +44,7 @@ contract SchnorrAccount { &mut context, this_ovpk_m, this, + this, )); } diff --git a/noir-projects/noir-contracts/contracts/spam_contract/src/main.nr b/noir-projects/noir-contracts/contracts/spam_contract/src/main.nr index b1bfe78f565..3523cc6e3f7 100644 --- a/noir-projects/noir-contracts/contracts/spam_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/spam_contract/src/main.nr @@ -36,7 +36,7 @@ contract Spam { for _ in 0..MAX_NOTE_HASHES_PER_CALL { storage.balances.at(caller).add(caller, U128::from_integer(amount)).emit( - encode_and_encrypt_note_unconstrained(&mut context, caller_ovpk_m, caller), + encode_and_encrypt_note_unconstrained(&mut context, caller_ovpk_m, caller, caller), ); } diff --git a/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr index 5b06e7fde18..cf83b3eabf0 100644 --- a/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr @@ -19,7 +19,7 @@ trait OwnedNote { pub struct TokenNote { // The amount of tokens in the note amount: U128, - // The nullifying public key hash is used with the nsk_app to ensure that the note can be privately spent. + // The owner of the note owner: AztecAddress, // Randomness of the note to hide its contents randomness: Field, diff --git a/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr index 958386d0803..99df952a7bc 100644 --- a/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr @@ -45,7 +45,7 @@ contract StatefulTest { fn create_note(owner: AztecAddress, outgoing_viewer: AztecAddress, value: Field) { if (value != 0) { let loc = storage.notes.at(owner); - increment(loc, value, owner, outgoing_viewer); + increment(loc, value, owner, outgoing_viewer, outgoing_viewer); } } @@ -54,7 +54,7 @@ contract StatefulTest { fn create_note_no_init_check(owner: AztecAddress, outgoing_viewer: AztecAddress, value: Field) { if (value != 0) { let loc = storage.notes.at(owner); - increment(loc, value, owner, outgoing_viewer); + increment(loc, value, owner, outgoing_viewer, outgoing_viewer); } } @@ -64,10 +64,10 @@ contract StatefulTest { let sender = context.msg_sender(); let sender_notes = storage.notes.at(sender); - decrement(sender_notes, amount, sender, context.msg_sender()); + decrement(sender_notes, amount, sender, sender, sender); let recipient_notes = storage.notes.at(recipient); - increment(recipient_notes, amount, recipient, context.msg_sender()); + increment(recipient_notes, amount, recipient, sender, sender); } #[private] @@ -76,10 +76,10 @@ contract StatefulTest { let sender = context.msg_sender(); let sender_notes = storage.notes.at(sender); - decrement(sender_notes, amount, sender, context.msg_sender()); + decrement(sender_notes, amount, sender, sender, sender); let recipient_notes = storage.notes.at(recipient); - increment(recipient_notes, amount, recipient, context.msg_sender()); + increment(recipient_notes, amount, recipient, sender, sender); } #[public] diff --git a/noir-projects/noir-contracts/contracts/static_child_contract/src/main.nr b/noir-projects/noir-contracts/contracts/static_child_contract/src/main.nr index 418c6a9c281..26ca21d1d75 100644 --- a/noir-projects/noir-contracts/contracts/static_child_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/static_child_contract/src/main.nr @@ -52,6 +52,7 @@ contract StaticChild { &mut context, msg_sender_ovpk_m, owner, + context.msg_sender(), )); new_value } @@ -70,6 +71,7 @@ contract StaticChild { &mut context, outgoing_viewer_ovpk_m, owner, + outgoing_viewer, )); new_value } diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr index 1305b464478..8b745370265 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -109,6 +109,7 @@ contract Test { &mut context, outgoing_viewer_ovpk_m, owner, + outgoing_viewer, )); } @@ -303,6 +304,7 @@ contract Test { 5, outgoing_viewer_ovpk_m, owner, + outgoing_viewer, )); // this contract has reached max number of functions, so using this one fn @@ -320,6 +322,7 @@ contract Test { 0, outgoing_viewer_ovpk_m, owner, + outgoing_viewer, )); } } @@ -343,6 +346,7 @@ contract Test { &mut context, msg_sender_ovpk_m, owner, + outgoing_viewer, )); storage_slot += 1; Test::at(context.this_address()) @@ -386,6 +390,7 @@ contract Test { amount: Field, secret_for_L1_to_L2_message_consumption: Field, portal_address: EthAddress, + message_leaf_index: Field, ) { // Consume L1 to L2 message and emit nullifier let content_hash = @@ -394,6 +399,7 @@ contract Test { content_hash, secret_for_L1_to_L2_message_consumption, portal_address, + message_leaf_index, ); } @@ -413,9 +419,10 @@ contract Test { content: Field, secret: Field, sender: EthAddress, + message_leaf_index: Field, ) { // Consume message and emit nullifier - context.consume_l1_to_l2_message(content, secret, sender); + context.consume_l1_to_l2_message(content, secret, sender, message_leaf_index); } #[private] diff --git a/noir-projects/noir-contracts/contracts/test_log_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test_log_contract/src/main.nr index 8c3f65d38fd..19e5c109243 100644 --- a/noir-projects/noir-contracts/contracts/test_log_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_log_contract/src/main.nr @@ -48,6 +48,7 @@ contract TestLog { // outgoing is set to other, incoming is set to msg sender other_ovpk_m, context.msg_sender(), + other, )); // We duplicate the emission, but specifying different incoming and outgoing parties @@ -57,6 +58,7 @@ contract TestLog { // outgoing is set to msg sender, incoming is set to other msg_sender_ovpk_m, other, + context.msg_sender(), )); let event1 = ExampleEvent1 { @@ -70,6 +72,7 @@ contract TestLog { // outgoing is set to other, incoming is set to msg sender other_ovpk_m, context.msg_sender(), + other, )); } diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr index 765d8530ae9..bdb3b8bd0a9 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr @@ -191,7 +191,12 @@ contract TokenBlacklist { // TODO: constrain encryption below - we are using unconstrained here only becuase of the following Noir issue // https://github.com/noir-lang/noir/issues/5771 storage.balances.add(to, U128::from_integer(amount)).emit( - encode_and_encrypt_note_unconstrained(&mut context, msg_sender_ovpk_m, to), + encode_and_encrypt_note_unconstrained( + &mut context, + msg_sender_ovpk_m, + to, + context.msg_sender(), + ), ); } @@ -212,7 +217,7 @@ contract TokenBlacklist { // TODO: constrain encryption below - we are using unconstrained here only becuase of the following Noir issue // https://github.com/noir-lang/noir/issues/5771 storage.balances.sub(from, U128::from_integer(amount)).emit( - encode_and_encrypt_note_unconstrained(&mut context, from_ovpk_m, from), + encode_and_encrypt_note_unconstrained(&mut context, from_ovpk_m, from, from), ); TokenBlacklist::at(context.this_address())._increase_public_balance(to, amount).enqueue( @@ -241,11 +246,13 @@ contract TokenBlacklist { &mut context, from_ovpk_m, from, + from, )); storage.balances.add(to, amount).emit(encode_and_encrypt_note_unconstrained( &mut context, from_ovpk_m, to, + from, )); } @@ -264,7 +271,7 @@ contract TokenBlacklist { // TODO: constrain encryption below - we are using unconstrained here only becuase of the following Noir issue // https://github.com/noir-lang/noir/issues/5771 storage.balances.sub(from, U128::from_integer(amount)).emit( - encode_and_encrypt_note_unconstrained(&mut context, from_ovpk_m, from), + encode_and_encrypt_note_unconstrained(&mut context, from_ovpk_m, from, from), ); TokenBlacklist::at(context.this_address())._reduce_total_supply(amount).enqueue(&mut context); diff --git a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr index ef94712166a..4ef9953c938 100644 --- a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr @@ -98,6 +98,7 @@ contract TokenBridge { secret_hash_for_redeeming_minted_notes: Field, // secret hash used to redeem minted notes at a later time. This enables anyone to call this function and mint tokens to a user on their behalf amount: Field, secret_for_L1_to_L2_message_consumption: Field, // secret used to consume the L1 to L2 message + message_leaf_index: Field, ) { // Consume L1 to L2 message and emit nullifier let content_hash = @@ -106,6 +107,7 @@ contract TokenBridge { content_hash, secret_for_L1_to_L2_message_consumption, storage.portal_address.read_private(), + message_leaf_index, ); // Mint tokens on L2 @@ -156,7 +158,7 @@ contract TokenBridge { #[public] #[internal] fn _call_mint_on_token(amount: Field, secret_hash: Field) { - Token::at(storage.token.read()).mint_private(amount, secret_hash).call(&mut context); + Token::at(storage.token.read()).mint_private_old(amount, secret_hash).call(&mut context); } // docs:end:call_mint_on_token diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index 78d1336cbc2..65968ff0622 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -35,8 +35,8 @@ contract Token { }, oracle::random::random, prelude::{ - AztecAddress, FunctionSelector, Map, NoteGetterOptions, PrivateSet, PublicMutable, - SharedImmutable, + AztecAddress, FunctionSelector, Map, NoteGetterOptions, PrivateSet, PublicContext, + PublicMutable, SharedImmutable, }, protocol_types::{point::Point, traits::Serialize}, utils::comparison::Comparator, @@ -208,8 +208,9 @@ contract Token { } // docs:end:mint_public // docs:start:mint_private + // TODO(benesjan): To be nuked in a followup PR. #[public] - fn mint_private(amount: Field, secret_hash: Field) { + fn mint_private_old(amount: Field, secret_hash: Field) { assert(storage.minters.at(context.msg_sender()).read(), "caller is not minter"); let pending_shields = storage.pending_shields; let mut note = TransparentNote::new(amount, secret_hash); @@ -220,25 +221,6 @@ contract Token { // docs:end:insert_from_public } // docs:end:mint_private - // TODO: Nuke this - test functions do not belong to token contract! - #[private] - fn privately_mint_private_note(amount: Field) { - let caller = context.msg_sender(); - let caller_ovpk_m = get_public_keys(caller).ovpk_m; - storage.balances.at(caller).add(caller, U128::from_integer(amount)).emit( - encode_and_encrypt_note(&mut context, caller_ovpk_m, caller), - ); - Token::at(context.this_address()) - .assert_minter_and_mint(context.msg_sender(), amount) - .enqueue(&mut context); - } - #[public] - #[internal] - fn assert_minter_and_mint(minter: AztecAddress, amount: Field) { - assert(storage.minters.at(minter).read(), "caller is not minter"); - let supply = storage.total_supply.read() + U128::from_integer(amount); - storage.total_supply.write(supply); - } // docs:start:shield #[public] fn shield(from: AztecAddress, amount: Field, secret_hash: Field, nonce: Field) { @@ -310,12 +292,13 @@ contract Token { &mut context, from_ovpk_m, to, + context.msg_sender(), )); } // docs:end:redeem_shield - // docs:start:unshield + // docs:start:transfer_to_public #[private] - fn unshield(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) { + fn transfer_to_public(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) { if (!from.eq(context.msg_sender())) { assert_current_call_valid_authwit(&mut context, from); } else { @@ -326,11 +309,11 @@ contract Token { // TODO: constrain encryption below - we are using unconstrained here only becuase of the following Noir issue // https://github.com/noir-lang/noir/issues/5771 storage.balances.at(from).sub(from, U128::from_integer(amount)).emit( - encode_and_encrypt_note_unconstrained(&mut context, from_ovpk_m, from), + encode_and_encrypt_note_unconstrained(&mut context, from_ovpk_m, from, from), ); Token::at(context.this_address())._increase_public_balance(to, amount).enqueue(&mut context); } - // docs:end:unshield + // docs:end:transfer_to_public // docs:start:transfer #[private] fn transfer(to: AztecAddress, amount: Field) { @@ -356,18 +339,20 @@ contract Token { &mut context, from_ovpk_m, from, + from, )); storage.balances.at(to).add(to, amount).emit(encode_and_encrypt_note_unconstrained( &mut context, from_ovpk_m, to, + from, )); // We don't constrain encryption of the note log in `transfer` (unlike in `transfer_from`) because the transfer // function is only designed to be used in situations where the event is not strictly necessary (e.g. payment to // another person where the payment is considered to be successful when the other party successfully decrypts a // note). Transfer { from, to, amount: amount.to_field() }.emit( - encode_and_encrypt_event_unconstrained(&mut context, from_ovpk_m, to), + encode_and_encrypt_event_unconstrained(&mut context, from_ovpk_m, to, from), ); } // docs:end:transfer @@ -452,6 +437,7 @@ contract Token { &mut context, from_ovpk_m, from, + from, )); // docs:end:encrypted // docs:end:increase_private_balance @@ -461,6 +447,7 @@ contract Token { &mut context, from_ovpk_m, to, + from, )); } // docs:end:transfer_from @@ -476,11 +463,193 @@ contract Token { // TODO: constrain encryption below - we are using unconstrained here only becuase of the following Noir issue // https://github.com/noir-lang/noir/issues/5771 storage.balances.at(from).sub(from, U128::from_integer(amount)).emit( - encode_and_encrypt_note_unconstrained(&mut context, from_ovpk_m, from), + encode_and_encrypt_note_unconstrained(&mut context, from_ovpk_m, from, from), ); Token::at(context.this_address())._reduce_total_supply(amount).enqueue(&mut context); } // docs:end:burn + + // Transfers token `amount` from public balance of message sender to a private balance of `to`. + #[private] + fn transfer_to_private(to: AztecAddress, amount: Field) { + // We check the minter permissions in the enqueued call as that allows us to avoid the need for `SharedMutable` + // which is less efficient. + let from = context.msg_sender(); + let token = Token::at(context.this_address()); + + // We prepare the transfer. + let hiding_point_slot = _prepare_transfer_to_private(to, &mut context, storage); + + // At last we finalize the transfer. Usage of the `unsafe` method here is safe because we set the `from` + // function argument to a message sender, guaranteeing that he can transfer only his own tokens. + token._finalize_transfer_to_private_unsafe(from, amount, hiding_point_slot).enqueue( + &mut context, + ); + } + + /// Prepares a transfer to a private balance of `to`. The transfer then needs to be + /// finalized by calling `finalize_transfer_to_private`. Returns a hiding point slot. + #[private] + fn prepare_transfer_to_private(to: AztecAddress) -> Field { + _prepare_transfer_to_private(to, &mut context, storage) + } + + /// This function exists separately from `prepare_transfer_to_private` solely as an optimization as it allows + /// us to have it inlined in the `transfer_to_private` function which results in one less kernel iteration. + /// + /// TODO(#9180): Consider adding macro support for functions callable both as an entrypoint and as an internal + /// function. + #[contract_library_method] + fn _prepare_transfer_to_private( + to: AztecAddress, + context: &mut PrivateContext, + storage: Storage<&mut PrivateContext>, + ) -> Field { + let to_note_slot = storage.balances.at(to).set.storage_slot; + + // We create a setup payload with unpopulated/zero `amount` for 'to' + // TODO(#7775): Manually fetching the randomness here is not great. If we decide to include randomness in all + // notes we could just inject it in macros. + let note_randomness = unsafe { random() }; + let note_setup_payload = UintNote::setup_payload().new(to, note_randomness, to_note_slot); + + // We set the ovpk to the message sender's ovpk and we encrypt the log. + let from_ovpk = get_public_keys(context.msg_sender()).ovpk_m; + let setup_log = + note_setup_payload.encrypt_log(context, from_ovpk, to, context.msg_sender()); + + // Using the x-coordinate as a hiding point slot is safe against someone else interfering with it because + // we have a guarantee that the public functions of the transaction are executed right after the private ones + // and for this reason the protocol guarantees that nobody can front-run us in consuming the hiding point. + // This guarantee would break if `finalize_transfer_to_private` was not called in the same transaction. This + // however is not the flow we are currently concerned with. To support the multi-transaction flow we could + // introduce a `from` function argument, hash the x-coordinate with it and then repeat the hashing in + // `finalize_transfer_to_private`. + // + // We can also be sure that the `hiding_point_slot` will not overwrite any other value in the storage because + // in our state variables we derive slots using a different hash function from multi scalar multiplication + // (MSM). + let hiding_point_slot = note_setup_payload.hiding_point.x; + + // We don't need to perform a check that the value overwritten by `_store_point_in_transient_storage_unsafe` + // is zero because the slot is the x-coordinate of the hiding point and hence we could only overwrite + // the value in the slot with the same value. This makes usage of the `unsafe` method safe. + Token::at(context.this_address()) + ._store_payload_in_transient_storage_unsafe( + hiding_point_slot, + note_setup_payload.hiding_point, + setup_log, + ) + .enqueue(context); + + hiding_point_slot + } + + /// Finalizes a transfer of token `amount` from public balance of `from` to a private balance of `to`. + /// The transfer must be prepared by calling `prepare_transfer_to_private` first and the resulting + /// `hiding_point_slot` must be passed as an argument to this function. + #[public] + fn finalize_transfer_to_private(amount: Field, hiding_point_slot: Field) { + let from = context.msg_sender(); + _finalize_transfer_to_private(from, amount, hiding_point_slot, &mut context, storage); + } + + #[public] + #[internal] + fn _finalize_transfer_to_private_unsafe( + from: AztecAddress, + amount: Field, + hiding_point_slot: Field, + ) { + _finalize_transfer_to_private(from, amount, hiding_point_slot, &mut context, storage); + } + + #[contract_library_method] + fn _finalize_transfer_to_private( + from: AztecAddress, + amount: Field, + hiding_point_slot: Field, + context: &mut PublicContext, + storage: Storage<&mut PublicContext>, + ) { + // TODO(#8271): Type the amount as U128 and nuke the ugly cast + let amount = U128::from_integer(amount); + + // First we subtract the `amount` from the public balance of `from` + let from_balance = storage.public_balances.at(from).read().sub(amount); + storage.public_balances.at(from).write(from_balance); + + // Then we finalize the partial note with the `amount` + let finalization_payload = + UintNote::finalization_payload().new(context, hiding_point_slot, amount); + + // At last we emit the note hash and the final log + finalization_payload.emit(); + } + + /// Mints token `amount` to a private balance of `to`. Message sender has to have minter permissions (checked + /// in the enqueud call). + #[private] + fn mint_to_private(to: AztecAddress, amount: Field) { + let from = context.msg_sender(); + let token = Token::at(context.this_address()); + + // We prepare the transfer. + let hiding_point_slot = _prepare_transfer_to_private(to, &mut context, storage); + + // At last we finalize the mint. Usage of the `unsafe` method here is safe because we set the `from` + // function argument to a message sender, guaranteeing that only a message sender with minter permissions + // can successfully execute the function. + token._finalize_mint_to_private_unsafe(from, amount, hiding_point_slot).enqueue(&mut context); + } + + /// Finalizes a mint of token `amount` to a private balance of `to`. The mint must be prepared by calling + /// `prepare_transfer_to_private` first and the resulting + /// `hiding_point_slot` must be passed as an argument to this function. + /// + /// Note: This function is only an optimization as it could be replaced by a combination of `mint_public` + /// and `finalize_transfer_to_private`. It is however used very commonly so it makes sense to optimize it + /// (e.g. used during token bridging, in AMM liquidity token etc.). + #[public] + fn finalize_mint_to_private(amount: Field, hiding_point_slot: Field) { + assert(storage.minters.at(context.msg_sender()).read(), "caller is not minter"); + + _finalize_mint_to_private(amount, hiding_point_slot, &mut context, storage); + } + + #[public] + #[internal] + fn _finalize_mint_to_private_unsafe( + from: AztecAddress, + amount: Field, + hiding_point_slot: Field, + ) { + // We check the minter permissions as it was not done in `mint_to_private` function. + assert(storage.minters.at(from).read(), "caller is not minter"); + _finalize_mint_to_private(amount, hiding_point_slot, &mut context, storage); + } + + #[contract_library_method] + fn _finalize_mint_to_private( + amount: Field, + hiding_point_slot: Field, + context: &mut PublicContext, + storage: Storage<&mut PublicContext>, + ) { + let amount = U128::from_integer(amount); + + // First we increase the total supply by the `amount` + let supply = storage.total_supply.read().add(amount); + storage.total_supply.write(supply); + + // Then we finalize the partial note with the `amount` + let finalization_payload = + UintNote::finalization_payload().new(context, hiding_point_slot, amount); + + // At last we emit the note hash and the final log + finalization_payload.emit(); + } + /// We need to use different randomness for the user and for the fee payer notes because if the randomness values /// were the same we could fingerprint the user by doing the following: /// 1) randomness_influence = fee_payer_point - G_npk * fee_payer_npk = @@ -528,6 +697,7 @@ contract Token { &mut context, user_ovpk, user, + user, )); // 4. Now we get the partial payloads @@ -565,8 +735,9 @@ contract Token { // 6. We compute setup logs let fee_payer_setup_log = - fee_payer_setup_payload.encrypt_log(&mut context, user_ovpk, fee_payer); - let user_setup_log = user_setup_payload.encrypt_log(&mut context, user_ovpk, user); + fee_payer_setup_payload.encrypt_log(&mut context, user_ovpk, fee_payer, fee_payer); + let user_setup_log = + user_setup_payload.encrypt_log(&mut context, user_ovpk, user, fee_payer); // 7. We store the hiding points an logs in transients storage Token::at(context.this_address()) @@ -604,7 +775,7 @@ contract Token { fn _store_payload_in_transient_storage_unsafe( slot: Field, point: Point, - setup_log: [Field; 16], + setup_log: [Field; 15], ) { context.storage_write(slot, point); context.storage_write(slot + aztec::protocol_types::point::POINT_LENGTH as Field, setup_log); diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test.nr index 7d23b6d81c0..35a4c1f1aff 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test.nr @@ -3,8 +3,9 @@ mod burn; mod utils; mod transfer_public; mod transfer_private; +mod transfer_to_private; +mod transfer_to_public; mod refunds; -mod unshielding; mod minting; mod reading_constants; mod shielding; diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/minting.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/minting.nr index 6d2b21207ae..bc4f877d2a3 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test/minting.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/minting.nr @@ -1,6 +1,6 @@ use crate::test::utils; use crate::{Token, types::transparent_note::TransparentNote}; -use dep::aztec::{hash::compute_secret_hash, oracle::random::random, test::helpers::cheatcodes}; +use dep::aztec::{oracle::random::random, test::helpers::cheatcodes}; #[test] unconstrained fn mint_public_success() { @@ -56,185 +56,3 @@ unconstrained fn mint_public_failures() { utils::check_public_balance(token_contract_address, recipient, mint_for_recipient_amount); utils::check_public_balance(token_contract_address, owner, 0); } - -#[test] -unconstrained fn mint_private_success() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, owner, _) = utils::setup(/* with_account_contracts */ false); - let mint_amount = 10000; - // Mint some tokens - let secret = random(); - let secret_hash = compute_secret_hash(secret); - Token::at(token_contract_address).mint_private(mint_amount, secret_hash).call(&mut env.public()); - - Token::at(token_contract_address).mint_public(owner, mint_amount).call(&mut env.public()); - - // Time travel so we can read keys from the registry - env.advance_block_by(6); - - // We need to manually add the note to TXE because `TransparentNote` does not support automatic note log delivery. - env.add_note( - &mut TransparentNote::new(mint_amount, secret_hash), - Token::storage_layout().pending_shields.slot, - token_contract_address, - ); - - // Redeem our shielded tokens - Token::at(token_contract_address).redeem_shield(owner, mint_amount, secret).call( - &mut env.private(), - ); - - utils::check_private_balance(token_contract_address, owner, mint_amount); -} - -#[test(should_fail_with = "note not popped")] -unconstrained fn mint_private_failure_double_spend() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, owner, recipient) = - utils::setup(/* with_account_contracts */ false); - let mint_amount = 10000; - // Mint some tokens - let secret = random(); - let secret_hash = compute_secret_hash(secret); - Token::at(token_contract_address).mint_private(mint_amount, secret_hash).call(&mut env.public()); - - Token::at(token_contract_address).mint_public(owner, mint_amount).call(&mut env.public()); - - // Time travel so we can read keys from the registry - env.advance_block_by(6); - - // We need to manually add the note to TXE because `TransparentNote` does not support automatic note log delivery. - env.add_note( - &mut TransparentNote::new(mint_amount, secret_hash), - Token::storage_layout().pending_shields.slot, - token_contract_address, - ); - - // Redeem our shielded tokens - Token::at(token_contract_address).redeem_shield(owner, mint_amount, secret).call( - &mut env.private(), - ); - - utils::check_private_balance(token_contract_address, owner, mint_amount); - - // Attempt to double spend - Token::at(token_contract_address).redeem_shield(recipient, mint_amount, secret).call( - &mut env.private(), - ); -} - -#[test(should_fail_with = "caller is not minter")] -unconstrained fn mint_private_failure_non_minter() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, _, recipient) = - utils::setup(/* with_account_contracts */ false); - let mint_amount = 10000; - // Try to mint some tokens impersonating recipient - env.impersonate(recipient); - - let secret = random(); - let secret_hash = compute_secret_hash(secret); - Token::at(token_contract_address).mint_private(mint_amount, secret_hash).call(&mut env.public()); -} - -#[test(should_fail_with = "call to assert_max_bit_size")] -unconstrained fn mint_private_failure_overflow() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, _, _) = utils::setup(/* with_account_contracts */ false); - - // Overflow recipient - let mint_amount = 2.pow_32(128); - let secret = random(); - let secret_hash = compute_secret_hash(secret); - Token::at(token_contract_address).mint_private(mint_amount, secret_hash).call(&mut env.public()); -} - -#[test(should_fail_with = "attempt to add with overflow")] -unconstrained fn mint_private_failure_overflow_recipient() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, owner, _) = utils::setup(/* with_account_contracts */ false); - let mint_amount = 10000; - // Mint some tokens - let secret = random(); - let secret_hash = compute_secret_hash(secret); - Token::at(token_contract_address).mint_private(mint_amount, secret_hash).call(&mut env.public()); - // Time travel so we can read keys from the registry - env.advance_block_by(6); - - // We need to manually add the note to TXE because `TransparentNote` does not support automatic note log delivery. - env.add_note( - &mut TransparentNote::new(mint_amount, secret_hash), - Token::storage_layout().pending_shields.slot, - token_contract_address, - ); - - // Redeem our shielded tokens - Token::at(token_contract_address).redeem_shield(owner, mint_amount, secret).call( - &mut env.private(), - ); - - utils::check_private_balance(token_contract_address, owner, mint_amount); - - let mint_amount = 2.pow_32(128) - mint_amount; - // Mint some tokens - let secret = random(); - let secret_hash = compute_secret_hash(secret); - Token::at(token_contract_address).mint_private(mint_amount, secret_hash).call(&mut env.public()); -} - -#[test(should_fail_with = "attempt to add with overflow")] -unconstrained fn mint_private_failure_overflow_total_supply() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, owner, recipient) = - utils::setup(/* with_account_contracts */ false); - let mint_amount = 10000; - // Mint some tokens - let secret_owner = random(); - let secret_recipient = random(); - let secret_hash_owner = compute_secret_hash(secret_owner); - let secret_hash_recipient = compute_secret_hash(secret_recipient); - - Token::at(token_contract_address).mint_private(mint_amount, secret_hash_owner).call( - &mut env.public(), - ); - Token::at(token_contract_address).mint_private(mint_amount, secret_hash_recipient).call( - &mut env.public(), - ); - - // Time travel so we can read keys from the registry - env.advance_block_by(6); - - // Store 2 notes in the cache so we can redeem it for owner and recipient - env.add_note( - &mut TransparentNote::new(mint_amount, secret_hash_owner), - Token::storage_layout().pending_shields.slot, - token_contract_address, - ); - env.add_note( - &mut TransparentNote::new(mint_amount, secret_hash_recipient), - Token::storage_layout().pending_shields.slot, - token_contract_address, - ); - - // Redeem owner's shielded tokens - env.impersonate(owner); - Token::at(token_contract_address).redeem_shield(owner, mint_amount, secret_owner).call( - &mut env.private(), - ); - - // Redeem recipient's shielded tokens - env.impersonate(recipient); - Token::at(token_contract_address).redeem_shield(recipient, mint_amount, secret_recipient).call( - &mut env.private(), - ); - - utils::check_private_balance(token_contract_address, owner, mint_amount); - utils::check_private_balance(token_contract_address, recipient, mint_amount); - - env.impersonate(owner); - let mint_amount = 2.pow_32(128) - 2 * mint_amount; - // Try to mint some tokens - let secret = random(); - let secret_hash = compute_secret_hash(secret); - Token::at(token_contract_address).mint_private(mint_amount, secret_hash).call(&mut env.public()); -} diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/refunds.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/refunds.nr index e52d6fb44fa..b4238459484 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test/refunds.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/refunds.nr @@ -1,8 +1,6 @@ use crate::{test::utils, Token}; use dep::authwit::cheatcodes as authwit_cheatcodes; -use dep::aztec::{prelude::NoteHeader, protocol_types::storage::map::derive_storage_slot_in_map}; -use dep::uint_note::uint_note::UintNote; use std::test::OracleMock; #[test] @@ -35,34 +33,23 @@ unconstrained fn setup_refund_success() { setup_refund_from_call_interface.call(&mut env.private()); - let fee_payer_balances_slot = - derive_storage_slot_in_map(Token::storage_layout().balances.slot, fee_payer); - let user_balances_slot = - derive_storage_slot_in_map(Token::storage_layout().balances.slot, user); - // When the refund was set up, we would've spent the note worth mint_amount, and inserted a note worth //`mint_amount - funded_amount`. When completing the refund, we would've constructed a hash corresponding to a note // worth `funded_amount - transaction_fee`. We "know" the transaction fee was 1 (it is hardcoded in // `executePublicFunction` TXE oracle) but we need to notify TXE of the note (preimage). - env.add_note( - &mut UintNote { - value: U128::from_integer(funded_amount - 1), - owner: user, - randomness: user_randomness, - header: NoteHeader::empty(), - }, - user_balances_slot, + utils::add_token_note( + env, token_contract_address, + fee_payer, + 1, + fee_payer_randomness, ); - env.add_note( - &mut UintNote { - value: U128::from_integer(1), - owner: fee_payer, - randomness: fee_payer_randomness, - header: NoteHeader::empty(), - }, - fee_payer_balances_slot, + utils::add_token_note( + env, token_contract_address, + user, + funded_amount - 1, + user_randomness, ); utils::check_private_balance(token_contract_address, user, mint_amount - 1); diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_to_private.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_to_private.nr new file mode 100644 index 00000000000..a02f4f3d4a2 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_to_private.nr @@ -0,0 +1,93 @@ +use crate::{test::utils, Token}; +use dep::aztec::{ + keys::getters::get_public_keys, oracle::random::random, + protocol_types::storage::map::derive_storage_slot_in_map, +}; +use std::test::OracleMock; + +/// Internal orchestration means that the calls to `prepare_transfer_to_private` +/// and `finalize_transfer_to_private` are done by the TOKEN contract itself. +/// In this test's case this is done by the `Token::transfer_to_private(...)` function called +/// in `utils::setup_mint_and_transfer_to_private`. +#[test] +unconstrained fn transfer_to_private_internal_orchestration() { + // The transfer to private is done in `utils::setup_and_mint_private` and for this reason + // in this test we just call it and check the outcome. + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (_, token_contract_address, user, _, amount) = + utils::setup_and_mint_private(/* with_account_contracts */ false); + + // User's private balance should be equal to the amount + utils::check_private_balance(token_contract_address, user, amount); +} + +/// External orchestration means that the calls to prepare and finalize are not done by the Token contract. This flow +/// will typically be used by a DEX. +#[test] +unconstrained fn transfer_to_private_external_orchestration() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, _, recipient, amount) = + utils::setup_and_mint_public(/* with_account_contracts */ false); + + let note_randomness = random(); + + // We mock the Oracle to return the note randomness such that later on we can manually add the note + let _ = OracleMock::mock("getRandomField").returns(note_randomness); + + // We prepare the transfer + let hiding_point_slot: Field = Token::at(token_contract_address) + .prepare_transfer_to_private(recipient) + .call(&mut env.private()); + + // Finalize the transfer of the tokens (message sender owns the tokens in public) + Token::at(token_contract_address).finalize_transfer_to_private(amount, hiding_point_slot).call( + &mut env.public(), + ); + + // We need to manually add the note because #8771 has not yet been implemented + utils::add_token_note( + env, + token_contract_address, + recipient, + amount, + note_randomness, + ); + + // Recipient's private balance should be equal to the amount + utils::check_private_balance(token_contract_address, recipient, amount); +} + +#[test(should_fail_with = "transfer not prepared")] +unconstrained fn transfer_to_private_transfer_not_prepared() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, _, _, amount) = + utils::setup_and_mint_public(/* with_account_contracts */ false); + + // Transfer was not prepared so we can use random value for the hiding point slot + let hiding_point_slot = random(); + + // Try finalizing the transfer without preparing it + Token::at(token_contract_address).finalize_transfer_to_private(amount, hiding_point_slot).call( + &mut env.public(), + ); +} + +#[test(should_fail_with = "Assertion failed: attempt to subtract with underflow 'hi == high'")] +unconstrained fn transfer_to_private_failure_not_an_owner() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, _, not_owner, amount) = + utils::setup_and_mint_public(/* with_account_contracts */ false); + + // (For this specific test we could set a random value for the commitment and not do the call to `prepare...` + // as the token balance check is before we use the value but that would made the test less robust against changes + // in the contract.) + let hiding_point_slot: Field = Token::at(token_contract_address) + .prepare_transfer_to_private(not_owner) + .call(&mut env.private()); + + // Try transferring someone else's token balance + env.impersonate(not_owner); + Token::at(token_contract_address).finalize_transfer_to_private(amount, hiding_point_slot).call( + &mut env.public(), + ); +} diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_to_public.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_to_public.nr new file mode 100644 index 00000000000..63f84481c74 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_to_public.nr @@ -0,0 +1,139 @@ +use crate::test::utils; +use crate::Token; +use dep::authwit::cheatcodes as authwit_cheatcodes; +use dep::aztec::oracle::random::random; + +#[test] +unconstrained fn transfer_to_public_on_behalf_of_self() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _, mint_amount) = + utils::setup_and_mint_private(/* with_account_contracts */ false); + + let transfer_to_public_amount = mint_amount / 10; + Token::at(token_contract_address) + .transfer_to_public(owner, owner, transfer_to_public_amount, 0) + .call(&mut env.private()); + utils::check_private_balance( + token_contract_address, + owner, + mint_amount - transfer_to_public_amount, + ); + utils::check_public_balance(token_contract_address, owner, transfer_to_public_amount); +} + +#[test] +unconstrained fn transfer_to_public_on_behalf_of_other() { + let (env, token_contract_address, owner, recipient, mint_amount) = + utils::setup_and_mint_private(/* with_account_contracts */ true); + + let transfer_to_public_amount = mint_amount / 10; + let transfer_to_public_call_interface = Token::at(token_contract_address).transfer_to_public( + owner, + recipient, + transfer_to_public_amount, + 0, + ); + authwit_cheatcodes::add_private_authwit_from_call_interface( + owner, + recipient, + transfer_to_public_call_interface, + ); + // Impersonate recipient + env.impersonate(recipient); + // transfer_to_public tokens + transfer_to_public_call_interface.call(&mut env.private()); + utils::check_private_balance( + token_contract_address, + owner, + mint_amount - transfer_to_public_amount, + ); + utils::check_public_balance(token_contract_address, recipient, transfer_to_public_amount); +} + +#[test(should_fail_with = "Balance too low")] +unconstrained fn transfer_to_public_failure_more_than_balance() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _, mint_amount) = + utils::setup_and_mint_private(/* with_account_contracts */ false); + + let transfer_to_public_amount = mint_amount + 1; + Token::at(token_contract_address) + .transfer_to_public(owner, owner, transfer_to_public_amount, 0) + .call(&mut env.private()); +} + +#[test(should_fail_with = "invalid nonce")] +unconstrained fn transfer_to_public_failure_on_behalf_of_self_non_zero_nonce() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _, mint_amount) = + utils::setup_and_mint_private(/* with_account_contracts */ false); + + let transfer_to_public_amount = mint_amount + 1; + Token::at(token_contract_address) + .transfer_to_public(owner, owner, transfer_to_public_amount, random()) + .call(&mut env.private()); +} + +#[test(should_fail_with = "Balance too low")] +unconstrained fn transfer_to_public_failure_on_behalf_of_other_more_than_balance() { + let (env, token_contract_address, owner, recipient, mint_amount) = + utils::setup_and_mint_private(/* with_account_contracts */ true); + + let transfer_to_public_amount = mint_amount + 1; + let transfer_to_public_call_interface = Token::at(token_contract_address).transfer_to_public( + owner, + recipient, + transfer_to_public_amount, + 0, + ); + authwit_cheatcodes::add_private_authwit_from_call_interface( + owner, + recipient, + transfer_to_public_call_interface, + ); + // Impersonate recipient + env.impersonate(recipient); + // transfer_to_public tokens + transfer_to_public_call_interface.call(&mut env.private()); +} + +#[test(should_fail_with = "Authorization not found for message hash")] +unconstrained fn transfer_to_public_failure_on_behalf_of_other_invalid_designated_caller() { + let (env, token_contract_address, owner, recipient, mint_amount) = + utils::setup_and_mint_private(/* with_account_contracts */ true); + + let transfer_to_public_amount = mint_amount + 1; + let transfer_to_public_call_interface = Token::at(token_contract_address).transfer_to_public( + owner, + recipient, + transfer_to_public_amount, + 0, + ); + authwit_cheatcodes::add_private_authwit_from_call_interface( + owner, + owner, + transfer_to_public_call_interface, + ); + // Impersonate recipient + env.impersonate(recipient); + // transfer_to_public tokens + transfer_to_public_call_interface.call(&mut env.private()); +} + +#[test(should_fail_with = "Authorization not found for message hash")] +unconstrained fn transfer_to_public_failure_on_behalf_of_other_no_approval() { + let (env, token_contract_address, owner, recipient, mint_amount) = + utils::setup_and_mint_private(/* with_account_contracts */ true); + + let transfer_to_public_amount = mint_amount + 1; + let transfer_to_public_call_interface = Token::at(token_contract_address).transfer_to_public( + owner, + recipient, + transfer_to_public_amount, + 0, + ); + // Impersonate recipient + env.impersonate(recipient); + // transfer_to_public tokens + transfer_to_public_call_interface.call(&mut env.private()); +} diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/unshielding.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/unshielding.nr deleted file mode 100644 index 9d7f4f23c1c..00000000000 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test/unshielding.nr +++ /dev/null @@ -1,115 +0,0 @@ -use crate::test::utils; -use crate::Token; -use dep::authwit::cheatcodes as authwit_cheatcodes; -use dep::aztec::oracle::random::random; - -#[test] -unconstrained fn unshield_on_behalf_of_self() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, owner, _, mint_amount) = - utils::setup_and_mint_private(/* with_account_contracts */ false); - - let unshield_amount = mint_amount / 10; - Token::at(token_contract_address).unshield(owner, owner, unshield_amount, 0).call( - &mut env.private(), - ); - utils::check_private_balance(token_contract_address, owner, mint_amount - unshield_amount); - utils::check_public_balance(token_contract_address, owner, unshield_amount); -} - -#[test] -unconstrained fn unshield_on_behalf_of_other() { - let (env, token_contract_address, owner, recipient, mint_amount) = - utils::setup_and_mint_private(/* with_account_contracts */ true); - - let unshield_amount = mint_amount / 10; - let unshield_call_interface = - Token::at(token_contract_address).unshield(owner, recipient, unshield_amount, 0); - authwit_cheatcodes::add_private_authwit_from_call_interface( - owner, - recipient, - unshield_call_interface, - ); - // Impersonate recipient - env.impersonate(recipient); - // Unshield tokens - unshield_call_interface.call(&mut env.private()); - utils::check_private_balance(token_contract_address, owner, mint_amount - unshield_amount); - utils::check_public_balance(token_contract_address, recipient, unshield_amount); -} - -#[test(should_fail_with = "Balance too low")] -unconstrained fn unshield_failure_more_than_balance() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, owner, _, mint_amount) = - utils::setup_and_mint_private(/* with_account_contracts */ false); - - let unshield_amount = mint_amount + 1; - Token::at(token_contract_address).unshield(owner, owner, unshield_amount, 0).call( - &mut env.private(), - ); -} - -#[test(should_fail_with = "invalid nonce")] -unconstrained fn unshield_failure_on_behalf_of_self_non_zero_nonce() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, owner, _, mint_amount) = - utils::setup_and_mint_private(/* with_account_contracts */ false); - - let unshield_amount = mint_amount + 1; - Token::at(token_contract_address).unshield(owner, owner, unshield_amount, random()).call( - &mut env.private(), - ); -} - -#[test(should_fail_with = "Balance too low")] -unconstrained fn unshield_failure_on_behalf_of_other_more_than_balance() { - let (env, token_contract_address, owner, recipient, mint_amount) = - utils::setup_and_mint_private(/* with_account_contracts */ true); - - let unshield_amount = mint_amount + 1; - let unshield_call_interface = - Token::at(token_contract_address).unshield(owner, recipient, unshield_amount, 0); - authwit_cheatcodes::add_private_authwit_from_call_interface( - owner, - recipient, - unshield_call_interface, - ); - // Impersonate recipient - env.impersonate(recipient); - // Unshield tokens - unshield_call_interface.call(&mut env.private()); -} - -#[test(should_fail_with = "Authorization not found for message hash")] -unconstrained fn unshield_failure_on_behalf_of_other_invalid_designated_caller() { - let (env, token_contract_address, owner, recipient, mint_amount) = - utils::setup_and_mint_private(/* with_account_contracts */ true); - - let unshield_amount = mint_amount + 1; - let unshield_call_interface = - Token::at(token_contract_address).unshield(owner, recipient, unshield_amount, 0); - authwit_cheatcodes::add_private_authwit_from_call_interface( - owner, - owner, - unshield_call_interface, - ); - // Impersonate recipient - env.impersonate(recipient); - // Unshield tokens - unshield_call_interface.call(&mut env.private()); -} - -#[test(should_fail_with = "Authorization not found for message hash")] -unconstrained fn unshield_failure_on_behalf_of_other_no_approval() { - let (env, token_contract_address, owner, recipient, mint_amount) = - utils::setup_and_mint_private(/* with_account_contracts */ true); - - let unshield_amount = mint_amount + 1; - let unshield_call_interface = - Token::at(token_contract_address).unshield(owner, recipient, unshield_amount, 0); - // Impersonate recipient - env.impersonate(recipient); - // Unshield tokens - unshield_call_interface.call(&mut env.private()); -} diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr index 34e731443b8..53f894c85b5 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr @@ -1,16 +1,16 @@ -use dep::aztec::{ - hash::compute_secret_hash, +use crate::Token; +use dep::uint_note::uint_note::UintNote; +use aztec::{ oracle::{ execution::{get_block_number, get_contract_address}, random::random, storage::storage_read, }, - prelude::AztecAddress, + prelude::{AztecAddress, NoteHeader}, protocol_types::storage::map::derive_storage_slot_in_map, test::helpers::{cheatcodes, test_environment::TestEnvironment}, }; - -use crate::{Token, types::transparent_note::TransparentNote}; +use std::test::OracleMock; pub unconstrained fn setup( with_account_contracts: bool, @@ -59,28 +59,34 @@ pub unconstrained fn setup_and_mint_public( pub unconstrained fn setup_and_mint_private( with_account_contracts: bool, ) -> (&mut TestEnvironment, AztecAddress, AztecAddress, AztecAddress, Field) { - // Setup + // Setup the tokens and mint public balance let (env, token_contract_address, owner, recipient) = setup(with_account_contracts); - let mint_amount = 10000; + // Mint some tokens - let secret = random(); - let secret_hash = compute_secret_hash(secret); - Token::at(token_contract_address).mint_private(mint_amount, secret_hash).call(&mut env.public()); + let mint_amount = 10000; + mint_private(env, token_contract_address, owner, mint_amount); - // docs:start:txe_test_add_note - // We need to manually add the note to TXE because `TransparentNote` does not support automatic note log delivery. - env.add_note( - &mut TransparentNote::new(mint_amount, secret_hash), - Token::storage_layout().pending_shields.slot, + (env, token_contract_address, owner, recipient, mint_amount) +} + +pub unconstrained fn mint_private( + env: &mut TestEnvironment, + token_contract_address: AztecAddress, + recipient: AztecAddress, + amount: Field, +) { + let note_randomness = random(); + let _ = OracleMock::mock("getRandomField").returns(note_randomness); + + Token::at(token_contract_address).mint_to_private(recipient, amount).call(&mut env.private()); + + add_token_note( + env, token_contract_address, + recipient, + amount, + note_randomness, ); - // docs:end:txe_test_add_note - // Redeem our shielded tokens - Token::at(token_contract_address).redeem_shield(owner, mint_amount, secret).call( - &mut env.private(), - ); - - (env, token_contract_address, owner, recipient, mint_amount) } // docs:start:txe_test_read_public @@ -115,3 +121,29 @@ pub unconstrained fn check_private_balance( cheatcodes::set_contract_address(current_contract_address); } // docs:end:txe_test_call_unconstrained + +// TODO(#8771): We need to manually add the note because in the partial notes flow `notify_created_note_oracle` +// is not called and we don't have a `NoteProcessor` in TXE. +pub unconstrained fn add_token_note( + env: &mut TestEnvironment, + token_contract_address: AztecAddress, + owner: AztecAddress, + amount: Field, + note_randomness: Field, +) { + // docs:start:txe_test_add_note + let balances_owner_slot = + derive_storage_slot_in_map(Token::storage_layout().balances.slot, owner); + + env.add_note( + &mut UintNote { + value: U128::from_integer(amount), + owner: owner, + randomness: note_randomness, + header: NoteHeader::empty(), + }, + balances_owner_slot, + token_contract_address, + ); + // docs:end:txe_test_add_note +} diff --git a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr index f1625dc6119..a9219a45144 100644 --- a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr @@ -115,8 +115,8 @@ contract Uniswap { input_asset_bridge: AztecAddress, input_amount: Field, output_asset_bridge: AztecAddress, - // params for using the unshield approval - nonce_for_unshield_approval: Field, + // params for using the transfer_to_public approval + nonce_for_transfer_to_public_approval: Field, // params for the swap uniswap_fee_tier: Field, // which uniswap tier to use (eg 3000 for 0.3% fee) minimum_output_amount: Field, // minimum output amount to receive (slippage protection for the swap) @@ -133,11 +133,11 @@ contract Uniswap { // Transfer funds to this contract Token::at(input_asset) - .unshield( + .transfer_to_public( context.msg_sender(), context.this_address(), input_amount, - nonce_for_unshield_approval, + nonce_for_transfer_to_public_approval, ) .call(&mut context); diff --git a/noir-projects/noir-protocol-circuits/bootstrap.sh b/noir-projects/noir-protocol-circuits/bootstrap.sh index c90b7e00328..76d20a5f2a4 100755 --- a/noir-projects/noir-protocol-circuits/bootstrap.sh +++ b/noir-projects/noir-protocol-circuits/bootstrap.sh @@ -40,12 +40,14 @@ esac # This value may be too low. # If vk generation fail with an amount of free memory greater than this value then it should be increased. MIN_PARALLEL_VK_GENERATION_MEMORY=500000000 -PARALLEL_VK=${PARALLEL_VK:-true} +PARALLEL_VK=${PARALLEL_VK:-false} if [[ AVAILABLE_MEMORY -gt MIN_PARALLEL_VK_GENERATION_MEMORY ]] && [[ $PARALLEL_VK == "true" ]]; then echo "Generating vks in parallel..." for pathname in "./target"/*.json; do - BB_HASH=$BB_HASH node ../scripts/generate_vk_json.js "$pathname" "./target/keys" & + if [[ $pathname != *"_simulated"* ]]; then + BB_HASH=$BB_HASH node ../scripts/generate_vk_json.js "$pathname" "./target/keys" & + fi done for job in $(jobs -p); do @@ -53,9 +55,11 @@ if [[ AVAILABLE_MEMORY -gt MIN_PARALLEL_VK_GENERATION_MEMORY ]] && [[ $PARALLEL_ done else - echo "System does not have enough memory for parallel vk generation, falling back to sequential" + echo "Generating VKs sequentially..." for pathname in "./target"/*.json; do + if [[ $pathname != *"_simulated"* ]]; then BB_HASH=$BB_HASH node ../scripts/generate_vk_json.js "$pathname" "./target/keys" + fi done fi diff --git a/noir-projects/noir-protocol-circuits/crates/empty-nested/src/main.nr b/noir-projects/noir-protocol-circuits/crates/empty-nested/src/main.nr index 2bd1dda122b..6f594ac47c1 100644 --- a/noir-projects/noir-protocol-circuits/crates/empty-nested/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/empty-nested/src/main.nr @@ -1,6 +1,5 @@ struct Empty {} -#[recursive] fn main(_inputs: Empty) -> pub Empty { assert(true); Empty {} diff --git a/noir-projects/noir-protocol-circuits/crates/parity-base/src/main.nr b/noir-projects/noir-protocol-circuits/crates/parity-base/src/main.nr index 8dbc300f0f3..8fea34b8cf8 100644 --- a/noir-projects/noir-protocol-circuits/crates/parity-base/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/parity-base/src/main.nr @@ -1,6 +1,5 @@ use dep::parity_lib::{BaseParityInputs, ParityPublicInputs}; -#[recursive] fn main(inputs: BaseParityInputs) -> pub ParityPublicInputs { inputs.base_parity_circuit() } diff --git a/noir-projects/noir-protocol-circuits/crates/parity-root/src/main.nr b/noir-projects/noir-protocol-circuits/crates/parity-root/src/main.nr index 05850ad6169..abb7584bc6f 100644 --- a/noir-projects/noir-protocol-circuits/crates/parity-root/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/parity-root/src/main.nr @@ -1,6 +1,5 @@ use dep::parity_lib::{ParityPublicInputs, RootParityInputs}; -#[recursive] fn main(inputs: RootParityInputs) -> pub ParityPublicInputs { inputs.root_parity_circuit() } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-empty/src/main.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-empty/src/main.nr index dbee20b81c0..1eeeb229b1b 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-empty/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-empty/src/main.nr @@ -1,7 +1,6 @@ use dep::private_kernel_lib::PrivateKernelEmptyPrivateInputs; use dep::types::KernelCircuitPublicInputs; -#[recursive] fn main(input: PrivateKernelEmptyPrivateInputs) -> pub KernelCircuitPublicInputs { input.execute() } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-base-private-simulated/src/main.nr b/noir-projects/noir-protocol-circuits/crates/rollup-base-private-simulated/src/main.nr index e2093b6e0c9..69331eba551 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-base-private-simulated/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-base-private-simulated/src/main.nr @@ -1,6 +1,5 @@ use dep::rollup_lib::base::{BaseOrMergeRollupPublicInputs, PrivateBaseRollupInputs}; -#[recursive] unconstrained fn main(inputs: PrivateBaseRollupInputs) -> pub BaseOrMergeRollupPublicInputs { inputs.execute() } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-base-private/src/main.nr b/noir-projects/noir-protocol-circuits/crates/rollup-base-private/src/main.nr index c6dea8ed5bc..839f5b0e6fa 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-base-private/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-base-private/src/main.nr @@ -1,6 +1,5 @@ use dep::rollup_lib::base::{BaseOrMergeRollupPublicInputs, PrivateBaseRollupInputs}; -#[recursive] fn main(inputs: PrivateBaseRollupInputs) -> pub BaseOrMergeRollupPublicInputs { inputs.execute() } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-base-public-simulated/src/main.nr b/noir-projects/noir-protocol-circuits/crates/rollup-base-public-simulated/src/main.nr index 57a1b92ab02..3d93aa20d0e 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-base-public-simulated/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-base-public-simulated/src/main.nr @@ -1,6 +1,5 @@ use dep::rollup_lib::base::{BaseOrMergeRollupPublicInputs, PublicBaseRollupInputs}; -#[recursive] unconstrained fn main(inputs: PublicBaseRollupInputs) -> pub BaseOrMergeRollupPublicInputs { inputs.execute() } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-base-public/src/main.nr b/noir-projects/noir-protocol-circuits/crates/rollup-base-public/src/main.nr index 30aad3b7daf..e1e2618162a 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-base-public/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-base-public/src/main.nr @@ -1,6 +1,5 @@ use dep::rollup_lib::base::{BaseOrMergeRollupPublicInputs, PublicBaseRollupInputs}; -#[recursive] fn main(inputs: PublicBaseRollupInputs) -> pub BaseOrMergeRollupPublicInputs { inputs.execute() } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-block-root-empty/src/main.nr b/noir-projects/noir-protocol-circuits/crates/rollup-block-root-empty/src/main.nr index 459ebf947e1..e1dcc617997 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-block-root-empty/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-block-root-empty/src/main.nr @@ -1,6 +1,5 @@ use rollup_lib::block_root::{BlockRootOrBlockMergePublicInputs, EmptyBlockRootRollupInputs}; -#[recursive] fn main(inputs: EmptyBlockRootRollupInputs) -> pub BlockRootOrBlockMergePublicInputs { inputs.empty_block_root_rollup_circuit() } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-block-root/src/main.nr b/noir-projects/noir-protocol-circuits/crates/rollup-block-root/src/main.nr index 33bcb5c457e..cb8caaea834 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-block-root/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-block-root/src/main.nr @@ -1,6 +1,5 @@ use dep::rollup_lib::block_root::{BlockRootOrBlockMergePublicInputs, BlockRootRollupInputs}; -#[recursive] fn main(inputs: BlockRootRollupInputs) -> pub BlockRootOrBlockMergePublicInputs { inputs.block_root_rollup_circuit() } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-merge/src/main.nr b/noir-projects/noir-protocol-circuits/crates/rollup-merge/src/main.nr index 108aadfa738..3b3883d6b84 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-merge/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-merge/src/main.nr @@ -1,6 +1,5 @@ use dep::rollup_lib::merge::{BaseOrMergeRollupPublicInputs, MergeRollupInputs}; -#[recursive] fn main(inputs: MergeRollupInputs) -> pub BaseOrMergeRollupPublicInputs { inputs.merge_rollup_circuit() } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index d132ebc7a8b..b15dd179f17 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -133,6 +133,7 @@ global FUNCTION_SELECTOR_NUM_BYTES: Field = 4; // to be large enough so that it's ensured that it doesn't collide with storage slots of other variables. global INITIALIZATION_SLOT_SEPARATOR: Field = 1000_000_000; global INITIAL_L2_BLOCK_NUM: Field = 1; +global PRIVATE_LOG_SIZE_IN_BYTES: u32 = 576; // This is currently defined by aztec-nr/aztec/src/encrypted_logs/payload.nr. See the comment there for how this value is calculated. global BLOB_SIZE_IN_BYTES: Field = 31 * 4096; global ETHEREUM_SLOT_DURATION: u32 = 12; // AZTEC_SLOT_DURATION should be a multiple of ETHEREUM_SLOT_DURATION diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/indexed_tagging_secret.nr b/noir-projects/noir-protocol-circuits/crates/types/src/indexed_tagging_secret.nr index 1685fdf38e8..3ea0310ef92 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/indexed_tagging_secret.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/indexed_tagging_secret.nr @@ -1,10 +1,20 @@ -use crate::traits::Deserialize; +use crate::traits::{Deserialize, Serialize}; +use super::{address::aztec_address::AztecAddress, hash::poseidon2_hash}; use std::meta::derive; -pub global INDEXED_TAGGING_SECRET_LENGTH: u32 = 2; +pub global INDEXED_TAGGING_SECRET_LENGTH: u32 = 3; -#[derive(Deserialize)] +#[derive(Serialize, Deserialize)] pub struct IndexedTaggingSecret { secret: Field, + recipient: AztecAddress, index: u32, } + +impl IndexedTaggingSecret { + pub fn compute_tag(self) -> Field { + poseidon2_hash( + [self.secret, self.recipient.to_field(), self.index as Field], + ) + } +} diff --git a/noir-projects/scripts/generate_vk_json.js b/noir-projects/scripts/generate_vk_json.js index f0538f6bf2d..47aebef30d7 100644 --- a/noir-projects/scripts/generate_vk_json.js +++ b/noir-projects/scripts/generate_vk_json.js @@ -82,16 +82,26 @@ function getBarretenbergHash() { }); } -function generateArtifactHash(barretenbergHash, bytecodeHash, isMegaHonk) { +function generateArtifactHash( + barretenbergHash, + bytecodeHash, + isMegaHonk, + isRecursive +) { return `${barretenbergHash}-${bytecodeHash}-${ isMegaHonk ? "mega-honk" : "ultra-honk" - }`; + }-${isRecursive}`; } -async function getArtifactHash(artifactPath, isMegaHonk) { +async function getArtifactHash(artifactPath, isMegaHonk, isRecursive) { const bytecodeHash = await getBytecodeHash(artifactPath); const barretenbergHash = await getBarretenbergHash(); - return generateArtifactHash(barretenbergHash, bytecodeHash, isMegaHonk); + return generateArtifactHash( + barretenbergHash, + bytecodeHash, + isMegaHonk, + isRecursive + ); } async function hasArtifactHashChanged(artifactHash, vkDataPath) { @@ -125,8 +135,13 @@ async function processArtifact( syncWithS3 ) { const isMegaHonk = isMegaHonkCircuit(artifactName); + const isRecursive = true; - const artifactHash = await getArtifactHash(artifactPath, isMegaHonk); + const artifactHash = await getArtifactHash( + artifactPath, + isMegaHonk, + isRecursive + ); const vkDataPath = vkDataFileNameForArtifactName(outputFolder, artifactName); @@ -145,7 +160,8 @@ async function processArtifact( outputFolder, artifactPath, artifactHash, - isMegaHonk + isMegaHonk, + isRecursive ); if (syncWithS3) { await writeVKToS3(artifactName, artifactHash, JSON.stringify(vkData)); @@ -162,7 +178,8 @@ async function generateVKData( outputFolder, artifactPath, artifactHash, - isMegaHonk + isMegaHonk, + isRecursive ) { if (isMegaHonk) { console.log("Generating new mega honk vk for", artifactName); @@ -178,7 +195,12 @@ async function generateVKData( const writeVkCommand = `${BB_BIN_PATH} ${ isMegaHonk ? "write_vk_mega_honk" : "write_vk_ultra_honk" - } -h -b "${artifactPath}" -o "${binaryVkPath}"`; + } -h -b "${artifactPath}" -o "${binaryVkPath}" ${ + isRecursive ? "--recursive" : "" + }`; + + console.log("WRITE VK CMD: ", writeVkCommand); + const vkAsFieldsCommand = `${BB_BIN_PATH} ${ isMegaHonk ? "vk_as_fields_mega_honk" : "vk_as_fields_ultra_honk" } -k "${binaryVkPath}" -o "${jsonVkPath}"`; diff --git a/noir/README.md b/noir/README.md index c604644e712..44d0babbf2c 100644 --- a/noir/README.md +++ b/noir/README.md @@ -15,6 +15,6 @@ To start the sync run [this action](https://github.com/AztecProtocol/aztec-packa ## Syncing from aztec-packages to Noir. -When syncing from aztec-packages to Noir it's important to check that the latest release of `bb` uses the same ACIR serialization format as the current master commit. This is because Noir uses a released version of barretenberg rather than being developed in sync with it, it's then not possible to sync if there's been serialization changes since the last release. +When syncing from aztec-packages to Noir it's important to check that the latest release of `bb` uses the same ACIR serialization format as the current master commit. This is because Noir uses a released version of barretenberg rather than being developed in sync with it, it's then not possible to sync if there's been serialization changes since the latest release. To start the sync run [this action](https://github.com/AztecProtocol/aztec-packages/actions/workflows/mirror-noir-subrepo.yml) manually (click the "Run Workflow" button in the top right). aztec-bot will then open a new PR in the `noir-lang/noir` repository which does the initial sync, this will have merge conflicts with master which will need to be resolved. diff --git a/noir/noir-repo/acvm-repo/acir/benches/serialization.rs b/noir/noir-repo/acvm-repo/acir/benches/serialization.rs index 792200c8912..dd6a5c8b1cf 100644 --- a/noir/noir-repo/acvm-repo/acir/benches/serialization.rs +++ b/noir/noir-repo/acvm-repo/acir/benches/serialization.rs @@ -38,7 +38,6 @@ fn sample_program(num_opcodes: usize) -> Program { public_parameters: PublicInputs(BTreeSet::from([Witness(5)])), return_values: PublicInputs(BTreeSet::from([Witness(6)])), assert_messages: Vec::new(), - recursive: false, }], unconstrained_functions: Vec::new(), } diff --git a/noir/noir-repo/acvm-repo/acir/codegen/acir.cpp b/noir/noir-repo/acvm-repo/acir/codegen/acir.cpp index 0880b5a0cbe..8fa471e57ba 100644 --- a/noir/noir-repo/acvm-repo/acir/codegen/acir.cpp +++ b/noir/noir-repo/acvm-repo/acir/codegen/acir.cpp @@ -1216,24 +1216,8 @@ namespace Program { }; struct AssertionPayload { - - struct StaticString { - std::string value; - - friend bool operator==(const StaticString&, const StaticString&); - std::vector bincodeSerialize() const; - static StaticString bincodeDeserialize(std::vector); - }; - - struct Dynamic { - std::tuple> value; - - friend bool operator==(const Dynamic&, const Dynamic&); - std::vector bincodeSerialize() const; - static Dynamic bincodeDeserialize(std::vector); - }; - - std::variant value; + uint64_t error_selector; + std::vector payload; friend bool operator==(const AssertionPayload&, const AssertionPayload&); std::vector bincodeSerialize() const; @@ -1305,7 +1289,6 @@ namespace Program { Program::PublicInputs public_parameters; Program::PublicInputs return_values; std::vector> assert_messages; - bool recursive; friend bool operator==(const Circuit&, const Circuit&); std::vector bincodeSerialize() const; @@ -1335,7 +1318,8 @@ namespace Program { namespace Program { inline bool operator==(const AssertionPayload &lhs, const AssertionPayload &rhs) { - if (!(lhs.value == rhs.value)) { return false; } + if (!(lhs.error_selector == rhs.error_selector)) { return false; } + if (!(lhs.payload == rhs.payload)) { return false; } return true; } @@ -1360,7 +1344,8 @@ template <> template void serde::Serializable::serialize(const Program::AssertionPayload &obj, Serializer &serializer) { serializer.increase_container_depth(); - serde::Serializable::serialize(obj.value, serializer); + serde::Serializable::serialize(obj.error_selector, serializer); + serde::Serializable::serialize(obj.payload, serializer); serializer.decrease_container_depth(); } @@ -1369,87 +1354,12 @@ template Program::AssertionPayload serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); Program::AssertionPayload obj; - obj.value = serde::Deserializable::deserialize(deserializer); + obj.error_selector = serde::Deserializable::deserialize(deserializer); + obj.payload = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Program { - - inline bool operator==(const AssertionPayload::StaticString &lhs, const AssertionPayload::StaticString &rhs) { - if (!(lhs.value == rhs.value)) { return false; } - return true; - } - - inline std::vector AssertionPayload::StaticString::bincodeSerialize() const { - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); - } - - inline AssertionPayload::StaticString AssertionPayload::StaticString::bincodeDeserialize(std::vector input) { - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw serde::deserialization_error("Some input bytes were not read"); - } - return value; - } - -} // end of namespace Program - -template <> -template -void serde::Serializable::serialize(const Program::AssertionPayload::StaticString &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.value, serializer); -} - -template <> -template -Program::AssertionPayload::StaticString serde::Deserializable::deserialize(Deserializer &deserializer) { - Program::AssertionPayload::StaticString obj; - obj.value = serde::Deserializable::deserialize(deserializer); - return obj; -} - -namespace Program { - - inline bool operator==(const AssertionPayload::Dynamic &lhs, const AssertionPayload::Dynamic &rhs) { - if (!(lhs.value == rhs.value)) { return false; } - return true; - } - - inline std::vector AssertionPayload::Dynamic::bincodeSerialize() const { - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); - } - - inline AssertionPayload::Dynamic AssertionPayload::Dynamic::bincodeDeserialize(std::vector input) { - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw serde::deserialization_error("Some input bytes were not read"); - } - return value; - } - -} // end of namespace Program - -template <> -template -void serde::Serializable::serialize(const Program::AssertionPayload::Dynamic &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.value, serializer); -} - -template <> -template -Program::AssertionPayload::Dynamic serde::Deserializable::deserialize(Deserializer &deserializer) { - Program::AssertionPayload::Dynamic obj; - obj.value = serde::Deserializable::deserialize(deserializer); - return obj; -} - namespace Program { inline bool operator==(const BinaryFieldOp &lhs, const BinaryFieldOp &rhs) { @@ -5589,7 +5499,6 @@ namespace Program { if (!(lhs.public_parameters == rhs.public_parameters)) { return false; } if (!(lhs.return_values == rhs.return_values)) { return false; } if (!(lhs.assert_messages == rhs.assert_messages)) { return false; } - if (!(lhs.recursive == rhs.recursive)) { return false; } return true; } @@ -5621,7 +5530,6 @@ void serde::Serializable::serialize(const Program::Circuit &ob serde::Serializable::serialize(obj.public_parameters, serializer); serde::Serializable::serialize(obj.return_values, serializer); serde::Serializable::serialize(obj.assert_messages, serializer); - serde::Serializable::serialize(obj.recursive, serializer); serializer.decrease_container_depth(); } @@ -5637,7 +5545,6 @@ Program::Circuit serde::Deserializable::deserialize(Deserializ obj.public_parameters = serde::Deserializable::deserialize(deserializer); obj.return_values = serde::Deserializable::deserialize(deserializer); obj.assert_messages = serde::Deserializable::deserialize(deserializer); - obj.recursive = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs index f700fefe0cd..12990f66175 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs @@ -68,11 +68,6 @@ pub struct Circuit { // c++ code at the moment when it is, due to OpcodeLocation needing a comparison // implementation which is never generated. pub assert_messages: Vec<(OpcodeLocation, AssertionPayload)>, - - /// States whether the backend should use a SNARK recursion friendly prover. - /// If implemented by a backend, this means that proofs generated with this circuit - /// will be friendly for recursively verifying inside of another SNARK. - pub recursive: bool, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -82,9 +77,9 @@ pub enum ExpressionOrMemory { } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub enum AssertionPayload { - StaticString(String), - Dynamic(/* error_selector */ u64, Vec>), +pub struct AssertionPayload { + pub error_selector: u64, + pub payload: Vec>, } #[derive(Debug, Copy, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)] @@ -120,12 +115,6 @@ impl<'de> Deserialize<'de> for ErrorSelector { } } -/// This selector indicates that the payload is a string. -/// This is used to parse any error with a string payload directly, -/// to avoid users having to parse the error externally to the ACVM. -/// Only non-string errors need to be parsed externally to the ACVM using the circuit ABI. -pub const STRING_ERROR_SELECTOR: ErrorSelector = ErrorSelector(0); - #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] pub struct RawAssertionPayload { pub selector: ErrorSelector, @@ -445,7 +434,6 @@ mod tests { public_parameters: PublicInputs(BTreeSet::from_iter(vec![Witness(2), Witness(12)])), return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(4), Witness(12)])), assert_messages: Default::default(), - recursive: false, }; let program = Program { functions: vec![circuit], unconstrained_functions: Vec::new() }; @@ -481,7 +469,6 @@ mod tests { public_parameters: PublicInputs(BTreeSet::from_iter(vec![Witness(2)])), return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(2)])), assert_messages: Default::default(), - recursive: false, }; let program = Program { functions: vec![circuit], unconstrained_functions: Vec::new() }; diff --git a/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs b/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs index a915cb95d07..9ab153b9f61 100644 --- a/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs +++ b/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs @@ -47,11 +47,11 @@ fn addition_circuit() { let expected_serialization: Vec = vec![ 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 144, 65, 14, 128, 32, 12, 4, 65, 124, 80, 75, 91, - 104, 111, 126, 69, 34, 252, 255, 9, 106, 228, 64, 162, 55, 153, 164, 217, 158, 38, 155, - 245, 238, 97, 189, 206, 187, 55, 161, 231, 214, 19, 254, 129, 126, 162, 107, 25, 92, 4, - 137, 185, 230, 88, 145, 112, 135, 104, 69, 5, 88, 74, 82, 84, 20, 149, 35, 42, 81, 85, 214, - 108, 197, 50, 24, 50, 85, 108, 98, 212, 186, 44, 204, 235, 5, 183, 99, 233, 46, 63, 252, - 110, 216, 56, 184, 15, 78, 146, 74, 173, 20, 141, 1, 0, 0, + 104, 111, 126, 69, 34, 252, 255, 9, 106, 228, 64, 194, 81, 38, 105, 182, 167, 201, 102, + 189, 251, 216, 159, 243, 110, 38, 244, 60, 122, 194, 63, 208, 47, 116, 109, 131, 139, 32, + 49, 215, 28, 43, 18, 158, 16, 173, 168, 0, 75, 73, 138, 138, 162, 114, 69, 37, 170, 202, + 154, 173, 88, 6, 67, 166, 138, 77, 140, 90, 151, 133, 117, 189, 224, 117, 108, 221, 229, + 135, 223, 13, 27, 135, 121, 106, 119, 3, 58, 173, 124, 163, 140, 1, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -91,10 +91,10 @@ fn multi_scalar_mul_circuit() { let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 77, 9, 14, 0, 32, 8, 202, 171, 227, 255, 255, 109, - 217, 162, 141, 114, 99, 2, 162, 74, 57, 53, 18, 2, 46, 208, 70, 122, 99, 162, 43, 113, 35, - 239, 102, 157, 230, 1, 94, 19, 45, 209, 145, 11, 202, 43, 238, 56, 249, 133, 254, 255, 187, - 79, 45, 204, 84, 220, 246, 193, 0, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 77, 9, 10, 0, 32, 8, 243, 236, 248, 255, 127, 35, + 163, 5, 35, 97, 184, 205, 169, 42, 183, 102, 65, 193, 21, 218, 73, 31, 44, 116, 35, 238, + 228, 189, 108, 208, 60, 193, 91, 161, 23, 6, 114, 73, 121, 195, 157, 32, 95, 232, 255, 191, + 203, 181, 1, 243, 231, 24, 106, 192, 0, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -134,24 +134,24 @@ fn schnorr_verify_circuit() { let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 85, 211, 103, 78, 2, 81, 24, 70, 225, 193, 130, 96, 239, - 189, 96, 239, 189, 35, 34, 34, 34, 34, 238, 130, 253, 47, 129, 192, 9, 223, 36, 7, 146, - 201, 60, 209, 31, 144, 123, 207, 155, 73, 250, 159, 118, 239, 201, 132, 121, 103, 227, 205, - 211, 137, 247, 144, 60, 220, 123, 114, 225, 17, 121, 84, 206, 202, 99, 114, 78, 206, 203, - 227, 242, 132, 60, 41, 79, 201, 211, 242, 140, 60, 43, 207, 201, 243, 242, 130, 188, 40, - 47, 201, 203, 242, 138, 188, 42, 175, 201, 235, 242, 134, 188, 41, 111, 201, 219, 242, 142, - 92, 144, 119, 229, 61, 121, 95, 62, 144, 15, 229, 35, 249, 88, 62, 145, 79, 229, 51, 249, - 92, 190, 144, 47, 229, 43, 249, 90, 190, 145, 111, 229, 59, 249, 94, 126, 144, 31, 229, 39, - 249, 89, 126, 145, 95, 229, 162, 252, 38, 151, 228, 119, 185, 44, 127, 200, 21, 249, 83, - 174, 134, 233, 52, 137, 191, 125, 233, 255, 53, 249, 91, 174, 203, 63, 114, 67, 254, 149, - 155, 242, 159, 220, 10, 255, 199, 247, 183, 244, 59, 216, 38, 155, 100, 139, 108, 144, 237, - 165, 155, 203, 199, 111, 102, 83, 108, 137, 13, 177, 29, 54, 195, 86, 216, 8, 219, 96, 19, - 108, 129, 13, 208, 62, 205, 211, 58, 141, 211, 54, 77, 211, 50, 13, 211, 46, 205, 22, 146, - 126, 163, 180, 73, 147, 180, 72, 131, 180, 71, 115, 180, 70, 99, 180, 69, 83, 180, 68, 67, - 180, 67, 51, 180, 66, 35, 180, 65, 19, 180, 64, 3, 220, 61, 119, 206, 93, 115, 199, 197, - 184, 211, 82, 220, 97, 57, 238, 172, 18, 119, 84, 141, 187, 168, 197, 217, 215, 227, 172, - 27, 113, 182, 205, 56, 203, 244, 204, 210, 115, 75, 116, 158, 3, 159, 46, 43, 32, 188, 53, - 25, 5, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 211, 103, 78, 2, 81, 24, 70, 225, 193, 130, 96, 239, + 189, 96, 239, 189, 35, 34, 34, 34, 82, 118, 193, 254, 151, 64, 224, 132, 111, 146, 67, 50, + 153, 39, 250, 3, 114, 239, 121, 51, 201, 240, 211, 29, 60, 153, 48, 239, 108, 188, 121, + 122, 241, 30, 145, 71, 7, 79, 46, 60, 38, 143, 203, 89, 121, 66, 206, 201, 121, 121, 82, + 158, 146, 167, 229, 25, 121, 86, 158, 147, 231, 229, 5, 121, 81, 94, 146, 151, 229, 21, + 121, 85, 94, 147, 215, 229, 13, 121, 83, 222, 146, 183, 229, 29, 121, 87, 222, 147, 11, + 242, 190, 124, 32, 31, 202, 71, 242, 177, 124, 34, 159, 202, 103, 242, 185, 124, 33, 95, + 202, 87, 242, 181, 124, 35, 223, 202, 119, 242, 189, 252, 32, 63, 202, 79, 242, 179, 252, + 34, 191, 202, 111, 242, 187, 92, 148, 63, 228, 146, 252, 41, 151, 229, 47, 185, 34, 127, + 203, 213, 48, 157, 38, 241, 183, 31, 253, 191, 38, 255, 202, 117, 249, 79, 110, 200, 255, + 114, 83, 110, 201, 237, 112, 39, 190, 191, 173, 223, 193, 54, 217, 36, 91, 100, 131, 108, + 47, 221, 92, 62, 126, 51, 155, 98, 75, 108, 136, 237, 176, 25, 182, 194, 70, 216, 6, 155, + 96, 11, 108, 128, 246, 105, 158, 214, 105, 156, 182, 105, 154, 150, 105, 152, 118, 105, + 182, 144, 12, 27, 165, 77, 154, 164, 69, 26, 164, 61, 154, 163, 53, 26, 163, 45, 154, 162, + 37, 26, 162, 29, 154, 161, 21, 26, 161, 13, 154, 160, 5, 26, 224, 238, 185, 115, 238, 154, + 59, 46, 198, 157, 150, 226, 14, 203, 113, 103, 149, 184, 163, 106, 220, 69, 45, 206, 190, + 30, 103, 221, 136, 179, 109, 198, 89, 166, 103, 150, 158, 91, 162, 243, 244, 167, 15, 14, + 161, 226, 6, 24, 5, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -214,12 +214,12 @@ fn simple_brillig_foreign_call() { let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 79, 73, 10, 128, 48, 12, 204, 40, 46, 5, 111, 126, - 36, 254, 192, 207, 120, 240, 226, 65, 196, 247, 91, 48, 129, 80, 218, 122, 48, 3, 33, 147, - 9, 89, 6, 244, 98, 140, 1, 225, 157, 100, 173, 45, 84, 91, 37, 243, 63, 44, 240, 219, 197, - 246, 223, 38, 37, 176, 34, 85, 156, 169, 251, 144, 233, 183, 142, 206, 67, 114, 215, 121, - 63, 15, 84, 135, 222, 157, 98, 244, 194, 247, 227, 222, 206, 11, 31, 19, 165, 186, 164, - 207, 153, 222, 3, 91, 101, 84, 220, 120, 2, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 79, 73, 10, 128, 48, 12, 236, 40, 46, 5, 111, 126, + 36, 254, 192, 207, 120, 240, 226, 65, 196, 247, 91, 48, 129, 80, 186, 28, 154, 129, 144, + 201, 132, 44, 3, 247, 99, 14, 1, 230, 3, 103, 169, 53, 68, 219, 57, 83, 27, 54, 216, 237, + 34, 253, 111, 23, 19, 104, 177, 96, 76, 204, 251, 68, 191, 55, 52, 238, 163, 187, 198, 251, + 105, 114, 101, 200, 221, 37, 196, 200, 252, 188, 222, 227, 126, 80, 153, 200, 213, 57, 125, + 77, 244, 62, 112, 171, 6, 33, 119, 2, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -344,17 +344,17 @@ fn complex_brillig_foreign_call() { let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 85, 93, 10, 194, 48, 12, 78, 155, 233, 54, 240, - 205, 11, 8, 122, 128, 76, 47, 176, 187, 136, 111, 138, 62, 122, 124, 45, 75, 88, 23, 139, - 19, 76, 64, 63, 24, 89, 75, 242, 229, 159, 6, 24, 208, 60, 191, 192, 255, 11, 150, 145, - 101, 186, 71, 152, 66, 116, 123, 150, 244, 29, 186, 96, 199, 69, 94, 49, 198, 63, 136, 17, - 29, 98, 132, 172, 255, 63, 216, 111, 203, 190, 152, 214, 15, 11, 251, 83, 193, 176, 95, 75, - 62, 215, 44, 27, 93, 232, 100, 20, 225, 117, 241, 38, 144, 233, 105, 149, 4, 229, 185, 183, - 201, 232, 208, 42, 191, 198, 252, 36, 213, 216, 192, 103, 249, 250, 228, 185, 39, 225, 71, - 23, 126, 234, 132, 191, 114, 234, 83, 173, 234, 149, 231, 146, 251, 93, 193, 56, 129, 199, - 235, 229, 118, 62, 221, 177, 96, 170, 205, 19, 182, 234, 188, 43, 148, 108, 142, 67, 144, - 63, 52, 239, 244, 67, 65, 127, 206, 102, 13, 227, 56, 201, 195, 246, 0, 155, 0, 46, 128, - 245, 6, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 85, 93, 10, 194, 48, 12, 78, 219, 233, 54, 240, + 205, 11, 8, 122, 128, 76, 47, 176, 187, 136, 111, 138, 62, 122, 124, 45, 75, 88, 140, 197, + 9, 38, 224, 62, 24, 89, 75, 242, 229, 159, 6, 24, 208, 60, 191, 64, 255, 11, 146, 145, 100, + 190, 79, 240, 10, 214, 237, 73, 226, 111, 232, 130, 29, 23, 122, 197, 24, 103, 16, 99, 114, + 136, 17, 68, 255, 255, 176, 223, 150, 125, 49, 173, 95, 42, 236, 79, 5, 195, 126, 45, 233, + 92, 147, 108, 116, 161, 179, 81, 132, 247, 197, 147, 224, 225, 105, 149, 4, 229, 184, 183, + 73, 232, 208, 42, 191, 198, 252, 200, 197, 216, 192, 119, 249, 250, 228, 185, 71, 230, 79, + 46, 252, 216, 49, 127, 229, 212, 167, 90, 213, 75, 230, 34, 253, 174, 96, 28, 192, 227, + 245, 114, 59, 159, 238, 169, 96, 170, 205, 51, 182, 234, 188, 43, 148, 108, 138, 131, 33, + 223, 153, 79, 250, 161, 160, 63, 101, 179, 134, 113, 156, 248, 93, 123, 0, 142, 67, 44, + 107, 244, 6, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -393,10 +393,10 @@ fn memory_op_circuit() { let expected_serialization: Vec = vec![ 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 82, 65, 10, 0, 32, 8, 211, 180, 255, 216, 15, 250, - 255, 171, 10, 82, 176, 232, 150, 30, 26, 200, 118, 144, 49, 135, 8, 11, 117, 14, 169, 102, - 229, 162, 140, 78, 219, 206, 137, 174, 44, 111, 104, 217, 190, 24, 236, 75, 113, 94, 146, - 93, 174, 252, 86, 46, 71, 223, 78, 46, 104, 129, 253, 155, 45, 60, 195, 5, 3, 89, 11, 161, - 73, 39, 3, 0, 0, + 255, 171, 10, 82, 176, 58, 166, 135, 6, 178, 29, 100, 204, 33, 194, 66, 157, 67, 170, 89, + 185, 40, 163, 211, 182, 115, 162, 43, 203, 27, 90, 182, 47, 6, 251, 82, 156, 151, 100, 151, + 43, 191, 149, 203, 209, 183, 147, 11, 90, 96, 255, 102, 11, 207, 112, 99, 0, 192, 100, 38, + 199, 38, 3, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -495,15 +495,15 @@ fn nested_acir_call_circuit() { let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 205, 146, 97, 10, 195, 32, 12, 133, 163, 66, 207, 147, - 24, 173, 241, 223, 174, 50, 153, 189, 255, 17, 214, 177, 148, 57, 17, 250, 99, 14, 250, - 224, 97, 144, 16, 146, 143, 231, 224, 45, 167, 126, 105, 217, 109, 118, 91, 248, 200, 168, - 225, 248, 63, 107, 114, 208, 233, 104, 188, 233, 139, 191, 137, 108, 51, 139, 113, 13, 161, - 38, 95, 137, 233, 142, 62, 23, 137, 24, 98, 89, 133, 132, 162, 196, 135, 23, 230, 42, 65, - 82, 46, 57, 97, 166, 192, 149, 182, 152, 121, 211, 97, 110, 222, 94, 8, 13, 132, 182, 54, - 48, 144, 235, 8, 254, 11, 22, 76, 132, 101, 231, 237, 229, 23, 189, 213, 54, 119, 15, 83, - 212, 199, 172, 175, 191, 226, 102, 96, 140, 251, 202, 84, 13, 204, 141, 224, 25, 176, 161, - 158, 53, 121, 144, 73, 14, 4, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 146, 81, 10, 195, 48, 8, 134, 77, 132, 158, 71, 99, + 210, 152, 183, 93, 101, 97, 233, 253, 143, 176, 142, 165, 44, 100, 133, 62, 52, 125, 232, + 7, 63, 138, 136, 232, 143, 8, 95, 176, 234, 195, 180, 202, 172, 178, 240, 195, 84, 193, 86, + 63, 106, 66, 232, 216, 26, 31, 53, 210, 57, 216, 54, 179, 132, 102, 239, 75, 116, 133, 133, + 159, 228, 82, 214, 64, 62, 228, 89, 89, 57, 104, 120, 57, 21, 41, 234, 53, 166, 156, 34, + 37, 246, 82, 120, 9, 73, 150, 58, 12, 199, 237, 69, 208, 152, 208, 230, 6, 254, 193, 206, + 192, 171, 188, 130, 129, 94, 217, 113, 123, 185, 169, 222, 106, 155, 187, 119, 159, 168, + 255, 178, 62, 199, 174, 102, 110, 102, 170, 129, 177, 15, 120, 228, 215, 30, 111, 39, 140, + 108, 64, 11, 4, 0, 0, ]; assert_eq!(bytes, expected_serialization); } diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs index 3570a36a7e7..f9c715a277f 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs @@ -191,7 +191,6 @@ mod tests { public_parameters: PublicInputs::default(), return_values: PublicInputs::default(), assert_messages: Default::default(), - recursive: false, } } diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs index dffa45dbd7a..a5f5783478e 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs @@ -6,7 +6,6 @@ use acir::{ brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, opcodes::BlockId, ErrorSelector, OpcodeLocation, RawAssertionPayload, ResolvedAssertionPayload, - STRING_ERROR_SELECTOR, }, native_types::WitnessMap, AcirField, @@ -307,25 +306,10 @@ fn extract_failure_payload_from_memory( .expect("Error selector is not u64"), ); - match error_selector { - STRING_ERROR_SELECTOR => { - // If the error selector is 0, it means the error is a string - let string = revert_values_iter - .map(|&memory_value| { - let as_u8: u8 = memory_value.try_into().expect("String item is not u8"); - as_u8 as char - }) - .collect(); - Some(ResolvedAssertionPayload::String(string)) - } - _ => { - // If the error selector is not 0, it means the error is a custom error - Some(ResolvedAssertionPayload::Raw(RawAssertionPayload { - selector: error_selector, - data: revert_values_iter.map(|value| value.to_field()).collect(), - })) - } - } + Some(ResolvedAssertionPayload::Raw(RawAssertionPayload { + selector: error_selector, + data: revert_values_iter.map(|value| value.to_field()).collect(), + })) } } diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs index fa3498da613..e2b9ef1defa 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs @@ -10,7 +10,7 @@ use acir::{ AcirFunctionId, BlockId, ConstantOrWitnessEnum, FunctionInput, InvalidInputBitSize, }, AssertionPayload, ErrorSelector, ExpressionOrMemory, Opcode, OpcodeLocation, - RawAssertionPayload, ResolvedAssertionPayload, STRING_ERROR_SELECTOR, + RawAssertionPayload, ResolvedAssertionPayload, }, native_types::{Expression, Witness, WitnessMap}, AcirField, BlackBoxFunc, @@ -445,59 +445,32 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver> ACVM<'a, F, B> { &self, location: OpcodeLocation, ) -> Option> { - let (_, found_assertion_payload) = + let (_, assertion_descriptor) = self.assertion_payloads.iter().find(|(loc, _)| location == *loc)?; - match found_assertion_payload { - AssertionPayload::StaticString(string) => { - Some(ResolvedAssertionPayload::String(string.clone())) - } - AssertionPayload::Dynamic(error_selector, expression) => { - let mut fields = vec![]; - for expr in expression { - match expr { - ExpressionOrMemory::Expression(expr) => { - let value = get_value(expr, &self.witness_map).ok()?; - fields.push(value); - } - ExpressionOrMemory::Memory(block_id) => { - let memory_block = self.block_solvers.get(block_id)?; - fields.extend((0..memory_block.block_len).map(|memory_index| { - *memory_block - .block_value - .get(&memory_index) - .expect("All memory is initialized on creation") - })); - } - } + let mut fields = Vec::new(); + for expr in assertion_descriptor.payload.iter() { + match expr { + ExpressionOrMemory::Expression(expr) => { + let value = get_value(expr, &self.witness_map).ok()?; + fields.push(value); + } + ExpressionOrMemory::Memory(block_id) => { + let memory_block = self.block_solvers.get(block_id)?; + fields.extend((0..memory_block.block_len).map(|memory_index| { + *memory_block + .block_value + .get(&memory_index) + .expect("All memory is initialized on creation") + })); } - let error_selector = ErrorSelector::new(*error_selector); - - Some(match error_selector { - STRING_ERROR_SELECTOR => { - // If the error selector is 0, it means the error is a string - let string = fields - .iter() - .map(|field| { - let as_u8: u8 = field - .try_to_u64() - .expect("String character doesn't fit in u64") - .try_into() - .expect("String character doesn't fit in u8"); - as_u8 as char - }) - .collect(); - ResolvedAssertionPayload::String(string) - } - _ => { - // If the error selector is not 0, it means the error is a custom error - ResolvedAssertionPayload::Raw(RawAssertionPayload { - selector: error_selector, - data: fields, - }) - } - }) } } + let error_selector = ErrorSelector::new(assertion_descriptor.error_selector); + + Some(ResolvedAssertionPayload::Raw(RawAssertionPayload { + selector: error_selector, + data: fields, + })) } fn solve_brillig_call_opcode( @@ -530,7 +503,7 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver> ACVM<'a, F, B> { )?, }; - let result = solver.solve().map_err(|err| self.map_brillig_error(err))?; + let result = solver.solve()?; match result { BrilligSolverStatus::ForeignCallWait(foreign_call) => { @@ -570,31 +543,6 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver> ACVM<'a, F, B> { } } - fn map_brillig_error(&self, mut err: OpcodeResolutionError) -> OpcodeResolutionError { - match &mut err { - OpcodeResolutionError::BrilligFunctionFailed { call_stack, payload, .. } => { - // Some brillig errors have static strings as payloads, we can resolve them here - let last_location = - call_stack.last().expect("Call stacks should have at least one item"); - let assertion_descriptor = - self.assertion_payloads.iter().find_map(|(loc, payload)| { - if loc == last_location { - Some(payload) - } else { - None - } - }); - - if let Some(AssertionPayload::StaticString(string)) = assertion_descriptor { - *payload = Some(ResolvedAssertionPayload::String(string.clone())); - } - - err - } - _ => err, - } - } - pub fn step_into_brillig(&mut self) -> StepResult<'a, F, B> { let Opcode::BrilligCall { id, inputs, outputs, predicate } = &self.opcodes[self.instruction_pointer] diff --git a/noir/noir-repo/acvm-repo/acvm_js/test/shared/addition.ts b/noir/noir-repo/acvm-repo/acvm_js/test/shared/addition.ts index 820a415acf3..2b8124e77d7 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/test/shared/addition.ts +++ b/noir/noir-repo/acvm-repo/acvm_js/test/shared/addition.ts @@ -3,10 +3,10 @@ import { WitnessMap } from '@noir-lang/acvm_js'; // See `addition_circuit` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 144, 65, 14, 128, 32, 12, 4, 65, 124, 80, 75, 91, 104, 111, 126, 69, 34, 252, - 255, 9, 106, 228, 64, 162, 55, 153, 164, 217, 158, 38, 155, 245, 238, 97, 189, 206, 187, 55, 161, 231, 214, 19, 254, - 129, 126, 162, 107, 25, 92, 4, 137, 185, 230, 88, 145, 112, 135, 104, 69, 5, 88, 74, 82, 84, 20, 149, 35, 42, 81, 85, - 214, 108, 197, 50, 24, 50, 85, 108, 98, 212, 186, 44, 204, 235, 5, 183, 99, 233, 46, 63, 252, 110, 216, 56, 184, 15, - 78, 146, 74, 173, 20, 141, 1, 0, 0, + 255, 9, 106, 228, 64, 194, 81, 38, 105, 182, 167, 201, 102, 189, 251, 216, 159, 243, 110, 38, 244, 60, 122, 194, 63, + 208, 47, 116, 109, 131, 139, 32, 49, 215, 28, 43, 18, 158, 16, 173, 168, 0, 75, 73, 138, 138, 162, 114, 69, 37, 170, + 202, 154, 173, 88, 6, 67, 166, 138, 77, 140, 90, 151, 133, 117, 189, 224, 117, 108, 221, 229, 135, 223, 13, 27, 135, + 121, 106, 119, 3, 58, 173, 124, 163, 140, 1, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ diff --git a/noir/noir-repo/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts b/noir/noir-repo/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts index 8eb7b7d5059..b2b0aff6ed3 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts +++ b/noir/noir-repo/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts @@ -2,14 +2,14 @@ import { WitnessMap } from '@noir-lang/acvm_js'; // See `complex_brillig_foreign_call` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 85, 93, 10, 194, 48, 12, 78, 155, 233, 54, 240, 205, 11, 8, 122, 128, 76, 47, - 176, 187, 136, 111, 138, 62, 122, 124, 45, 75, 88, 23, 139, 19, 76, 64, 63, 24, 89, 75, 242, 229, 159, 6, 24, 208, 60, - 191, 192, 255, 11, 150, 145, 101, 186, 71, 152, 66, 116, 123, 150, 244, 29, 186, 96, 199, 69, 94, 49, 198, 63, 136, - 17, 29, 98, 132, 172, 255, 63, 216, 111, 203, 190, 152, 214, 15, 11, 251, 83, 193, 176, 95, 75, 62, 215, 44, 27, 93, - 232, 100, 20, 225, 117, 241, 38, 144, 233, 105, 149, 4, 229, 185, 183, 201, 232, 208, 42, 191, 198, 252, 36, 213, 216, - 192, 103, 249, 250, 228, 185, 39, 225, 71, 23, 126, 234, 132, 191, 114, 234, 83, 173, 234, 149, 231, 146, 251, 93, - 193, 56, 129, 199, 235, 229, 118, 62, 221, 177, 96, 170, 205, 19, 182, 234, 188, 43, 148, 108, 142, 67, 144, 63, 52, - 239, 244, 67, 65, 127, 206, 102, 13, 227, 56, 201, 195, 246, 0, 155, 0, 46, 128, 245, 6, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 85, 93, 10, 194, 48, 12, 78, 219, 233, 54, 240, 205, 11, 8, 122, 128, 76, 47, + 176, 187, 136, 111, 138, 62, 122, 124, 45, 75, 88, 140, 197, 9, 38, 224, 62, 24, 89, 75, 242, 229, 159, 6, 24, 208, + 60, 191, 64, 255, 11, 146, 145, 100, 190, 79, 240, 10, 214, 237, 73, 226, 111, 232, 130, 29, 23, 122, 197, 24, 103, + 16, 99, 114, 136, 17, 68, 255, 255, 176, 223, 150, 125, 49, 173, 95, 42, 236, 79, 5, 195, 126, 45, 233, 92, 147, 108, + 116, 161, 179, 81, 132, 247, 197, 147, 224, 225, 105, 149, 4, 229, 184, 183, 73, 232, 208, 42, 191, 198, 252, 200, + 197, 216, 192, 119, 249, 250, 228, 185, 71, 230, 79, 46, 252, 216, 49, 127, 229, 212, 167, 90, 213, 75, 230, 34, 253, + 174, 96, 28, 192, 227, 245, 114, 59, 159, 238, 169, 96, 170, 205, 51, 182, 234, 188, 43, 148, 108, 138, 131, 33, 223, + 153, 79, 250, 161, 160, 63, 101, 179, 134, 113, 156, 248, 93, 123, 0, 142, 67, 44, 107, 244, 6, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], diff --git a/noir/noir-repo/acvm-repo/acvm_js/test/shared/foreign_call.ts b/noir/noir-repo/acvm-repo/acvm_js/test/shared/foreign_call.ts index dc3c6f23f6f..6cec99a636d 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/test/shared/foreign_call.ts +++ b/noir/noir-repo/acvm-repo/acvm_js/test/shared/foreign_call.ts @@ -2,11 +2,11 @@ import { WitnessMap } from '@noir-lang/acvm_js'; // See `simple_brillig_foreign_call` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 79, 73, 10, 128, 48, 12, 204, 40, 46, 5, 111, 126, 36, 254, 192, 207, 120, - 240, 226, 65, 196, 247, 91, 48, 129, 80, 218, 122, 48, 3, 33, 147, 9, 89, 6, 244, 98, 140, 1, 225, 157, 100, 173, 45, - 84, 91, 37, 243, 63, 44, 240, 219, 197, 246, 223, 38, 37, 176, 34, 85, 156, 169, 251, 144, 233, 183, 142, 206, 67, - 114, 215, 121, 63, 15, 84, 135, 222, 157, 98, 244, 194, 247, 227, 222, 206, 11, 31, 19, 165, 186, 164, 207, 153, 222, - 3, 91, 101, 84, 220, 120, 2, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 79, 73, 10, 128, 48, 12, 236, 40, 46, 5, 111, 126, 36, 254, 192, 207, 120, + 240, 226, 65, 196, 247, 91, 48, 129, 80, 186, 28, 154, 129, 144, 201, 132, 44, 3, 247, 99, 14, 1, 230, 3, 103, 169, + 53, 68, 219, 57, 83, 27, 54, 216, 237, 34, 253, 111, 23, 19, 104, 177, 96, 76, 204, 251, 68, 191, 55, 52, 238, 163, + 187, 198, 251, 105, 114, 101, 200, 221, 37, 196, 200, 252, 188, 222, 227, 126, 80, 153, 200, 213, 57, 125, 77, 244, + 62, 112, 171, 6, 33, 119, 2, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000005'], diff --git a/noir/noir-repo/acvm-repo/acvm_js/test/shared/memory_op.ts b/noir/noir-repo/acvm-repo/acvm_js/test/shared/memory_op.ts index f7443c2258b..2287d31d37e 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/test/shared/memory_op.ts +++ b/noir/noir-repo/acvm-repo/acvm_js/test/shared/memory_op.ts @@ -1,9 +1,9 @@ // See `memory_op_circuit` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 82, 65, 10, 0, 32, 8, 211, 180, 255, 216, 15, 250, 255, 171, 10, 82, 176, 232, - 150, 30, 26, 200, 118, 144, 49, 135, 8, 11, 117, 14, 169, 102, 229, 162, 140, 78, 219, 206, 137, 174, 44, 111, 104, - 217, 190, 24, 236, 75, 113, 94, 146, 93, 174, 252, 86, 46, 71, 223, 78, 46, 104, 129, 253, 155, 45, 60, 195, 5, 3, 89, - 11, 161, 73, 39, 3, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 82, 65, 10, 0, 32, 8, 211, 180, 255, 216, 15, 250, 255, 171, 10, 82, 176, 58, + 166, 135, 6, 178, 29, 100, 204, 33, 194, 66, 157, 67, 170, 89, 185, 40, 163, 211, 182, 115, 162, 43, 203, 27, 90, 182, + 47, 6, 251, 82, 156, 151, 100, 151, 43, 191, 149, 203, 209, 183, 147, 11, 90, 96, 255, 102, 11, 207, 112, 99, 0, 192, + 100, 38, 199, 38, 3, 0, 0, ]); export const initialWitnessMap = new Map([ diff --git a/noir/noir-repo/acvm-repo/acvm_js/test/shared/multi_scalar_mul.ts b/noir/noir-repo/acvm-repo/acvm_js/test/shared/multi_scalar_mul.ts index 239d5473606..3ec589dd0c8 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/test/shared/multi_scalar_mul.ts +++ b/noir/noir-repo/acvm-repo/acvm_js/test/shared/multi_scalar_mul.ts @@ -1,8 +1,8 @@ // See `multi_scalar_mul_circuit` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 77, 9, 14, 0, 32, 8, 202, 171, 227, 255, 255, 109, 217, 162, 141, 114, 99, 2, - 162, 74, 57, 53, 18, 2, 46, 208, 70, 122, 99, 162, 43, 113, 35, 239, 102, 157, 230, 1, 94, 19, 45, 209, 145, 11, 202, - 43, 238, 56, 249, 133, 254, 255, 187, 79, 45, 204, 84, 220, 246, 193, 0, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 77, 9, 10, 0, 32, 8, 243, 236, 248, 255, 127, 35, 163, 5, 35, 97, 184, 205, + 169, 42, 183, 102, 65, 193, 21, 218, 73, 31, 44, 116, 35, 238, 228, 189, 108, 208, 60, 193, 91, 161, 23, 6, 114, 73, + 121, 195, 157, 32, 95, 232, 255, 191, 203, 181, 1, 243, 231, 24, 106, 192, 0, 0, 0, ]); export const initialWitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], diff --git a/noir/noir-repo/acvm-repo/acvm_js/test/shared/nested_acir_call.ts b/noir/noir-repo/acvm-repo/acvm_js/test/shared/nested_acir_call.ts index 64051dff93f..5e0bcf25300 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/test/shared/nested_acir_call.ts +++ b/noir/noir-repo/acvm-repo/acvm_js/test/shared/nested_acir_call.ts @@ -2,13 +2,13 @@ import { WitnessMap, StackItem, WitnessStack } from '@noir-lang/acvm_js'; // See `nested_acir_call_circuit` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 205, 146, 97, 10, 195, 32, 12, 133, 163, 66, 207, 147, 24, 173, 241, 223, 174, 50, - 153, 189, 255, 17, 214, 177, 148, 57, 17, 250, 99, 14, 250, 224, 97, 144, 16, 146, 143, 231, 224, 45, 167, 126, 105, - 217, 109, 118, 91, 248, 200, 168, 225, 248, 63, 107, 114, 208, 233, 104, 188, 233, 139, 191, 137, 108, 51, 139, 113, - 13, 161, 38, 95, 137, 233, 142, 62, 23, 137, 24, 98, 89, 133, 132, 162, 196, 135, 23, 230, 42, 65, 82, 46, 57, 97, - 166, 192, 149, 182, 152, 121, 211, 97, 110, 222, 94, 8, 13, 132, 182, 54, 48, 144, 235, 8, 254, 11, 22, 76, 132, 101, - 231, 237, 229, 23, 189, 213, 54, 119, 15, 83, 212, 199, 172, 175, 191, 226, 102, 96, 140, 251, 202, 84, 13, 204, 141, - 224, 25, 176, 161, 158, 53, 121, 144, 73, 14, 4, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 146, 81, 10, 195, 48, 8, 134, 77, 132, 158, 71, 99, 210, 152, 183, 93, 101, + 97, 233, 253, 143, 176, 142, 165, 44, 100, 133, 62, 52, 125, 232, 7, 63, 138, 136, 232, 143, 8, 95, 176, 234, 195, + 180, 202, 172, 178, 240, 195, 84, 193, 86, 63, 106, 66, 232, 216, 26, 31, 53, 210, 57, 216, 54, 179, 132, 102, 239, + 75, 116, 133, 133, 159, 228, 82, 214, 64, 62, 228, 89, 89, 57, 104, 120, 57, 21, 41, 234, 53, 166, 156, 34, 37, 246, + 82, 120, 9, 73, 150, 58, 12, 199, 237, 69, 208, 152, 208, 230, 6, 254, 193, 206, 192, 171, 188, 130, 129, 94, 217, + 113, 123, 185, 169, 222, 106, 155, 187, 119, 159, 168, 255, 178, 62, 199, 174, 102, 110, 102, 170, 129, 177, 15, 120, + 228, 215, 30, 111, 39, 140, 108, 64, 11, 4, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ diff --git a/noir/noir-repo/acvm-repo/acvm_js/test/shared/schnorr_verify.ts b/noir/noir-repo/acvm-repo/acvm_js/test/shared/schnorr_verify.ts index 830ca1026d6..d2df63a8ddb 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/test/shared/schnorr_verify.ts +++ b/noir/noir-repo/acvm-repo/acvm_js/test/shared/schnorr_verify.ts @@ -1,19 +1,19 @@ // See `schnorr_verify_circuit` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 85, 211, 103, 78, 2, 81, 24, 70, 225, 193, 130, 96, 239, 189, 96, 239, 189, 35, 34, - 34, 34, 34, 238, 130, 253, 47, 129, 192, 9, 223, 36, 7, 146, 201, 60, 209, 31, 144, 123, 207, 155, 73, 250, 159, 118, - 239, 201, 132, 121, 103, 227, 205, 211, 137, 247, 144, 60, 220, 123, 114, 225, 17, 121, 84, 206, 202, 99, 114, 78, - 206, 203, 227, 242, 132, 60, 41, 79, 201, 211, 242, 140, 60, 43, 207, 201, 243, 242, 130, 188, 40, 47, 201, 203, 242, - 138, 188, 42, 175, 201, 235, 242, 134, 188, 41, 111, 201, 219, 242, 142, 92, 144, 119, 229, 61, 121, 95, 62, 144, 15, - 229, 35, 249, 88, 62, 145, 79, 229, 51, 249, 92, 190, 144, 47, 229, 43, 249, 90, 190, 145, 111, 229, 59, 249, 94, 126, - 144, 31, 229, 39, 249, 89, 126, 145, 95, 229, 162, 252, 38, 151, 228, 119, 185, 44, 127, 200, 21, 249, 83, 174, 134, - 233, 52, 137, 191, 125, 233, 255, 53, 249, 91, 174, 203, 63, 114, 67, 254, 149, 155, 242, 159, 220, 10, 255, 199, 247, - 183, 244, 59, 216, 38, 155, 100, 139, 108, 144, 237, 165, 155, 203, 199, 111, 102, 83, 108, 137, 13, 177, 29, 54, 195, - 86, 216, 8, 219, 96, 19, 108, 129, 13, 208, 62, 205, 211, 58, 141, 211, 54, 77, 211, 50, 13, 211, 46, 205, 22, 146, - 126, 163, 180, 73, 147, 180, 72, 131, 180, 71, 115, 180, 70, 99, 180, 69, 83, 180, 68, 67, 180, 67, 51, 180, 66, 35, - 180, 65, 19, 180, 64, 3, 220, 61, 119, 206, 93, 115, 199, 197, 184, 211, 82, 220, 97, 57, 238, 172, 18, 119, 84, 141, - 187, 168, 197, 217, 215, 227, 172, 27, 113, 182, 205, 56, 203, 244, 204, 210, 115, 75, 116, 158, 3, 159, 46, 43, 32, - 188, 53, 25, 5, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 211, 103, 78, 2, 81, 24, 70, 225, 193, 130, 96, 239, 189, 96, 239, 189, 35, 34, + 34, 34, 82, 118, 193, 254, 151, 64, 224, 132, 111, 146, 67, 50, 153, 39, 250, 3, 114, 239, 121, 51, 201, 240, 211, 29, + 60, 153, 48, 239, 108, 188, 121, 122, 241, 30, 145, 71, 7, 79, 46, 60, 38, 143, 203, 89, 121, 66, 206, 201, 121, 121, + 82, 158, 146, 167, 229, 25, 121, 86, 158, 147, 231, 229, 5, 121, 81, 94, 146, 151, 229, 21, 121, 85, 94, 147, 215, + 229, 13, 121, 83, 222, 146, 183, 229, 29, 121, 87, 222, 147, 11, 242, 190, 124, 32, 31, 202, 71, 242, 177, 124, 34, + 159, 202, 103, 242, 185, 124, 33, 95, 202, 87, 242, 181, 124, 35, 223, 202, 119, 242, 189, 252, 32, 63, 202, 79, 242, + 179, 252, 34, 191, 202, 111, 242, 187, 92, 148, 63, 228, 146, 252, 41, 151, 229, 47, 185, 34, 127, 203, 213, 48, 157, + 38, 241, 183, 31, 253, 191, 38, 255, 202, 117, 249, 79, 110, 200, 255, 114, 83, 110, 201, 237, 112, 39, 190, 191, 173, + 223, 193, 54, 217, 36, 91, 100, 131, 108, 47, 221, 92, 62, 126, 51, 155, 98, 75, 108, 136, 237, 176, 25, 182, 194, 70, + 216, 6, 155, 96, 11, 108, 128, 246, 105, 158, 214, 105, 156, 182, 105, 154, 150, 105, 152, 118, 105, 182, 144, 12, 27, + 165, 77, 154, 164, 69, 26, 164, 61, 154, 163, 53, 26, 163, 45, 154, 162, 37, 26, 162, 29, 154, 161, 21, 26, 161, 13, + 154, 160, 5, 26, 224, 238, 185, 115, 238, 154, 59, 46, 198, 157, 150, 226, 14, 203, 113, 103, 149, 184, 163, 106, 220, + 69, 45, 206, 190, 30, 103, 221, 136, 179, 109, 198, 89, 166, 103, 150, 158, 91, 162, 243, 244, 167, 15, 14, 161, 226, + 6, 24, 5, 0, 0, ]); export const initialWitnessMap = new Map([ diff --git a/noir/noir-repo/compiler/integration-tests/scripts/codegen-verifiers.sh b/noir/noir-repo/compiler/integration-tests/scripts/codegen-verifiers.sh index bec59eb6889..de1f71a4cc0 100755 --- a/noir/noir-repo/compiler/integration-tests/scripts/codegen-verifiers.sh +++ b/noir/noir-repo/compiler/integration-tests/scripts/codegen-verifiers.sh @@ -17,8 +17,8 @@ KEYS=$(mktemp -d) # Codegen verifier contract for 1_mul mul_dir=$repo_root/test_programs/execution_success/1_mul nargo --program-dir $mul_dir compile -$NARGO_BACKEND_PATH write_vk -b $mul_dir/target/1_mul.json -o $KEYS/1_mul -$NARGO_BACKEND_PATH contract -k $KEYS/1_mul -o $contracts_dir/1_mul.sol +$NARGO_BACKEND_PATH write_vk -b $mul_dir/target/1_mul.json -o $KEYS/1_mul +$NARGO_BACKEND_PATH contract -k $KEYS/1_mul -o $contracts_dir/1_mul.sol # Codegen verifier contract for assert_statement assert_statement_dir=$repo_root/test_programs/execution_success/assert_statement diff --git a/noir/noir-repo/compiler/integration-tests/test/browser/recursion.test.ts b/noir/noir-repo/compiler/integration-tests/test/browser/recursion.test.ts index 4ee92d5b795..71d6697f843 100644 --- a/noir/noir-repo/compiler/integration-tests/test/browser/recursion.test.ts +++ b/noir/noir-repo/compiler/integration-tests/test/browser/recursion.test.ts @@ -19,7 +19,7 @@ await newABICoder(); await initACVM(); const base_relative_path = '../../../../..'; -const circuit_main = 'test_programs/execution_success/assert_statement_recursive'; +const circuit_main = 'test_programs/execution_success/assert_statement'; const circuit_recursion = 'compiler/integration-tests/circuits/recursion'; async function getCircuit(projectPath: string) { @@ -45,7 +45,7 @@ describe('It compiles noir program code, receiving circuit bytes and abi object. const main_program = await getCircuit(`${base_relative_path}/${circuit_main}`); const main_inputs: InputMap = TOML.parse(circuit_main_toml) as InputMap; - const main_backend = new UltraPlonkBackend(main_program.bytecode); + const main_backend = new UltraPlonkBackend(main_program.bytecode, {}, { recursive: true }); const { witness: main_witnessUint8Array } = await new Noir(main_program).execute(main_inputs); @@ -73,7 +73,7 @@ describe('It compiles noir program code, receiving circuit bytes and abi object. const recursion_program = await getCircuit(`${base_relative_path}/${circuit_recursion}`); - const recursion_backend = new UltraPlonkBackend(recursion_program.bytecode); + const recursion_backend = new UltraPlonkBackend(recursion_program.bytecode, {}, { recursive: false }); const { witness: recursion_witnessUint8Array } = await new Noir(recursion_program).execute(recursion_inputs); diff --git a/noir/noir-repo/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts b/noir/noir-repo/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts index b08d52e1604..1694da28805 100644 --- a/noir/noir-repo/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts +++ b/noir/noir-repo/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts @@ -17,7 +17,7 @@ it.skip(`smart contract can verify a recursive proof`, async () => { const fm = createFileManager(basePath); const innerCompilationResult = await compile( fm, - join(basePath, './test_programs/execution_success/assert_statement_recursive'), + join(basePath, './test_programs/execution_success/assert_statement'), ); if (!('program' in innerCompilationResult)) { throw new Error('Compilation failed'); @@ -35,11 +35,11 @@ it.skip(`smart contract can verify a recursive proof`, async () => { // Intermediate proof - const inner_backend = new UltraPlonkBackend(innerProgram.bytecode); + const inner_backend = new UltraPlonkBackend(innerProgram.bytecode, {}, { recursive: true }); const inner = new Noir(innerProgram); const inner_prover_toml = readFileSync( - join(basePath, `./test_programs/execution_success/assert_statement_recursive/Prover.toml`), + join(basePath, `./test_programs/execution_success/assert_statement/Prover.toml`), ).toString(); const inner_inputs = toml.parse(inner_prover_toml); @@ -67,7 +67,7 @@ it.skip(`smart contract can verify a recursive proof`, async () => { const { witness: recursionWitness } = await recursion.execute(recursion_inputs); - const recursion_backend = new UltraPlonkBackend(recursionProgram.bytecode); + const recursion_backend = new UltraPlonkBackend(recursionProgram.bytecode, {}, { recursive: false }); const recursion_proof = await recursion_backend.generateProof(recursionWitness); expect(await recursion_backend.verifyProof(recursion_proof)).to.be.true; diff --git a/noir/noir-repo/compiler/integration-tests/test/node/smart_contract_verifier.test.ts b/noir/noir-repo/compiler/integration-tests/test/node/smart_contract_verifier.test.ts index c43fba01424..74a36262829 100644 --- a/noir/noir-repo/compiler/integration-tests/test/node/smart_contract_verifier.test.ts +++ b/noir/noir-repo/compiler/integration-tests/test/node/smart_contract_verifier.test.ts @@ -46,7 +46,7 @@ test_cases.forEach((testInfo) => { const inputs = toml.parse(prover_toml); const { witness } = await program.execute(inputs); - const backend = new UltraPlonkBackend(noir_program.bytecode); + const backend = new UltraPlonkBackend(noir_program.bytecode, {}, { recursive: false }); const proofData = await backend.generateProof(witness); // JS verification diff --git a/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs b/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs index ad54d0f7d6b..bf64c9c920c 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs @@ -6,6 +6,7 @@ use iter_extended::vecmap; use noirc_abi::{ Abi, AbiErrorType, AbiParameter, AbiReturnType, AbiType, AbiValue, AbiVisibility, Sign, }; +use noirc_evaluator::ErrorType; use noirc_frontend::ast::{Signedness, Visibility}; use noirc_frontend::TypeBinding; use noirc_frontend::{ @@ -25,7 +26,7 @@ pub(super) fn gen_abi( context: &Context, func_id: &FuncId, return_visibility: Visibility, - error_types: BTreeMap, + error_types: BTreeMap, ) -> Abi { let (parameters, return_type) = compute_function_abi(context, func_id); let return_type = return_type.map(|typ| AbiReturnType { @@ -34,23 +35,27 @@ pub(super) fn gen_abi( }); let error_types = error_types .into_iter() - .map(|(selector, typ)| (selector, build_abi_error_type(context, &typ))) + .map(|(selector, typ)| (selector, build_abi_error_type(context, typ))) .collect(); Abi { parameters, return_type, error_types } } -fn build_abi_error_type(context: &Context, typ: &Type) -> AbiErrorType { +fn build_abi_error_type(context: &Context, typ: ErrorType) -> AbiErrorType { match typ { - Type::FmtString(len, item_types) => { - let length = len.evaluate_to_u32().expect("Cannot evaluate fmt length"); - let Type::Tuple(item_types) = item_types.as_ref() else { - unreachable!("FmtString items must be a tuple") - }; - let item_types = - item_types.iter().map(|typ| abi_type_from_hir_type(context, typ)).collect(); - AbiErrorType::FmtString { length, item_types } + ErrorType::Dynamic(typ) => { + if let Type::FmtString(len, item_types) = typ { + let length = len.evaluate_to_u32().expect("Cannot evaluate fmt length"); + let Type::Tuple(item_types) = item_types.as_ref() else { + unreachable!("FmtString items must be a tuple") + }; + let item_types = + item_types.iter().map(|typ| abi_type_from_hir_type(context, typ)).collect(); + AbiErrorType::FmtString { length, item_types } + } else { + AbiErrorType::Custom(abi_type_from_hir_type(context, &typ)) + } } - _ => AbiErrorType::Custom(abi_type_from_hir_type(context, typ)), + ErrorType::String(string) => AbiErrorType::String { string }, } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 23c802e3a14..48914c6219d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -259,7 +259,7 @@ impl<'block> BrilligBlock<'block> { }; match assert_message { - Some(ConstrainError::Dynamic(selector, values)) => { + Some(ConstrainError::Dynamic(selector, _, values)) => { let payload_values = vecmap(values, |value| self.convert_ssa_value(*value, dfg)); let payload_as_params = vecmap(values, |value| { @@ -270,7 +270,7 @@ impl<'block> BrilligBlock<'block> { condition, payload_values, payload_as_params, - selector.as_u64(), + *selector, ); } Some(ConstrainError::StaticString(message)) => { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index 276456fc40d..40a133d5e38 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -1,7 +1,9 @@ use acvm::acir::brillig::Opcode as BrilligOpcode; +use acvm::acir::circuit::ErrorSelector; use std::collections::{BTreeMap, HashMap}; use crate::ssa::ir::{basic_block::BasicBlockId, dfg::CallStack, function::FunctionId}; +use crate::ErrorType; use super::procedures::ProcedureId; @@ -23,7 +25,7 @@ pub(crate) enum BrilligParameter { pub(crate) struct GeneratedBrillig { pub(crate) byte_code: Vec>, pub(crate) locations: BTreeMap, - pub(crate) assert_messages: BTreeMap, + pub(crate) error_types: BTreeMap, pub(crate) name: String, pub(crate) procedure_locations: HashMap, } @@ -33,10 +35,7 @@ pub(crate) struct GeneratedBrillig { /// It includes the bytecode of the function and all the metadata that allows linking with other functions. pub(crate) struct BrilligArtifact { pub(crate) byte_code: Vec>, - /// A map of bytecode positions to assertion messages. - /// Some error messages (compiler intrinsics) are not emitted via revert data, - /// instead, they are handled externally so they don't add size to user programs. - pub(crate) assert_messages: BTreeMap, + pub(crate) error_types: BTreeMap, /// The set of jumps that need to have their locations /// resolved. unresolved_jumps: Vec<(JumpInstructionPosition, UnresolvedJumpLocation)>, @@ -68,7 +67,7 @@ pub(crate) struct BrilligArtifact { /// A pointer to a location in the opcode. pub(crate) type OpcodeLocation = usize; -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] pub(crate) enum LabelType { /// Labels for the entry point bytecode Entrypoint, @@ -98,7 +97,7 @@ impl std::fmt::Display for LabelType { /// /// It is assumed that an entity will keep a map /// of labels to Opcode locations. -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] pub(crate) struct Label { pub(crate) label_type: LabelType, pub(crate) section: Option, @@ -106,7 +105,7 @@ pub(crate) struct Label { impl Label { pub(crate) fn add_section(&self, section: usize) -> Self { - Label { label_type: self.label_type, section: Some(section) } + Label { label_type: self.label_type.clone(), section: Some(section) } } pub(crate) fn function(func_id: FunctionId) -> Self { @@ -157,7 +156,7 @@ impl BrilligArtifact { GeneratedBrillig { byte_code: self.byte_code, locations: self.locations, - assert_messages: self.assert_messages, + error_types: self.error_types, name: self.name, procedure_locations: self.procedure_locations, } @@ -165,7 +164,7 @@ impl BrilligArtifact { /// Gets the first unresolved function call of this artifact. pub(crate) fn first_unresolved_function_call(&self) -> Option