diff --git a/.github/workflows/code-scanning.yml b/.github/workflows/code-scanning.yml index 3583ed428..d13fb927e 100644 --- a/.github/workflows/code-scanning.yml +++ b/.github/workflows/code-scanning.yml @@ -46,6 +46,11 @@ jobs: uses: actions/setup-python@v2 with: python-version: '3.x' + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: codeql-py-pip-${{ hashFiles('**/setup.py') }}-${{ runner.os }} - name: Custom dependencies installation run: | cd client @@ -63,4 +68,3 @@ jobs: setup-python-dependencies: false - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 - diff --git a/.github/workflows/dev-docker-apps.yml b/.github/workflows/dev-docker-apps.yml index 5c72ca696..a3224448a 100644 --- a/.github/workflows/dev-docker-apps.yml +++ b/.github/workflows/dev-docker-apps.yml @@ -14,40 +14,39 @@ env: jobs: docker-build: - name: "Docker build" + name: Docker build runs-on: ubuntu-20.04 - timeout-minutes: 360 + timeout-minutes: 180 strategy: - fail-fast: false + fail-fast: true matrix: app: ['mpc', 'client', 'prover'] max-parallel: 3 steps: - - name: "Checkout" + - name: Checkout uses: actions/checkout@v2 - - name: "Get vars from git" + - name: Get vars from git id: git_vars run: echo ::set-output name=COMMIT_HASH::$(git rev-parse --short HEAD) - - name: "Login to DockerHub" - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: "Docker build" - run: docker build -f ./Dockerfile-${{ matrix.app }} -t $REPO_OWNER/zeth-${{ matrix.app }}:git-${{ steps.git_vars.outputs.COMMIT_HASH }} . + - name: Configure Docker + run: echo ${{ secrets.CR_PAT }} | docker login ghcr.io -u $GITHUB_ACTOR --password-stdin - - name: "Docker push" - run: docker push $REPO_OWNER/zeth-${{ matrix.app }}:git-${{ steps.git_vars.outputs.COMMIT_HASH }} + - name: Set up Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + with: + version: latest - - name: "Docker Info" + - name: Build and push image git-SHA run: | - docker inspect $REPO_OWNER/zeth-${{ matrix.app }}:git-${{ steps.git_vars.outputs.COMMIT_HASH }} - docker history $REPO_OWNER/zeth-${{ matrix.app }}:git-${{ steps.git_vars.outputs.COMMIT_HASH }} --no-trunc - docker version - docker info - uname -a + docker buildx build \ + --tag ghcr.io/$GITHUB_REPOSITORY:git-${{ steps.git_vars.outputs.COMMIT_HASH }}-${{ matrix.app }} \ + --cache-from=type=registry,ref=ghcr.io/$GITHUB_REPOSITORY:cache-${{ matrix.app }} \ + --cache-to=type=registry,ref=ghcr.io/$GITHUB_REPOSITORY:cache-${{ matrix.app }} \ + --platform linux/amd64 \ + --output "type=image,push=true" \ + --file ./Dockerfile-${{ matrix.app }} ./ diff --git a/.github/workflows/onpullrequest-build-ubuntu.yml b/.github/workflows/onpullrequest-build-ubuntu.yml index 7a460e83c..025532ce8 100644 --- a/.github/workflows/onpullrequest-build-ubuntu.yml +++ b/.github/workflows/onpullrequest-build-ubuntu.yml @@ -1,13 +1,46 @@ # Actions for pull requests only -name: zeth-ci-pull-request +name: zeth-ci-onpullrequest-build-ubuntu on: pull_request: jobs: - build-linux-full: + # Job to build the grpc libraries. The grpc build directory is populated and + # cached so that all other jobs can mark this job as a prerequisite and just + # run `make install`. + # + # NOTE: the version number here (in `key` and in script arguments) must be + # kept in sync with the key used by the jobs. + onpr-build-grpc: runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: Cache grpc + uses: actions/cache@v2 + with: + key: grpc-1.31.x-${{ runner.os }} + path: depends/grpc + - name: Build grpc + run: if ! [ -d depends/grpc ] ; then scripts/install_grpc /usr v1.31.x depends/grpc ; fi + + # Extract the commits of submodules for use by cache steps + onpr-submodules: + runs-on: ubuntu-20.04 + outputs: + commits: ${{ steps.get-commits.outputs.commits }} + steps: + - uses: actions/checkout@v2 + - name: Get Submodule Commits + id: get-commits + run: | + git submodule sync + echo "::set-output name=commits::"`git submodule status depends/libsodium | grep -oe '[0-9a-fA-F]\+' | head -c 8`-`git submodule status depends/libsnark | grep -oe '[0-9a-fA-F]\+' | head -c 8` + + # Run the prover testing python scripts + prover-tests-linux: + runs-on: ubuntu-20.04 + needs: [onpr-build-grpc, onpr-submodules] strategy: matrix: curve: [ BLS12_377, ALT_BN128 ] @@ -15,5 +48,83 @@ jobs: - uses: actions/checkout@v1 with: submodules: recursive + - uses: actions/setup-node@v1 + with: + node-version: 10 + - name: Cache grpc + uses: actions/cache@v2 + with: + key: grpc-1.31.x-${{ runner.os }} + path: depends/grpc + # - name: Cache ccache + # uses: actions/cache@v2 + # with: + # key: prover-tests-ccache-${{ needs.onpr-submodules.outputs.commits }}-${{ matrix.curve }}-${{ runner.os }} + # path: ~/.ccache + - name: Cache pip + uses: actions/cache@v2 + with: + path: | + ~/.cache/pip + ~/Library/Caches/pip + key: prover-tests-pip-${{ hashFiles('**/setup.py') }}-${{ runner.os }} + - name: Cache npm + uses: actions/cache@v2 + with: + path: | + ~/.npm + depends/ganache-cli/node_modules + key: prover-tests-npm-${{ hashFiles('**/package-lock.json') }}-${{ runner.os }} + - name: Install dependencies + run: | + scripts/install_grpc /usr v1.31.x depends/grpc + sudo apt install -y ccache + - name: Execute + run: CI_CONFIG=Release CI_CURVE=${{ matrix.curve }} CI_PROVER_TESTS=1 scripts/ci build + + # Run all unit tests and integration tests + integration-tests-linux: + runs-on: ubuntu-20.04 + needs: [onpr-build-grpc, onpr-submodules] + strategy: + matrix: + curve: [ BLS12_377, ALT_BN128 ] + steps: + - uses: actions/checkout@v1 + with: + submodules: recursive + - uses: actions/setup-node@v1 + with: + node-version: 10 + - name: Cache grpc + uses: actions/cache@v2 + with: + key: grpc-1.31.x-${{ runner.os }} + path: depends/grpc + # ccache in this job seems to make the tests crash occasionally. Disabling + # until the cause is understood. + # - name: Cache ccache + # uses: actions/cache@v2 + # with: + # key: integration-tests-ccache-${{ needs.onpr-submodules.outputs.commits }}-${{ matrix.curve }}-${{ runner.os }} + # path: ~/.ccache + - name: Cache pip + uses: actions/cache@v2 + with: + path: | + ~/.cache/pip + ~/Library/Caches/pip + key: integration-tests-pip-${{ hashFiles('**/setup.py') }}-${{ runner.os }} + - name: Cache npm + uses: actions/cache@v2 + with: + path: | + ~/.npm + depends/ganache-cli/node_modules + key: integration-tests-npm-${{ hashFiles('**/package-lock.json') }}-${{ runner.os }} + - name: Install dependencies + run: | + scripts/install_grpc /usr v1.31.x depends/grpc + sudo apt install -y ccache - name: Execute - run: CI_EVENT_NAME=pull_request CI_USE_DOCKER=1 CI_CONFIG=Release CI_CURVE=${{ matrix.curve }} scripts/ci build + run: CI_CONFIG=Release CI_CURVE=${{ matrix.curve }} CI_FULL_TESTS=1 CI_INTEGRATION_TESTS=1 scripts/ci build diff --git a/.github/workflows/onpush-build-macos.yml b/.github/workflows/onpush-build-macos.yml index 00ebae38c..7b86fd776 100644 --- a/.github/workflows/onpush-build-macos.yml +++ b/.github/workflows/onpush-build-macos.yml @@ -5,21 +5,45 @@ on: push: env: - MACOS_BREW_PACKAGES: "pkg-config libomp" + MACOS_BREW_PACKAGES: "pkg-config libomp ccache" jobs: + # Extract the commits of submodules for use by cache steps + submodules: + runs-on: ubuntu-20.04 + outputs: + commits: ${{ steps.get-commits.outputs.commits }} + steps: + - uses: actions/checkout@v2 + - name: Get Submodule Commits + id: get-commits + run: | + git submodule sync + echo "::set-output name=commits::"`git submodule status depends/libsodium | grep -oe '[0-9a-fA-F]\+' | head -c 8`-`git submodule status depends/libsnark | grep -oe '[0-9a-fA-F]\+' | head -c 8` + + # Main build build-macos: runs-on: macos-10.15 + needs: submodules strategy: matrix: config: [ Debug, Release ] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 with: submodules: recursive + - name: Cache ccache + uses: actions/cache@v2 + with: + key: submodules-${{ needs.submodules.outputs.commits }}-${{ runner.os }}-${{ matrix.config }} + path: ~/Library/Caches/ccache + - name: Cache pip (for mpc tests) + uses: actions/cache@v2 + with: + path: ~/Library/Caches/pip + key: build-macos-pip-${{ hashFiles('**/setup.py') }}-${{ runner.os }} - name: Install Dependencies run: brew install ${MACOS_BREW_PACKAGES} - name: Execute - run: CI_CONFIG=${{ matrix.config }} scripts/ci build - + run: CI_MPC_TESTS=1 CI_CONFIG=${{ matrix.config }} scripts/ci build diff --git a/.github/workflows/onpush-build-ubuntu.yml b/.github/workflows/onpush-build-ubuntu.yml index 830443563..1c711ff8a 100644 --- a/.github/workflows/onpush-build-ubuntu.yml +++ b/.github/workflows/onpush-build-ubuntu.yml @@ -6,33 +6,85 @@ on: jobs: + # Job to build and cache the grpc libraries. The grpc build directory is + # populated and cached so that all other jobs can mark this job as a + # prerequisite and just run `make install`. + # + # NOTE: the version number here (in `key` and in script arguments) must be + # kept in sync with the key used by the jobs. + build-grpc: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: Cache grpc + uses: actions/cache@v2 + with: + key: grpc-1.31.x-${{ runner.os }} + path: depends/grpc + - name: Build grpc + run: if ! [ -d depends/grpc ] ; then scripts/install_grpc /usr v1.31.x depends/grpc ; fi + build-linux: runs-on: ubuntu-20.04 + needs: build-grpc strategy: matrix: config: [ Debug, Release ] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 with: submodules: recursive + - name: Cache grpc + uses: actions/cache@v2 + with: + key: grpc-1.31.x-${{ runner.os }} + path: depends/grpc + - name: Cache pip (for mpc tests) + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: build-linux-pip-${{ hashFiles('**/setup.py') }}-${{ runner.os }} + - name: Install dependencies + run: | + scripts/install_grpc /usr v1.31.x depends/grpc + sudo apt install -y ccache - name: Execute - run: CI_CHECK_FORMAT=1 CI_USE_DOCKER=1 CI_CONFIG=${{ matrix.config }} scripts/ci build + run: CI_CHECK_FORMAT=1 CI_MPC_TESTS=1 CI_CONFIG=${{ matrix.config }} scripts/ci build build-linux-pghr13: runs-on: ubuntu-20.04 + needs: build-grpc steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 with: submodules: recursive + - name: Cache grpc + uses: actions/cache@v2 + with: + key: grpc-1.31.x-${{ runner.os }} + path: depends/grpc + - name: Install dependencies + run: | + scripts/install_grpc /usr v1.31.x depends/grpc + sudo apt install -y ccache - name: Execute - run: CI_USE_DOCKER=1 CI_CONFIG=Release CI_ZKSNARK=PGHR13 scripts/ci build + run: CI_CONFIG=Release CI_ZKSNARK=PGHR13 scripts/ci build build-linux-bls12-377: runs-on: ubuntu-20.04 + needs: build-grpc steps: - uses: actions/checkout@v1 with: submodules: recursive + - name: Cache grpc + uses: actions/cache@v2 + with: + key: grpc-1.31.x-${{ runner.os }} + path: depends/grpc + - name: Install dependencies + run: | + scripts/install_grpc /usr v1.31.x depends/grpc + sudo apt install -y ccache - name: Execute - run: CI_USE_DOCKER=1 CI_CONFIG=Release CI_CURVE=BLS12_377 scripts/ci build - + run: CI_CONFIG=Release CI_CURVE=BLS12_377 scripts/ci build diff --git a/.github/workflows/onpush-checks.yml b/.github/workflows/onpush-checks.yml index c93f1861e..d0fb3b386 100644 --- a/.github/workflows/onpush-checks.yml +++ b/.github/workflows/onpush-checks.yml @@ -1,5 +1,5 @@ # Check actions (linters, analysis tools etc.) -name: zeth-ci-push-checks +name: zeth-ci-onpush-checks on: push: @@ -9,12 +9,26 @@ jobs: check-contracts: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 with: submodules: recursive - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v2 + with: + node-version: '10' + - name: Cache npm (incl ganache-cli/node_modules) + uses: actions/cache@v2 + with: + path: | + ~/.npm + depends/ganache-cli/node_modules + key: check-contracts-npm-${{ hashFiles('**/package-lock.json') }}-${{ runner.os }} + - name: Cache pip + uses: actions/cache@v2 with: - node-version: 10 + path: | + ~/.cache/pip + ~/.solcx + key: check-contracts-pip-solcx-${{ hashFiles('**/setup.py') }}-${{ runner.os }} - name: Check Contracts run: scripts/ci check_contracts @@ -24,7 +38,12 @@ jobs: - uses: actions/checkout@v1 with: submodules: recursive - - name: Execute + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: check-client-pip-${{ hashFiles('**/setup.py') }}-${{ runner.os }} + - name: Check Client run: scripts/ci check_client check-cpp-linux: diff --git a/.github/workflows/release-docker-apps.yml b/.github/workflows/release-docker-apps.yml index 606f55cf3..6b55dbf82 100644 --- a/.github/workflows/release-docker-apps.yml +++ b/.github/workflows/release-docker-apps.yml @@ -5,48 +5,52 @@ on: tags: - v[0-9]+.[0-9]+.[0-9]+ # Triggered by git tags like: v0.2.12 -env: - REPO_OWNER: "clearmatics" - jobs: docker-build: - name: "Docker build" + name: Docker build runs-on: ubuntu-20.04 - timeout-minutes: 360 + timeout-minutes: 180 strategy: + fail-fast: true matrix: app: ['mpc', 'client', 'prover'] max-parallel: 3 steps: - - name: "Checkout" + - name: Checkout uses: actions/checkout@v2 - - name: "Get vars from git" + - name: Get vars from git id: git_vars - run: echo ::set-output name=TAG::${GITHUB_REF/refs\/tags\//} + run: | + echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/} + echo ::set-output name=COMMIT_HASH::$(git rev-parse --short HEAD) - - name: "Login to DockerHub" - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Configure Docker + run: echo ${{ secrets.CR_PAT }} | docker login ghcr.io -u $GITHUB_ACTOR --password-stdin - - name: "Docker build" - run: docker build -f ./Dockerfile-${{ matrix.app }} -t $REPO_OWNER/zeth-${{ matrix.app }}:${{ steps.git_vars.outputs.TAG }} . + - name: Set up Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + with: + version: latest - - name: "Docker push" + - name: Build and push image git-SHA run: | - docker push $REPO_OWNER/zeth-${{ matrix.app }}:${{ steps.git_vars.outputs.TAG }} - docker tag $REPO_OWNER/zeth-${{ matrix.app }}:${{ steps.git_vars.outputs.TAG }} $REPO_OWNER/zeth-${{ matrix.app }}:latest - docker push $REPO_OWNER/zeth-${{ matrix.app }}:latest - - - name: "Docker Info" + docker buildx build \ + --tag ghcr.io/$GITHUB_REPOSITORY:git-${{ steps.git_vars.outputs.COMMIT_HASH }}-${{ matrix.app }} \ + --cache-from=type=registry,ref=ghcr.io/$GITHUB_REPOSITORY:cache-${{ matrix.app }} \ + --cache-to=type=registry,ref=ghcr.io/$GITHUB_REPOSITORY:cache-${{ matrix.app }} \ + --platform linux/amd64 \ + --output "type=image,push=true" \ + --file ./Dockerfile-${{ matrix.app }} ./ + + - name: Tag and push latest image to registry run: | - docker inspect $REPO_OWNER/zeth-${{ matrix.app }}:${{ steps.git_vars.outputs.TAG }} - docker history $REPO_OWNER/zeth-${{ matrix.app }}:${{ steps.git_vars.outputs.TAG }} --no-trunc - docker version - docker info - uname -a + docker pull ghcr.io/$GITHUB_REPOSITORY:git-${{ steps.git_vars.outputs.COMMIT_HASH }}-${{ matrix.app }} + docker tag ghcr.io/$GITHUB_REPOSITORY:git-${{ steps.git_vars.outputs.COMMIT_HASH }}-${{ matrix.app }} ghcr.io/$GITHUB_REPOSITORY:${{ steps.git_vars.outputs.VERSION }}-${{ matrix.app }} + docker tag ghcr.io/$GITHUB_REPOSITORY:git-${{ steps.git_vars.outputs.COMMIT_HASH }}-${{ matrix.app }} ghcr.io/$GITHUB_REPOSITORY:latest-${{ matrix.app }} + docker push ghcr.io/$GITHUB_REPOSITORY:${{ steps.git_vars.outputs.VERSION }}-${{ matrix.app }} + docker push ghcr.io/$GITHUB_REPOSITORY:latest-${{ matrix.app }} diff --git a/.github/workflows/release-docker-base.yml b/.github/workflows/release-docker-base.yml index ab0860a83..95e5e4db5 100644 --- a/.github/workflows/release-docker-base.yml +++ b/.github/workflows/release-docker-base.yml @@ -5,56 +5,50 @@ on: tags: - docker-base-v[0-9]+.[0-9]+.[0-9]+ # Triggered by git tags like: docker-base-v0.2.12 -env: - REPO_OWNER: "clearmatics" - jobs: docker-build: - name: "Build docker zeth-base" + name: Docker build runs-on: ubuntu-20.04 timeout-minutes: 360 steps: - - name: "Checkout" + - name: Checkout uses: actions/checkout@v2 - - name: "Get vars from git" - id: git_vars - run: echo ::set-output name=TAG::${GITHUB_REF/refs\/tags\/docker-base-/} - - - name: "Create swap from file" + - name: Create swap from file run: | - sudo fallocate -l 21G /swapfile2 + sudo fallocate -l 19G /swapfile2 sudo chmod 600 /swapfile2 sudo mkswap /swapfile2 sudo swapon /swapfile2 - - name: "Show build host parameters" + - name: Show build host parameters run: | free -h df -h nproc - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Get vars from git + id: git_vars + run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/docker-base-v/} + + - name: Configure Docker + run: echo ${{ secrets.CR_PAT }} | docker login ghcr.io -u $GITHUB_ACTOR --password-stdin - name: Docker build - run: docker build -f ./Dockerfile-base -t $REPO_OWNER/zeth-base:${{ steps.git_vars.outputs.TAG }} . + run: docker build -f ./Dockerfile-base -t ghcr.io/$GITHUB_REPOSITORY:${{ steps.git_vars.outputs.VERSION }}-base . - name: Docker push run: | - docker push $REPO_OWNER/zeth-base:${{ steps.git_vars.outputs.TAG }} - docker tag $REPO_OWNER/zeth-base:${{ steps.git_vars.outputs.TAG }} $REPO_OWNER/zeth-base:latest - docker push $REPO_OWNER/zeth-base:latest + docker push ghcr.io/$GITHUB_REPOSITORY:${{ steps.git_vars.outputs.VERSION }}-base + docker tag ghcr.io/$GITHUB_REPOSITORY:${{ steps.git_vars.outputs.VERSION }}-base ghcr.io/$GITHUB_REPOSITORY:latest-base + docker push ghcr.io/$GITHUB_REPOSITORY:latest-base - - name: "Docker Info" + - name: Docker Info run: | - docker inspect $REPO_OWNER/zeth-base:${{ steps.git_vars.outputs.TAG }} - docker history $REPO_OWNER/zeth-base:${{ steps.git_vars.outputs.TAG }} --no-trunc + docker inspect ghcr.io/$GITHUB_REPOSITORY:${{ steps.git_vars.outputs.VERSION }}-base + docker history ghcr.io/$GITHUB_REPOSITORY:${{ steps.git_vars.outputs.VERSION }}-base --no-trunc docker version docker info uname -a diff --git a/CMakeLists.txt b/CMakeLists.txt index 69f4b17e4..146c72ee7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ project(zeth CXX) # Versionning of the project set(ZETH_VERSION_MAJOR 0) -set(ZETH_VERSION_MINOR 6) +set(ZETH_VERSION_MINOR 7) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -315,6 +315,7 @@ add_subdirectory(libzeth) # If zeth is being used as a dependency, skip the tools build if ("${IS_ZETH_PARENT}") add_subdirectory(prover_server) + add_subdirectory(verifier) # For now the MPC for Groth16 only is tailored to the alt_bn128 pairing group if((${ZETH_SNARK} STREQUAL "GROTH16") AND (${MPC})) add_subdirectory(mpc_tools) diff --git a/CODING_STANDARDS.md b/CODING_STANDARDS.md index 987cfefc0..5f3255a7a 100644 --- a/CODING_STANDARDS.md +++ b/CODING_STANDARDS.md @@ -15,7 +15,19 @@ We adhere to the `pep8` standard, using `flake8`, `pylint` and `mypy` to catch f ## Solidity -As of now, the solidity code does not follow consistent style conventions. Nevertheless, new solidity code must adhere to the [solidity coding standards](https://docs.soliditylang.org/en/develop/style-guide.html). Old code will progressively be updated to converge towards the recommended style. +Solidity code must adhere to the [solidity coding standards](https://docs.soliditylang.org/en/develop/style-guide.html), with the following amendments: +- Test functions must have a `test` prefix (e.g. `testReverseBytes`) +- Private/internal state variables and functions must have an underscore prefix (e.g. `_myInternalFunction`) +- The order in which contract members are written must driven by their scope (i.e. `public/external` functions must appear first in the contract code, `internal/private` functions must appear last). Additionally, `virtual` functions in abstract contracts must be written last in their visibility group. +- Function parameters must not be prefixed with an underscore +- Interface names must have a capital I prefix (e.g. `IERC20`) +- Library names must have a `Lib` prefix (e.g. `LibPairing`) +- Test contract names must have a `Test` prefix (e.g. `TestMyContract`) +- Abstract contract names must have an `Abstract` prefix (e.g. `AbstractMyContract`) +- Contract names may not be PascalCase if using PascalCase is introducing confusions in the name (e.g. `BLS12377.sol` vs `BLS12_377.sol`). PascalCase should be used whenever possible +- Event names must be prefixed by `Log` (e.g. `LogDeposit`) + +**Note:** Some of the files of the current code base may not fully comply with the coding standards above. Old code will progressively be updated to converge towards the recommended style. ## C++ diff --git a/Dockerfile-base b/Dockerfile-base index 1930fb29f..5243c8810 100644 --- a/Dockerfile-base +++ b/Dockerfile-base @@ -1,5 +1,7 @@ FROM alpine:3.12 +LABEL org.opencontainers.image.source https://github.com/clearmatics/zeth + #### # This Dockerfile builds the base image # (installs all the dependencies) for Zeth diff --git a/Dockerfile-client b/Dockerfile-client index 8de3a66a2..76e75803f 100644 --- a/Dockerfile-client +++ b/Dockerfile-client @@ -1,5 +1,13 @@ +# Image to copy solc compiler from +FROM ethereum/solc:0.8.1-alpine as solc-0.8.1 + FROM python:3.8-alpine3.12 +LABEL org.opencontainers.image.source https://github.com/clearmatics/zeth + +# Image to copy solc compiler from +COPY --from=solc-0.8.1 /usr/local/bin/solc /root/.solcx/solc-v0.8.1 + RUN apk --update --no-cache add \ build-base \ linux-headers \ diff --git a/Dockerfile-dev b/Dockerfile-dev index 339f56adf..11b9c2069 100644 --- a/Dockerfile-dev +++ b/Dockerfile-dev @@ -1,4 +1,6 @@ -FROM clearmatics/zeth-base:latest +FROM ghcr.io/clearmatics/zeth:0.0.2-base + +LABEL org.opencontainers.image.source https://github.com/clearmatics/zeth COPY . /home/zeth diff --git a/Dockerfile-mpc b/Dockerfile-mpc index 0bee00a27..8d071a81d 100644 --- a/Dockerfile-mpc +++ b/Dockerfile-mpc @@ -1,4 +1,6 @@ -FROM clearmatics/zeth-base:v0.0.0 AS stage1 +FROM ghcr.io/clearmatics/zeth:0.0.2-base AS stage1 + +LABEL org.opencontainers.image.source https://github.com/clearmatics/zeth # 1. Build phase 1: powers of tau rust binaries ENV POT_PATH=/home/powersoftau @@ -28,6 +30,9 @@ RUN cd ${ZETH_PATH} && mkdir build && cd build \ ## FROM alpine:3.12 + +LABEL org.opencontainers.image.source https://github.com/clearmatics/zeth + # Move rust and c++ binaries from previous image and put it in the PATH COPY --from=stage1 /usr/local/bin/ /usr/local/bin # Move the mpc python code diff --git a/Dockerfile-prover b/Dockerfile-prover index 0c5e54959..c2fa91d5c 100644 --- a/Dockerfile-prover +++ b/Dockerfile-prover @@ -1,4 +1,6 @@ -FROM clearmatics/zeth-base:v0.0.0 AS stage1 +FROM ghcr.io/clearmatics/zeth:0.0.2-base AS stage1 + +LABEL org.opencontainers.image.source https://github.com/clearmatics/zeth ENV ZETH_PATH=/home/zeth # Copy necessary files in the docker container @@ -18,6 +20,9 @@ RUN cd ${ZETH_PATH} \ ## FROM alpine:3.12 + +LABEL org.opencontainers.image.source https://github.com/clearmatics/zeth + RUN apk add --no-cache bash ## Move `prover_server` from previous image and put it in the PATH COPY --from=stage1 /home/zeth/build/prover_server/prover_server /usr/local/bin diff --git a/README.md b/README.md index 13c8274a7..ff3fa1bc7 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,8 @@ We propose 2 alternatives to run the `prover_server` below. ##### Fetch the prover_server image (recommended) ```bash -docker pull clearmatics/zeth-prover:latest -docker run -ti -p 50051:50051 --name prover zeth-prover:latest prover_server +docker pull ghcr.io/clearmatics/zeth:latest-prover +docker run -ti -p 50051:50051 --name prover ghcr.io/clearmatics/zeth:latest-prover prover_server ``` ##### Build and run the prover_server in the development container @@ -169,11 +169,10 @@ make check ## Docker images | Docker files | Image | Tags | Description | |---------------|------|-----|--| -| [./Dockerfile-prover](./Dockerfile-prover) | [clearmatics/zeth-prover](https://hub.docker.com/r/clearmatics/zeth-prover) | `latest`, `vX.Y.Z` - Release of zeth, `git-%HASH%` - developers build by git-commit | [Zeth Prover Server](./prover_server/README.md). Image use `zeth-base` for building | -| [./Dockerfile-client](./Dockerfile-client) | [clearmatics/zeth-client](https://hub.docker.com/r/clearmatics/zeth-client) | `latest`, `vX.Y.Z` - Release of zeth, `git-%HASH%` - developers build by git-commit | [Python client to interact with the prover](./client/README.md) | -| [./Dockerfile-mpc](./Dockerfile-mpc) | [clearmatics/zeth-mpc](https://hub.docker.com/r/clearmatics/zeth-mpc) | `latest`, `vX.Y.Z` - Release of zeth, `git-%HASH%` - developers build by git-commit | [Tools for Multi-Party Computation](./mpc/README.md). Image use `zeth-base` for building | -| [./Dockerfile-base](./Dockerfile-base) | [clearmatics/zeth-base](https://hub.docker.com/r/clearmatics/zeth-base) | `latest`, `vA.B.C` - Release of zeth-base | Base image for building other containers | - +| [./Dockerfile-prover](./Dockerfile-prover) | [ghcr.io/clearmatics/zeth:latest-prover](https://github.com/orgs/clearmatics/packages/container/package/zeth) | `latest-prover`, `X.Y.Z-prover` - Release of zeth, `git-%HASH%-prover` - developers build by git-commit | [Zeth Prover Server](./prover_server/README.md). Image use `zeth-base` for building | +| [./Dockerfile-client](./Dockerfile-client) | [ghcr.io/clearmatics/zeth:latest-client](https://github.com/orgs/clearmatics/packages/container/package/zeth) | `latest-client`, `X.Y.Z-client` - Release of zeth, `git-%HASH%-client` - developers build by git-commit | [Python client to interact with the prover](./client/README.md) | +| [./Dockerfile-mpc](./Dockerfile-mpc) | [ghcr.io/clearmatics/zeth:latest-mpc](https://github.com/orgs/clearmatics/packages/container/package/zeth) | `latest-mpc`, `X.Y.Z-mpc` - Release of zeth, `git-%HASH%-mpc` - developers build by git-commit | [Tools for Multi-Party Computation](./mpc/README.md). Image use `zeth-base` for building | +| [./Dockerfile-base](./Dockerfile-base) | [ghcr.io/clearmatics/zeth:latest-base](https://github.com/orgs/clearmatics/packages/container/package/zeth) | `latest-base`, `A.B.C-base` - Release of zeth-base | Base image for building other containers | ## Run analysis tools on the code diff --git a/client/Makefile b/client/Makefile index 8be9862d9..9cc596444 100644 --- a/client/Makefile +++ b/client/Makefile @@ -33,11 +33,11 @@ syntax: ${PROTOBUF_OUTPUT} mypy zeth/cli/zeth zeth/helper/zeth_helper mypy -p tests mypy -p test_commands + mypy -p test_contracts pylint zeth.core zeth.cli zeth.helper tests test_commands test: ${PROTOBUF_OUTPUT} - python -m unittest + python -m unittest discover tests test_contracts: ${PROTOBUF_OUTPUT} - python test_commands/test_contract_base_mixer.py - python test_commands/test_merkle_tree_contract.py + python -m unittest discover test_contracts diff --git a/client/py.typed b/client/py.typed index a0c5d77b1..58db294ed 100644 --- a/client/py.typed +++ b/client/py.typed @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/setup.py b/client/setup.py index 6449ec5f6..b6d331dd1 100644 --- a/client/setup.py +++ b/client/setup.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -18,7 +18,7 @@ setup( name='zeth', - version='0.6', + version='0.7', description='Interface to zeth operations', packages=find_packages(), install_requires=[ @@ -48,9 +48,9 @@ "parsimonious==0.8.1", "protobuf==3.13.0", "py_ecc==1.7.1", - "py-solc-x==0.7.0", + "py-solc-x==1.1.0", "pycryptodome==3.9.8", - "cryptography==3.2", + "cryptography==3.3.2", "requests==2.21.0", "rlp==1.1.0", "semantic-version==2.8.4", diff --git a/client/test_commands/__init__.py b/client/test_commands/__init__.py index 6fb9229d2..f50145434 100644 --- a/client/test_commands/__init__.py +++ b/client/test_commands/__init__.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/test_commands/deploy_test_token.py b/client/test_commands/deploy_test_token.py index 1cabf79a7..e1e3193ce 100644 --- a/client/test_commands/deploy_test_token.py +++ b/client/test_commands/deploy_test_token.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -65,11 +65,11 @@ def compile_token() -> Interface: zeth_dir = get_zeth_dir() allowed_path = join( zeth_dir, - "zeth_contracts/node_modules/openzeppelin-solidity/contracts") + "zeth_contracts/contracts") path_to_token = join( zeth_dir, - "zeth_contracts/node_modules/openzeppelin-solidity/contracts", - "token/ERC20/ERC20Mintable.sol") + "zeth_contracts/contracts", + "ERC20Mintable.sol") # Compilation set_solc_version(SOL_COMPILER_VERSION) compiled_sol = compile_files([path_to_token], allow_paths=allowed_path) diff --git a/client/test_commands/mock.py b/client/test_commands/mock.py index 7163028f4..f683d22ad 100644 --- a/client/test_commands/mock.py +++ b/client/test_commands/mock.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/test_commands/scenario.py b/client/test_commands/scenario.py index 1029b4e1e..dab9ee4df 100644 --- a/client/test_commands/scenario.py +++ b/client/test_commands/scenario.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -56,7 +56,6 @@ def wait_for_tx_update_mk_tree( def get_mix_parameters_components( zeth_client: MixerClient, prover_client: ProverClient, - zksnark: IZKSnarkProvider, mk_tree: MerkleTree, sender_ownership_keypair: OwnershipKeyPair, inputs: List[Tuple[int, ZethNote]], @@ -64,7 +63,7 @@ def get_mix_parameters_components( v_in: EtherValue, v_out: EtherValue, compute_h_sig_cb: Optional[ComputeHSigCB] = None -) -> Tuple[ZethNote, ZethNote, ExtendedProof, JoinsplitSigKeyPair]: +) -> Tuple[ZethNote, ZethNote, ExtendedProof, List[int], JoinsplitSigKeyPair]: """ Manually create the components required for MixParameters. The tests below manipulate these to create custom MixParameters as part of attacks. @@ -79,12 +78,12 @@ def get_mix_parameters_components( compute_h_sig_cb) prover_inputs, signing_keypair = zeth_client.create_prover_inputs( mix_call_desc) - ext_proof_proto = prover_client.get_proof(prover_inputs) - ext_proof = zksnark.extended_proof_from_proto(ext_proof_proto) + ext_proof, public_data = prover_client.get_proof(prover_inputs) return ( prover_inputs.js_outputs[0], prover_inputs.js_outputs[1], ext_proof, + public_data, signing_keypair) @@ -258,11 +257,10 @@ def compute_h_sig_attack_nf( return compute_h_sig( bytes.fromhex(attack_nf0), bytes.fromhex(attack_nf1), sign_vk) - output_note1, output_note2, proof, signing_keypair = \ + output_note1, output_note2, proof, public_data, signing_keypair = \ get_mix_parameters_components( zeth_client, prover_client, - zksnark, mk_tree, keystore["Charlie"].ownership_keypair(), # sender [input1, input2], @@ -279,10 +277,10 @@ def compute_h_sig_attack_nf( assert attack_primary_input4 != 0 print("proof = ", proof) - print("proof.inputs[3] = ", proof.inputs[3]) - print("proof.inputs[4] = ", proof.inputs[4]) - proof.inputs[3] = hex(attack_primary_input3) - proof.inputs[4] = hex(attack_primary_input4) + print("public_data[3] = ", public_data[3]) + print("public_data[4] = ", public_data[4]) + public_data[3] = attack_primary_input3 + public_data[4] = attack_primary_input4 # ### ATTACK BLOCK # construct pk object from bytes @@ -300,10 +298,12 @@ def compute_h_sig_attack_nf( signing_keypair, charlie_eth_address, ciphertexts, - proof) + proof, + public_data) mix_params = MixParameters( proof, + public_data, signing_keypair.vk, joinsplit_sig_charlie, ciphertexts) @@ -366,11 +366,10 @@ def charlie_corrupt_bob_deposit( v_in = EtherValue(BOB_DEPOSIT_ETH) - output_note1, output_note2, proof, joinsplit_keypair = \ + output_note1, output_note2, proof, public_data, joinsplit_keypair = \ get_mix_parameters_components( zeth_client, prover_client, - zksnark, mk_tree, keystore["Bob"].ownership_keypair(), [input1, input2], @@ -403,10 +402,12 @@ def charlie_corrupt_bob_deposit( joinsplit_keypair, charlie_eth_address, ciphertexts, - proof) + proof, + public_data) mix_params = MixParameters( proof, + public_data, joinsplit_keypair.vk, joinsplit_sig_charlie, [fake_ciphertext0, fake_ciphertext1]) @@ -443,9 +444,11 @@ def charlie_corrupt_bob_deposit( new_joinsplit_keypair, charlie_eth_address, [fake_ciphertext0, fake_ciphertext1], - proof) + proof, + public_data) mix_params = MixParameters( proof, + public_data, new_joinsplit_keypair.vk, joinsplit_sig_charlie, [fake_ciphertext0, fake_ciphertext1]) @@ -474,9 +477,11 @@ def charlie_corrupt_bob_deposit( joinsplit_keypair, bob_eth_address, ciphertexts, - proof) + proof, + public_data) mix_params = MixParameters( proof, + public_data, joinsplit_keypair.vk, joinsplit_sig_bob, ciphertexts) @@ -504,9 +509,11 @@ def charlie_corrupt_bob_deposit( joinsplit_keypair, bob_eth_address, ciphertexts, - proof) + proof, + public_data) mix_params = MixParameters( proof, + public_data, joinsplit_keypair.vk, joinsplit_sig_bob, ciphertexts) diff --git a/client/test_commands/test_altbn128_mixer_base.py b/client/test_commands/test_altbn128_mixer_base.py deleted file mode 100644 index dcc6b6253..000000000 --- a/client/test_commands/test_altbn128_mixer_base.py +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd -# -# SPDX-License-Identifier: LGPL-3.0+ - -from zeth.core.constants import \ - JS_INPUTS, ZETH_PUBLIC_UNIT_VALUE, ZETH_MERKLE_TREE_DEPTH -import test_commands.mock as mock -from typing import Any - -# pylint: disable=line-too-long - -# TODO: These tests are specific to AltBN128MixerBase, however the mixer that -# is deployed is a function of the currently running prover server. Change this -# to deploy a test contract (inheriting from AltBN128MixerBase) which then -# calls the given methods with the expected data (i.e. remove the requirement -# for a running prover_server, and support type-checking of the test code -# against interface changes). - -# Primary inputs - -ROOT = 0 - -NULLIFIERS = [ - int( - "0010000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000011000", - 2), - int( - "0100000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000100001", - 2), -] - -COMMITMENTS = [ - int( - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000001", - 2), - int( - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000010", - 2), -] - -HSIG = int( - "1010000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000101111", - 2) - -HTAGS = [ - int( - "1100000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000110010", - 2), - int( - "1110000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000111011", - 2), -] - -VPUB = (0x5555555555555500, 0x00eeeeeeeeeeeeee) - -# 255 128 64 0 -# |||||)|| -RESIDUAL_BITS = int( - "101" # h_sig - "010" # nf_1 - "001" # nf_0 - "111" # htag_1 - "110" # htag_0 - "0101010101010101010101010101010101010101010101010101010100000000" # vin - "0000000011101110111011101110111011101110111011101110111011101110", # vout - 2) - -PACKED_PRIMARY_INPUTS = \ - [ROOT] + COMMITMENTS + NULLIFIERS + [HSIG] + HTAGS + [RESIDUAL_BITS] - - -def test_assemble_nullifiers(mixer_instance: Any) -> None: - # Test retrieving nullifiers - print("--- test_assemble_nullifiers") - for i in range(JS_INPUTS): - res = mixer_instance.functions.\ - assemble_nullifier_test(i, PACKED_PRIMARY_INPUTS).call() - val = int.from_bytes(res, byteorder="big") - assert val == NULLIFIERS[i], f"expected: {NULLIFIERS[i]}, got: {val}" - - -def test_assemble_hsig(mixer_instance: Any) -> None: - # Test retrieving hsig - print("--- test_assemble_hsig") - res = mixer_instance.functions.\ - assemble_hsig_test(PACKED_PRIMARY_INPUTS).call() - hsig = int.from_bytes(res, byteorder="big") - assert hsig == HSIG, f"expected: {HSIG}, got {hsig}" - - -def test_assemble_vpub(mixer_instance: Any) -> None: - # Test retrieving public values - print("--- test_assemble_vpub") - v_in, v_out = mixer_instance.functions.assemble_public_values_test( - PACKED_PRIMARY_INPUTS[-1]).call() - v_in_expect = VPUB[0] * ZETH_PUBLIC_UNIT_VALUE - v_out_expect = VPUB[1] * ZETH_PUBLIC_UNIT_VALUE - assert v_in == v_in_expect, f"expected: {v_in_expect}, got: {v_in}" - assert v_out == v_out_expect, f"expected: {v_out_expect}, got: {v_out}" - - -def main() -> None: - print("Deploying AltBN128MixerBase_test.sol") - _web3, eth = mock.open_test_web3() - deployer_eth_address = eth.accounts[0] - _mixer_interface, mixer_instance = mock.deploy_contract( - eth, - deployer_eth_address, - "AltBN128MixerBase_test", - { - 'mk_depth': ZETH_MERKLE_TREE_DEPTH, - }) - - print("Testing ...") - test_assemble_nullifiers(mixer_instance) - test_assemble_vpub(mixer_instance) - test_assemble_hsig(mixer_instance) - - print("========================================") - print("== PASSED ==") - print("========================================") - - -if __name__ == '__main__': - main() diff --git a/client/test_commands/test_erc_token_mixing.py b/client/test_commands/test_erc_token_mixing.py index 0ffea8e63..912ac2cd1 100644 --- a/client/test_commands/test_erc_token_mixing.py +++ b/client/test_commands/test_erc_token_mixing.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/test_commands/test_ether_mixing.py b/client/test_commands/test_ether_mixing.py index 7306dca54..8d5db1803 100644 --- a/client/test_commands/test_ether_mixing.py +++ b/client/test_commands/test_ether_mixing.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/test_commands/test_merkle_tree_contract.py b/client/test_commands/test_merkle_tree_contract.py deleted file mode 100644 index 586ba04d9..000000000 --- a/client/test_commands/test_merkle_tree_contract.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd -# -# SPDX-License-Identifier: LGPL-3.0+ - -from zeth.core.constants import ZETH_MERKLE_TREE_DEPTH -from zeth.core.merkle_tree import MerkleTree -from zeth.core.utils import extend_32bytes -from zeth.core.mimc import MiMC7 -from typing import Any -import test_commands.mock as mock - -TEST_VALUES = [ - extend_32bytes(bytes.fromhex("f0")), - extend_32bytes(bytes.fromhex("f1")), - extend_32bytes(bytes.fromhex("f2")), - extend_32bytes(bytes.fromhex("f3")), - extend_32bytes(bytes.fromhex("f4")), - extend_32bytes(bytes.fromhex("f5")), - extend_32bytes(bytes.fromhex("f6")), - extend_32bytes(bytes.fromhex("f7")), - extend_32bytes(bytes.fromhex("f8")), - extend_32bytes(bytes.fromhex("f9")), - extend_32bytes(bytes.fromhex("fa")), - extend_32bytes(bytes.fromhex("fb")), - extend_32bytes(bytes.fromhex("fc")), - extend_32bytes(bytes.fromhex("fd")), - extend_32bytes(bytes.fromhex("fe")), - extend_32bytes(bytes.fromhex("ff")), -] - - -def assert_root(expected_root: bytes, actual_root: bytes, msg: str) -> None: - if actual_root != expected_root: - print(f"FAILED: {msg}") - print(f"Expected: {expected_root.hex()}") - print(f"Actual: {actual_root.hex()}") - raise Exception("failed") - - -def test_tree_empty(contract: Any) -> None: - mktree = MerkleTree.empty_with_depth(ZETH_MERKLE_TREE_DEPTH, MiMC7()) - expected_root = mktree.recompute_root() - root = contract.functions.testAddLeaves([], []).call() - assert_root(expected_root, root, "test_tree_empty") - - -def test_tree_partial(contract: Any) -> None: - """ - Send a series of different arrays of leaves to the contract and check that - the root is as expected. Send as 2 batches, to test updating the tree, from - various states. - """ - - def _test_partial(num_entries: int, step: int = 1) -> None: - """ - Take the first 'num_entries' from TEST_VALUES. Cut them at each possible - place and submit them as two halves to the contract, receiving back the - root for the updated tree. - """ - leaves = TEST_VALUES[:num_entries] - - mktree = MerkleTree.empty_with_depth(ZETH_MERKLE_TREE_DEPTH, MiMC7()) - for leaf in leaves: - mktree.insert(leaf) - expected_root = mktree.recompute_root() - - for cut in range(0, num_entries + 1, step): - print(f"_test_partial: num_entries={num_entries}, cut={cut}") - first = leaves[:cut] - second = leaves[cut:] - root = contract.functions.testAddLeaves(first, second).call() - assert_root( - expected_root, - root, - f"num_entries: {num_entries}, cut: {cut}: ") - - # Perform the filling tests using arrays of these sizes - _test_partial(1) - _test_partial(7) - _test_partial(8) - _test_partial(9) - _test_partial(15, 3) - _test_partial(16, 3) - - -def main() -> None: - _web3, eth = mock.open_test_web3() - deployer_eth_address = eth.accounts[0] - _mktree_interface, mktree_instance = mock.deploy_contract( - eth, - deployer_eth_address, - "MerkleTreeMiMC7_test", - {'treeDepth': ZETH_MERKLE_TREE_DEPTH}) - - test_tree_empty(mktree_instance) - test_tree_partial(mktree_instance) - - -if __name__ == '__main__': - main() diff --git a/client/test_commands/test_mimc_contract.py b/client/test_commands/test_mimc_contract.py deleted file mode 100644 index dc965844f..000000000 --- a/client/test_commands/test_mimc_contract.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd -# -# SPDX-License-Identifier: LGPL-3.0+ - -from zeth.core.utils import get_contracts_dir -from zeth.core.contracts import InstanceDescription -from zeth.core.mimc import MiMC7, MiMC31 -from zeth.cli.utils import get_eth_network, open_web3_from_network -from os.path import join -import sys -from typing import Any - - -def test_mimc7(instance: Any) -> None: - # pylint: disable=line-too-long - x = int(28948022309329048855892746252171976963317496166410141009864396001978282409983).to_bytes(32, 'big') # noqa - y = int(14220067918847996031108144435763672811050758065945364308986253046354060608451).to_bytes(32, 'big') # noqa - # pylint: enable=line-too-long - h = MiMC7().hash(x, y) - - result = instance.functions.test_mimc7(x, y).call() - assert result == h - - -def test_mimc31(instance: Any) -> None: - # pylint: disable=line-too-long - x = int(28948022309329048855892746252171976963317496166410141009864396001978282409983).to_bytes(32, 'big') # noqa - y = int(14220067918847996031108144435763672811050758065945364308986253046354060608451).to_bytes(32, 'big') # noqa - # pylint: enable=line-too-long - h = MiMC31().hash(x, y) - - result = instance.functions.test_mimc31(x, y).call() - - assert result == h - - -def main() -> int: - web3: Any = open_web3_from_network(get_eth_network(None)) - contracts_dir = get_contracts_dir() - contract_instance_desc = InstanceDescription.deploy( - web3, - join(contracts_dir, "MiMC_test.sol"), - "MiMC_test", - web3.eth.accounts[0], # pylint: disable=no-member - None, - 500000, - {"allow_paths": contracts_dir}) - contract_instance = contract_instance_desc.instantiate(web3) - - test_mimc7(contract_instance) - test_mimc31(contract_instance) - - print("========================================") - print("== PASSED ==") - print("========================================") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/client/test_contracts/__init__.py b/client/test_contracts/__init__.py new file mode 100644 index 000000000..f50145434 --- /dev/null +++ b/client/test_contracts/__init__.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd +# +# SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/test_commands/test_bls12_377_contract.py b/client/test_contracts/test_bls12_377_contract.py similarity index 67% rename from client/test_commands/test_bls12_377_contract.py rename to client/test_contracts/test_bls12_377_contract.py index 453695fbf..e03c7336d 100644 --- a/client/test_commands/test_bls12_377_contract.py +++ b/client/test_contracts/test_bls12_377_contract.py @@ -1,8 +1,9 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ from test_commands import mock +from unittest import TestCase from typing import Any # Data generated by libzeth/tests/core/ec_operation_data_test. Statements to be @@ -74,53 +75,47 @@ def _b32(hex_value: str) -> bytes: ] -def test_bls12_ecadd(bls12_instance: Any) -> None: - """ - Check that [6] == [2] + [4] - """ - result = bls12_instance.functions.testECAdd(G1_2 + G1_4).call() - assert result == G1_6 - - -def test_bls12_ecmul(bls12_instance: Any) -> None: - """ - Check that [-8] == -2 * [4] - """ - result = bls12_instance.functions.testECMul(G1_4 + FR_MINUS_2).call() - assert result == G1_MINUS_8 - - -def test_bls12_ecpairing(bls12_instance: Any) -> None: - """ - Check that e([6], [4]) * e([3],[8]) * e([4],[4]) * e([-8], [8]) == 1 - """ - # Note, return result here is uint256(1) or uint256(0) depending on the - # pairing check result. - points = G1_6 + G2_4 + G1_3 + G2_8 + G1_4 + G2_4 + G1_MINUS_8 + G2_8 - result = bls12_instance.functions.testECPairing(points).call() - assert result == 1 - - points = G1_6 + G2_4 + G1_3 + G2_8 + G1_4 + G2_4 + G1_MINUS_8 + G2_4 - result = bls12_instance.functions.testECPairing(points).call() - assert result == 0 - - -def main() -> None: - _web3, eth = mock.open_test_web3() - _bls12_interface, bls12_instance = mock.deploy_contract( - eth, - eth.accounts[0], - "BLS12_377_test", - {}) - - test_bls12_ecadd(bls12_instance) - test_bls12_ecmul(bls12_instance) - test_bls12_ecpairing(bls12_instance) - - print("========================================") - print("== PASSED ==") - print("========================================") - - -if __name__ == "__main__": - main() +BLS12_INSTANCE: Any = None + + +class TestBLS12_377Contract(TestCase): + + @staticmethod + def setUpClass() -> None: + print("Deploying TestBLS12_377.sol") + _web3, eth = mock.open_test_web3() + _bls12_interface, bls12_instance = mock.deploy_contract( + eth, + eth.accounts[0], + "TestBLS12_377", + {}) + global BLS12_INSTANCE # pylint: disable=global-statement + BLS12_INSTANCE = bls12_instance + + def test_bls12_ecadd(self) -> None: + """ + Check that [6] == [2] + [4] + """ + result = BLS12_INSTANCE.functions.testECAdd(G1_2 + G1_4).call() + self.assertEqual(G1_6, result) + + def test_bls12_ecmul(self) -> None: + """ + Check that [-8] == -2 * [4] + """ + result = BLS12_INSTANCE.functions.testECMul(G1_4 + FR_MINUS_2).call() + self.assertEqual(G1_MINUS_8, result) + + def test_bls12_ecpairing(self) -> None: + """ + Check that e([6], [4]) * e([3],[8]) * e([4],[4]) * e([-8], [8]) == 1 + """ + # Note, return result here is uint256(1) or uint256(0) depending on the + # pairing check result. + points = G1_6 + G2_4 + G1_3 + G2_8 + G1_4 + G2_4 + G1_MINUS_8 + G2_8 + result = BLS12_INSTANCE.functions.testECPairing(points).call() + self.assertEqual(1, result) + + points = G1_6 + G2_4 + G1_3 + G2_8 + G1_4 + G2_4 + G1_MINUS_8 + G2_4 + result = BLS12_INSTANCE.functions.testECPairing(points).call() + self.assertEqual(0, result) diff --git a/client/test_commands/test_bw6_761_contract.py b/client/test_contracts/test_bw6_761_contract.py similarity index 71% rename from client/test_commands/test_bw6_761_contract.py rename to client/test_contracts/test_bw6_761_contract.py index f6bd6483b..82cc75356 100644 --- a/client/test_commands/test_bw6_761_contract.py +++ b/client/test_contracts/test_bw6_761_contract.py @@ -1,8 +1,9 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ from test_commands import mock +from unittest import TestCase from typing import Any # Data generated by libzeth/tests/core/ec_operation_data_test. Statements to be @@ -80,54 +81,47 @@ def _b32(hex_value: str) -> bytes: _b32("ed5512a98239b73fdff458e7c0e09b9d246e3f5681429affc2b1f7e2fcbd1141"), ] - -def test_bw6_ecadd(bw6_instance: Any) -> None: - """ - Check that [6] == [2] + [4] - """ - result = bw6_instance.functions.testECAdd(G1_2 + G1_4).call() - assert result == G1_6 - - -def test_bw6_ecmul(bw6_instance: Any) -> None: - """ - Check that [-8] == -2 * [4] - """ - result = bw6_instance.functions.testECMul(G1_4 + FR_MINUS_2).call() - assert result == G1_MINUS_8 - - -def test_bw6_ecpairing(bw6_instance: Any) -> None: - """ - Check that e([6], [4]) * e([3],[8]) * e([4],[4]) * e([-8], [8]) == 1 - """ - # Note, return result here is uint256(1) or uint256(0) depending on the - # pairing check result. - points = G1_6 + G2_4 + G1_3 + G2_8 + G1_4 + G2_4 + G1_MINUS_8 + G2_8 - result = bw6_instance.functions.testECPairing(points).call() - assert result == 1 - - points = G1_6 + G2_4 + G1_3 + G2_8 + G1_4 + G2_4 + G1_MINUS_8 + G2_4 - result = bw6_instance.functions.testECPairing(points).call() - assert result == 0 - - -def main() -> None: - _web3, eth = mock.open_test_web3() - _bw6_interface, bw6_instance = mock.deploy_contract( - eth, - eth.accounts[0], - "BW6_761_test", - {}) - - test_bw6_ecadd(bw6_instance) - test_bw6_ecmul(bw6_instance) - test_bw6_ecpairing(bw6_instance) - - print("========================================") - print("== PASSED ==") - print("========================================") - - -if __name__ == "__main__": - main() +BW6_INSTANCE: Any = None + + +class TestBW6_761Contract(TestCase): + + @staticmethod + def setUpClass() -> None: + print("Deploying TestBW6_761.sol") + _web3, eth = mock.open_test_web3() + _bw6_interface, bw6_instance = mock.deploy_contract( + eth, + eth.accounts[0], + "TestBW6_761", + {}) + global BW6_INSTANCE # pylint: disable=global-statement + BW6_INSTANCE = bw6_instance + + def test_bw6_ecadd(self) -> None: + """ + Check that [6] == [2] + [4] + """ + result = BW6_INSTANCE.functions.testECAdd(G1_2 + G1_4).call() + self.assertEqual(G1_6, result) + + def test_bw6_ecmul(self) -> None: + """ + Check that [-8] == -2 * [4] + """ + result = BW6_INSTANCE.functions.testECMul(G1_4 + FR_MINUS_2).call() + self.assertEqual(G1_MINUS_8, result) + + def test_bw6_ecpairing(self) -> None: + """ + Check that e([6], [4]) * e([3],[8]) * e([4],[4]) * e([-8], [8]) == 1 + """ + # Note, return result here is uint256(1) or uint256(0) depending on the + # pairing check result. + points = G1_6 + G2_4 + G1_3 + G2_8 + G1_4 + G2_4 + G1_MINUS_8 + G2_8 + result = BW6_INSTANCE.functions.testECPairing(points).call() + self.assertEqual(1, result) + + points = G1_6 + G2_4 + G1_3 + G2_8 + G1_4 + G2_4 + G1_MINUS_8 + G2_4 + result = BW6_INSTANCE.functions.testECPairing(points).call() + self.assertEqual(0, result) diff --git a/client/test_commands/test_groth16_bls12_377_contract.py b/client/test_contracts/test_groth16_bls12_377_contract.py similarity index 66% rename from client/test_commands/test_groth16_bls12_377_contract.py rename to client/test_contracts/test_groth16_bls12_377_contract.py index dffa5fe39..160e5486b 100644 --- a/client/test_commands/test_groth16_bls12_377_contract.py +++ b/client/test_contracts/test_groth16_bls12_377_contract.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -8,7 +8,7 @@ from zeth.cli.utils import get_eth_network, open_web3_from_network from tests.test_pairing import BLS12_377_PAIRING from os.path import join -import sys +from unittest import TestCase from typing import List, Any # pylint: disable=line-too-long @@ -63,51 +63,42 @@ ] # pylint: enable=line-too-long - -def _invoke_groth16_bls12_377_verify( - contract_instance: Any, - vk: Groth16.VerificationKey, - proof: Groth16.Proof, - inputs: List[str]) -> bool: - vk_evm = Groth16.verification_key_to_contract_parameters( - vk, BLS12_377_PAIRING) - proof_evm = Groth16.proof_to_contract_parameters(proof, BLS12_377_PAIRING) - inputs_evm = hex_list_to_uint256_list(inputs) - return contract_instance.functions.test_verify( - vk_evm, proof_evm, inputs_evm).call() - - -def test_groth16_bls12_377_valid(contract_instance: Any) -> None: - assert _invoke_groth16_bls12_377_verify( - contract_instance, VERIFICATION_KEY, PROOF, INPUTS_VALID) - - -def test_groth16_bls12_377_invalid(contract_instance: Any) -> None: - assert not _invoke_groth16_bls12_377_verify( - contract_instance, VERIFICATION_KEY, PROOF, INPUTS_INVALID) - - -def main() -> int: - web3: Any = open_web3_from_network(get_eth_network(None)) - contracts_dir = get_contracts_dir() - contract_instance_desc = InstanceDescription.deploy( - web3, - join(contracts_dir, "Groth16BLS12_377_test.sol"), - "Groth16BLS12_377_test", - web3.eth.accounts[0], # pylint: disable=no-member - None, - 500000, - {"allow_paths": contracts_dir}) - contract_instance = contract_instance_desc.instantiate(web3) - - test_groth16_bls12_377_valid(contract_instance) - test_groth16_bls12_377_invalid(contract_instance) - - print("========================================") - print("== PASSED ==") - print("========================================") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) +CONTRACT_INSTANCE: Any = None + + +class TestGroth16BLS12_377Contract(TestCase): + + @staticmethod + def setUpClass() -> None: + web3: Any = open_web3_from_network(get_eth_network(None)) + contracts_dir = get_contracts_dir() + contract_instance_desc = InstanceDescription.deploy( + web3, + join(contracts_dir, "TestGroth16BLS12_377.sol"), + "TestGroth16BLS12_377", + web3.eth.accounts[0], # pylint: disable=no-member + None, + 500000, + {"allow_paths": contracts_dir}) + global CONTRACT_INSTANCE # pylint: disable=global-statement + CONTRACT_INSTANCE = contract_instance_desc.instantiate(web3) + + @staticmethod + def _invoke_groth16_bls12_377_verify( + vk: Groth16.VerificationKey, + proof: Groth16.Proof, + inputs: List[str]) -> bool: + vk_evm = Groth16.verification_key_to_contract_parameters( + vk, BLS12_377_PAIRING) + proof_evm = Groth16.proof_to_contract_parameters(proof, BLS12_377_PAIRING) + inputs_evm = hex_list_to_uint256_list(inputs) + return CONTRACT_INSTANCE.functions.testVerify( + vk_evm, proof_evm, inputs_evm).call() + + def test_groth16_bls12_377_valid(self) -> None: + self.assertTrue(self._invoke_groth16_bls12_377_verify( + VERIFICATION_KEY, PROOF, INPUTS_VALID)) + + def test_groth16_bls12_377_invalid(self) -> None: + self.assertFalse(self._invoke_groth16_bls12_377_verify( + VERIFICATION_KEY, PROOF, INPUTS_INVALID)) diff --git a/client/test_contracts/test_mimc_contract.py b/client/test_contracts/test_mimc_contract.py new file mode 100644 index 000000000..3c9a5b78c --- /dev/null +++ b/client/test_contracts/test_mimc_contract.py @@ -0,0 +1,56 @@ +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd +# +# SPDX-License-Identifier: LGPL-3.0+ + +from zeth.core.utils import get_contracts_dir +from zeth.core.contracts import InstanceDescription +from zeth.core.mimc import MiMCAltBN128, MiMCBLS12_377 +from zeth.cli.utils import get_eth_network, open_web3_from_network +from os.path import join +from unittest import TestCase +from typing import Any + +CONTRACT_INSTANCE: Any = None + +""" +Test data here matches that used in test_mimc.py, which is also used in the +tests of mimc circuits. +""" + + +class TestMiMCContract(TestCase): + + @staticmethod + def setUpClass() -> None: + web3: Any = open_web3_from_network(get_eth_network(None)) + contracts_dir = get_contracts_dir() + contract_instance_desc = InstanceDescription.deploy( + web3, + join(contracts_dir, "TestMiMC.sol"), + "TestMiMC", + web3.eth.accounts[0], # pylint: disable=no-member + None, + 500000, + {"allow_paths": contracts_dir}) + global CONTRACT_INSTANCE # pylint: disable=global-statement + CONTRACT_INSTANCE = contract_instance_desc.instantiate(web3) + + def test_mimc_alt_bn128(self) -> None: + # pylint: disable=line-too-long + x = int(28948022309329048855892746252171976963317496166410141009864396001978282409983).to_bytes(32, 'big') # noqa + y = int(14220067918847996031108144435763672811050758065945364308986253046354060608451).to_bytes(32, 'big') # noqa + # pylint: enable=line-too-long + h = MiMCAltBN128().hash(x, y) + + result = CONTRACT_INSTANCE.functions.testMimcAltBN128(x, y).call() + self.assertEqual(h, result) + + def test_mimc_bls12_377(self) -> None: + # pylint: disable=line-too-long + x = int(28948022309329048855892746252171976963317496166410141009864396001978282409983).to_bytes(32, 'big') # noqa + y = int(14220067918847996031108144435763672811050758065945364308986253046354060608451).to_bytes(32, 'big') # noqa + # pylint: enable=line-too-long + h = MiMCBLS12_377().hash(x, y) + + result = CONTRACT_INSTANCE.functions.testMimcBLS12_377(x, y).call() + self.assertEqual(h, result) diff --git a/client/tests/__init__.py b/client/tests/__init__.py index 6fb9229d2..f50145434 100644 --- a/client/tests/__init__.py +++ b/client/tests/__init__.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/tests/test_contracts.py b/client/tests/test_contracts.py deleted file mode 100644 index 275fbdc53..000000000 --- a/client/tests/test_contracts.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd -# -# SPDX-License-Identifier: LGPL-3.0+ - -""" -Tests for zeth.core.contracts module -""" - -from zeth.core.zksnark import ExtendedProof, Groth16 -from zeth.core.mixer_client import MixParameters -from zeth.core.encryption import generate_encryption_keypair, encrypt -from zeth.core.signing import gen_signing_keypair, sign, encode_vk_to_bytes -from zeth.core.constants import NOTE_LENGTH_BYTES -from unittest import TestCase -from secrets import token_bytes - - -class TestContracts(TestCase): - - def test_mix_parameters(self) -> None: - zksnark = Groth16() - - ext_proof = ExtendedProof( - proof=Groth16.proof_from_json_dict({ - "a": ["1234", "2345"], - "b": [["3456", "4567"], ["5678", "6789"]], - "c": ["789a", "89ab"], - }), - inputs=[ - "9abc", - "abcd", - "bcde", - "cdef", - ]) - sig_keypair = gen_signing_keypair() - sig_vk = sig_keypair.vk - sig = sign(sig_keypair.sk, bytes.fromhex("00112233")) - receiver_enc_keypair = generate_encryption_keypair() - ciphertexts = [ - encrypt(token_bytes(NOTE_LENGTH_BYTES), receiver_enc_keypair.k_pk), - encrypt(token_bytes(NOTE_LENGTH_BYTES), receiver_enc_keypair.k_pk), - ] - - mix_params = MixParameters(ext_proof, sig_vk, sig, ciphertexts) - - mix_params_json = mix_params.to_json() - mix_params_2 = MixParameters.from_json(zksnark, mix_params_json) - - self.assertEqual( - mix_params.extended_proof.to_json_dict(), - mix_params_2.extended_proof.to_json_dict()) - self.assertEqual( - encode_vk_to_bytes(mix_params.signature_vk), - encode_vk_to_bytes(mix_params_2.signature_vk)) - self.assertEqual(mix_params.signature, mix_params_2.signature) - self.assertEqual(mix_params.ciphertexts, mix_params_2.ciphertexts) diff --git a/client/tests/test_encryption.py b/client/tests/test_encryption.py index d9750e328..2ff86b710 100644 --- a/client/tests/test_encryption.py +++ b/client/tests/test_encryption.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/tests/test_ethervalue.py b/client/tests/test_ethervalue.py index ef0be2701..e26d9be16 100644 --- a/client/tests/test_ethervalue.py +++ b/client/tests/test_ethervalue.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/tests/test_input_hasher.py b/client/tests/test_input_hasher.py new file mode 100644 index 000000000..cc7c4b99e --- /dev/null +++ b/client/tests/test_input_hasher.py @@ -0,0 +1,44 @@ +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd +# +# SPDX-License-Identifier: LGPL-3.0+ + +from zeth.core.mimc import MiMCAltBN128, MiMCBLS12_377 +from zeth.core.input_hasher import InputHasher +from unittest import TestCase + +DUMMY_INPUT_VALUES = [-1, 0, 1] + + +class TestInputHasher(TestCase): + + def test_input_hasher_simple(self) -> None: + # Some very simple cases + mimc = MiMCAltBN128() + input_hasher = InputHasher(mimc, 7) + self.assertEqual(mimc.hash_int(7, 0), input_hasher.hash([])) + self.assertEqual( + mimc.hash_int(mimc.hash_int(7, 1), 1), input_hasher.hash([1])) + self.assertEqual( + mimc.hash_int( + mimc.hash_int( + mimc.hash_int(7, 1), 2), + 2), + input_hasher.hash([1, 2])) + + def test_input_hasher_mimc_alt_bn128(self) -> None: + mimc = MiMCAltBN128() + input_hasher = InputHasher(mimc) + values = [x % mimc.prime for x in DUMMY_INPUT_VALUES] + # pylint:disable=line-too-long + expect = 1147542777914688064255377945014225852776952826405497760158376896026758431650 # noqa + # pylint:enable=line-too-long + self.assertEqual(expect, input_hasher.hash(values)) + + def test_input_hasher_mimc_bls12_377(self) -> None: + mimc = MiMCBLS12_377() + input_hasher = InputHasher(mimc) + values = [x % mimc.prime for x in DUMMY_INPUT_VALUES] + # pylint: disable=line-too-long + expect = 3481757288350338818975783012202519902801563645563026508811358096682731778741 # noqa + # pylint: enable=line-too-long + self.assertEqual(expect, input_hasher.hash(values)) diff --git a/client/tests/test_joinsplit.py b/client/tests/test_joinsplit.py index f137ff3ab..7722c5bc9 100644 --- a/client/tests/test_joinsplit.py +++ b/client/tests/test_joinsplit.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/tests/test_merkle_tree.py b/client/tests/test_merkle_tree.py index 45e511407..43e58a255 100644 --- a/client/tests/test_merkle_tree.py +++ b/client/tests/test_merkle_tree.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -6,7 +6,7 @@ from zeth.core.merkle_tree import MerkleTree, PersistentMerkleTree, ZERO_ENTRY, \ compute_merkle_path from zeth.core.utils import extend_32bytes -from zeth.core.mimc import MiMC7 +from zeth.core.mimc import MiMCAltBN128 from os.path import exists, join from os import makedirs from shutil import rmtree @@ -16,7 +16,7 @@ MERKLE_TREE_TEST_DIR = "_merkle_tests" MERKLE_TREE_TEST_DEPTH = 4 MERKLE_TREE_TEST_NUM_LEAVES = pow(2, MERKLE_TREE_TEST_DEPTH) -MERKLE_TREE_HASH = MiMC7() +MERKLE_TREE_HASH = MiMCAltBN128() TEST_VALUES = [ extend_32bytes(i.to_bytes(1, 'big')) for i in range(1, MERKLE_TREE_TEST_NUM_LEAVES)] @@ -43,7 +43,7 @@ def test_combine(self) -> None: right = self._test_vector_to_bytes32( 15683951496311901749339509118960676303290224812129752890706581988986633412003) # noqa expect = self._test_vector_to_bytes32( - 16797922449555994684063104214233396200599693715764605878168345782964540311877) # noqa + 449619487941393604225985437455969025878050402333083242690002834641114429734) # noqa result = MERKLE_TREE_HASH.hash(left, right) self.assertEqual(expect, result) @@ -147,7 +147,7 @@ def _expected_empty(self) -> bytes: self.assertEqual(16, MERKLE_TREE_TEST_NUM_LEAVES) # Test vector generated by test_mimc.py return self._test_vector_to_bytes32( - 1792447880902456454889084480864374954299744757125100424674028184042059183092) # noqa + 20048582095008167683805983195827846276103487741417695738966466927999203944276) # noqa def _check_merkle_path( self, address: int, mkpath: List[str], mktree: MerkleTree) -> None: diff --git a/client/tests/test_mimc.py b/client/tests/test_mimc.py index a6d3e8994..70ef0328d 100644 --- a/client/tests/test_mimc.py +++ b/client/tests/test_mimc.py @@ -1,8 +1,8 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ -from zeth.core.mimc import MiMC7, MiMC31 +from zeth.core.mimc import MiMCAltBN128, MiMCBLS12_377 from unittest import TestCase # pylint: disable=line-too-long @@ -10,37 +10,41 @@ class TestMiMC(TestCase): - def test_mimc7_round(self) -> None: - mimc = MiMC7("Clearmatics") + def test_mimc_alt_bn128_round(self) -> None: + mimc = MiMCAltBN128("Clearmatics") msg = 340282366920938463463374607431768211456 key = 28948022309329048855892746252171976963317496166410141009864396001978282409983 # noqa round_const = 14220067918847996031108144435763672811050758065945364308986253046354060608451 # noqa - expect_result = 7970444205539657036866618419973693567765196138501849736587140180515018751924 # noqa + expect_result = 15194574649778181158537940501307832704788048781286507777438072456493095881604 # noqa self.assertEqual(expect_result, mimc.mimc_round(msg, key, round_const)) - def test_mimc7(self) -> None: - left = 28948022309329048855892746252171976963317496166410141009864396001978282409983 # noqa - right = 14220067918847996031108144435763672811050758065945364308986253046354060608451 # noqa - expect_result = 14404914332179247771191118015445305957789480573634910846417052002923707343766 # noqa - result = MiMC7().hash(_int_to_bytes(left), _int_to_bytes(right)) - self.assertEqual(_int_to_bytes(expect_result), result) + def test_mimc_alt_bn128_hash(self) -> None: + left = 340282366920938463463374607431768211456 + right = 28948022309329048855892746252171976963317496166410141009864396001978282409983 # noqa + expect_result = 14599678357063082723814206975733222579132256174923645170354481857040188426666 # noqa + result = MiMCAltBN128().hash(_int_to_bytes(left), _int_to_bytes(right)) + self.assertEqual(expect_result, _bytes_to_int(result)) - def test_mimc31_round(self) -> None: + def test_mimc_bls12_377_round(self) -> None: msg = 340282366920938463463374607431768211456 key = 3614637061043937583146271435827337369189798160947949526058695634226054692860 # noqa round_const = 5775606169419625606859319496982126279674858730791300481051019590436651369410 # noqa - expect_result = 5523634951166384704739554074217840169048851347397743343350526776025419511991 # noqa + expect_result = 706529233840138407487116494744828417642056684171152884149736992660816802274 # noqa self.assertEqual( - expect_result, MiMC31().mimc_round(msg, key, round_const)) + expect_result, MiMCBLS12_377().mimc_round(msg, key, round_const)) - def test_mimc31(self) -> None: + def test_mimc_bls12_377_hash(self) -> None: left = 3614637061043937583146271435827337369189798160947949526058695634226054692860 # noqa right = 5775606169419625606859319496982126279674858730791300481051019590436651369410 # noqa - expect_result = 7575204549404107478830739557698679330537656688050664462892741835534561279075 # noqa - result = MiMC31().hash(_int_to_bytes(left), _int_to_bytes(right)) - self.assertEqual(_int_to_bytes(expect_result), result) + expect_result = 5803106354831571205534057512593837953191890709037390680911925249983717812220 # noqa + result = MiMCBLS12_377().hash(_int_to_bytes(left), _int_to_bytes(right)) + self.assertEqual(expect_result, _bytes_to_int(result)) # pylint: enable=line-too-long def _int_to_bytes(value: int) -> bytes: return value.to_bytes(32, byteorder='big') + + +def _bytes_to_int(value: bytes) -> int: + return int.from_bytes(value, byteorder='big') diff --git a/client/tests/test_mixer_client.py b/client/tests/test_mixer_client.py index 3115d7304..256b6c769 100644 --- a/client/tests/test_mixer_client.py +++ b/client/tests/test_mixer_client.py @@ -1,11 +1,11 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ from zeth.core.zksnark import ExtendedProof, Groth16 from zeth.core.mixer_client import MixParameters from zeth.core.encryption import generate_encryption_keypair, encrypt -from zeth.core.signing import gen_signing_keypair, sign, encode_vk_to_bytes +from zeth.core.signing import gen_signing_keypair, sign from zeth.core.constants import NOTE_LENGTH_BYTES from unittest import TestCase from secrets import token_bytes @@ -28,6 +28,7 @@ def test_mix_parameters(self) -> None: "bcde", "cdef", ]) + public_data = [1234, 4321, 9876, 6789] sig_keypair = gen_signing_keypair() sig_vk = sig_keypair.vk sig = sign(sig_keypair.sk, bytes.fromhex("00112233")) @@ -37,7 +38,8 @@ def test_mix_parameters(self) -> None: encrypt(token_bytes(NOTE_LENGTH_BYTES), receiver_enc_keypair.k_pk), ] - mix_params = MixParameters(ext_proof, sig_vk, sig, ciphertexts) + mix_params = MixParameters( + ext_proof, public_data, sig_vk, sig, ciphertexts) mix_params_json = mix_params.to_json() mix_params_2 = MixParameters.from_json(zksnark, mix_params_json) @@ -46,7 +48,7 @@ def test_mix_parameters(self) -> None: mix_params.extended_proof.to_json_dict(), mix_params_2.extended_proof.to_json_dict()) self.assertEqual( - encode_vk_to_bytes(mix_params.signature_vk), - encode_vk_to_bytes(mix_params_2.signature_vk)) + mix_params.signature_vk.to_bytes(), + mix_params_2.signature_vk.to_bytes()) self.assertEqual(mix_params.signature, mix_params_2.signature) self.assertEqual(mix_params.ciphertexts, mix_params_2.ciphertexts) diff --git a/client/tests/test_pairing.py b/client/tests/test_pairing.py index e2b4821d6..12e40a159 100644 --- a/client/tests/test_pairing.py +++ b/client/tests/test_pairing.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/tests/test_signing.py b/client/tests/test_signing.py index 9d43df6eb..25a3a4f76 100644 --- a/client/tests/test_signing.py +++ b/client/tests/test_signing.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -44,6 +44,15 @@ def test_signature_encoding(self) -> None: """ m = sha256("clearmatics".encode()).digest() sig = signing.sign(self.keypair.sk, m) - sig_encoded = signing.encode_signature_to_bytes(sig) - sig_decoded = signing.decode_signature_from_bytes(sig_encoded) + sig_encoded = signing.signature_to_bytes(sig) + sig_decoded = signing.signature_from_bytes(sig_encoded) self.assertEqual(sig, sig_decoded) + + def test_keypair_encode_decode(self) -> None: + """ + Test encoding and decoding of key pair + """ + keypair = signing.gen_signing_keypair() + keypair_json = keypair.to_json_dict() + keypair2 = signing.SigningKeyPair.from_json_dict(keypair_json) + self.assertEqual(keypair_json, keypair2.to_json_dict()) diff --git a/client/tests/test_zksnark.py b/client/tests/test_zksnark.py index 8afe5f494..73fe37c28 100644 --- a/client/tests/test_zksnark.py +++ b/client/tests/test_zksnark.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/__init__.py b/client/zeth/__init__.py index b6e016d34..fca72ddb7 100644 --- a/client/zeth/__init__.py +++ b/client/zeth/__init__.py @@ -1,3 +1,3 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/api/__init__.py b/client/zeth/api/__init__.py index b6e016d34..fca72ddb7 100644 --- a/client/zeth/api/__init__.py +++ b/client/zeth/api/__init__.py @@ -1,3 +1,3 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/api/py.typed b/client/zeth/api/py.typed index a0c5d77b1..58db294ed 100644 --- a/client/zeth/api/py.typed +++ b/client/zeth/api/py.typed @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/cli/__init__.py b/client/zeth/cli/__init__.py index b6e016d34..fca72ddb7 100644 --- a/client/zeth/cli/__init__.py +++ b/client/zeth/cli/__init__.py @@ -1,3 +1,3 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/cli/constants.py b/client/zeth/cli/constants.py index b7df75cdc..598dd677e 100644 --- a/client/zeth/cli/constants.py +++ b/client/zeth/cli/constants.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/cli/py.typed b/client/zeth/cli/py.typed index a0c5d77b1..58db294ed 100644 --- a/client/zeth/cli/py.typed +++ b/client/zeth/cli/py.typed @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/cli/utils.py b/client/zeth/cli/utils.py index 78fddac44..827db4162 100644 --- a/client/zeth/cli/utils.py +++ b/client/zeth/cli/utils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -21,7 +21,7 @@ import json from os.path import exists, join, splitext from web3 import Web3 # type: ignore -from typing import Dict, Tuple, Optional, Callable, Any +from typing import Dict, Tuple, Optional, Callable, Any, cast class NetworkConfig: @@ -118,6 +118,20 @@ def open_web3_from_network(eth_net: NetworkConfig) -> Any: insecure=eth_net.insecure) +def load_contract_address(contract_addr: str) -> str: + """ + Parse a string as either an eth address, or a contract instance file. + """ + if contract_addr.startswith("0x"): + return Web3.toChecksumAddress(contract_addr) + if exists(contract_addr): + with open(contract_addr, "r") as instance_f: + instance = InstanceDescription.from_json_dict(json.load(instance_f)) + return Web3.toChecksumAddress(instance.address) + raise ClickException( + f"failed to parse as address or instance file: {contract_addr}") + + def open_web3_from_ctx(ctx: ClientConfig) -> Any: eth_net = get_eth_network(ctx.eth_network) return open_web3_from_network(eth_net) @@ -126,31 +140,48 @@ def open_web3_from_ctx(ctx: ClientConfig) -> Any: class MixerDescription: """ Holds an InstanceDescription for the mixer contract, and optionally an - InstanceDescription for the token contract. + InstanceDescription for the token contract. When serialized to json, the + InstanceDescription for the mixer is held in the top-level object, so that + MixerDescription is compatible with a regular contract instance. """ def __init__( self, mixer: InstanceDescription, - token: Optional[InstanceDescription]): + token: Optional[InstanceDescription], + permitted_dispatcher: Optional[str], + vk_hash: Optional[str]): self.mixer = mixer self.token = token + self.permitted_dispatcher = permitted_dispatcher + self.vk_hash = vk_hash - def to_json(self) -> str: - json_dict = { - "mixer": self.mixer.to_json_dict() - } + def to_json_dict(self) -> Dict[str, Any]: + # Create an InstanceDescription JSON object, adding a "zeth_mixer" + # attribute for the extra data. + json_dict = self.mixer.to_json_dict() + + zeth_mixer: Dict[str, Any] = {} if self.token: - json_dict["token"] = self.token.to_json_dict() - return json.dumps(json_dict) + zeth_mixer["token"] = self.token.to_json_dict() + if self.permitted_dispatcher: + zeth_mixer["permitted_dispatcher"] = self.permitted_dispatcher + if self.vk_hash: + zeth_mixer["vk_hash"] = self.vk_hash + json_dict["zeth_mixer"] = zeth_mixer + return json_dict @staticmethod - def from_json(json_str: str) -> MixerDescription: - json_dict = json.loads(json_str) - mixer = InstanceDescription.from_json_dict(json_dict["mixer"]) - token_dict = json_dict.get("token", None) + def from_json_dict(json_dict: Dict[str, Any]) -> MixerDescription: + zeth_mixer = json_dict["zeth_mixer"] + + mixer = InstanceDescription.from_json_dict(json_dict) + token_dict = cast(Optional[Dict[str, Any]], zeth_mixer.get("token", None)) token = InstanceDescription.from_json_dict(token_dict) \ if token_dict else None - return MixerDescription(mixer, token) + permitted_dispatcher = \ + cast(Optional[str], zeth_mixer.get("permitted_dispatcher", None)) + vk_hash = cast(Optional[str], zeth_mixer.get("vk_hash", None)) + return MixerDescription(mixer, token, permitted_dispatcher, vk_hash) def get_erc20_abi() -> Dict[str, Any]: @@ -175,15 +206,15 @@ def write_mixer_description( Write the mixer (and token) instance information """ with open(mixer_desc_file, "w") as instance_f: - instance_f.write(mixer_desc.to_json()) + json.dump(mixer_desc.to_json_dict(), instance_f) -def load_mixer_description(mixer_description_file: str) -> MixerDescription: +def load_mixer_description(mixer_desc_file: str) -> MixerDescription: """ Return mixer and token (if present) contract instances """ - with open(mixer_description_file, "r") as desc_f: - return MixerDescription.from_json(desc_f.read()) + with open(mixer_desc_file, "r") as desc_f: + return MixerDescription.from_json_dict(json.load(desc_f)) def load_mixer_description_from_ctx(ctx: ClientConfig) -> MixerDescription: diff --git a/client/zeth/cli/zeth b/client/zeth/cli/zeth index ba248de18..aaa1a8ddd 100644 --- a/client/zeth/cli/zeth +++ b/client/zeth/cli/zeth @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -9,10 +9,12 @@ from zeth.cli.constants import \ INSTANCE_FILE_DEFAULT, ZETH_SECRET_ADDRESS_FILE_DEFAULT, WALLET_DIR_DEFAULT, \ ETH_NETWORK_FILE_DEFAULT, ETH_NETWORK_DEFAULT from zeth.cli.utils import ClientConfig +from zeth.cli.zeth_get_verification_key import get_verification_key from zeth.cli.zeth_deploy import deploy from zeth.cli.zeth_gen_address import gen_address from zeth.cli.zeth_sync import sync from zeth.cli.zeth_mix import mix +from zeth.cli.zeth_wait import wait from zeth.cli.zeth_ls_notes import ls_notes from zeth.cli.zeth_ls_commits import ls_commits from click import group, command, option, pass_context, ClickException, Context @@ -80,10 +82,12 @@ def zeth( wallet_dir=wallet_dir) +zeth.add_command(get_verification_key) zeth.add_command(deploy) zeth.add_command(gen_address) zeth.add_command(sync) zeth.add_command(mix) +zeth.add_command(wait) zeth.add_command(ls_notes) zeth.add_command(ls_commits) zeth.add_command(help) diff --git a/client/zeth/cli/zeth_deploy.py b/client/zeth/cli/zeth_deploy.py index 49dbeb162..729f003a3 100644 --- a/client/zeth/cli/zeth_deploy.py +++ b/client/zeth/cli/zeth_deploy.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -6,9 +6,9 @@ from zeth.cli.utils import \ open_web3_from_ctx, get_erc20_instance_description, load_eth_address, \ load_eth_private_key, write_mixer_description, MixerDescription, \ - create_prover_client + create_prover_client, load_contract_address from zeth.core.mixer_client import MixerClient -from click import Context, command, option, pass_context +from click import Context, command, option, pass_context, ClickException from typing import Optional @@ -20,6 +20,10 @@ default=INSTANCE_FILE_DEFAULT, help=f"File to write deployment address to (default={INSTANCE_FILE_DEFAULT})") @option("--token-address", help="Address of token contract (if used)") +@option( + "--permitted-dispatcher", + help="Instance file or address of contract permitted to call dispatch method") +@option("--vk-hash", type=str, help="verification key hash for dispatch calls") @option("--deploy-gas", type=int, help="Maximum gas, in Wei") @pass_context def deploy( @@ -27,7 +31,9 @@ def deploy( eth_addr: Optional[str], eth_private_key: Optional[str], instance_out: str, - token_address: str, + token_address: Optional[str], + permitted_dispatcher: Optional[str], + vk_hash: Optional[str], deploy_gas: Optional[int]) -> None: """ Deploy the zeth contracts and record the instantiation details. @@ -37,10 +43,19 @@ def deploy( client_ctx = ctx.obj web3 = open_web3_from_ctx(client_ctx) + if bool(permitted_dispatcher) != bool(vk_hash): + raise ClickException( + "Must supply BOTH --permitted-dispatch AND --vk-hash, or NEITHER") + print(f"deploy: eth_address={eth_address}") print(f"deploy: instance_out={instance_out}") print(f"deploy: token_address={token_address}") + if permitted_dispatcher: + permitted_dispatcher = load_contract_address(permitted_dispatcher) + print(f"deploy: permitted_dispatcher={permitted_dispatcher}") + print(f"deploy: vk_hash={vk_hash}") + token_instance_desc = get_erc20_instance_description(token_address) \ if token_address else None @@ -51,7 +66,13 @@ def deploy( eth_address, eth_private_key_data, token_address, - deploy_gas) + permitted_dispatcher=permitted_dispatcher, + vk_hash=vk_hash, + deploy_gas=deploy_gas) - mixer_desc = MixerDescription(mixer_instance_desc, token_instance_desc) + mixer_desc = MixerDescription( + mixer=mixer_instance_desc, + token=token_instance_desc, + permitted_dispatcher=permitted_dispatcher, + vk_hash=vk_hash) write_mixer_description(instance_out, mixer_desc) diff --git a/client/zeth/cli/zeth_gen_address.py b/client/zeth/cli/zeth_gen_address.py index 28702210f..75783fd03 100644 --- a/client/zeth/cli/zeth_gen_address.py +++ b/client/zeth/cli/zeth_gen_address.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/cli/zeth_get_verification_key.py b/client/zeth/cli/zeth_get_verification_key.py new file mode 100644 index 000000000..662728163 --- /dev/null +++ b/client/zeth/cli/zeth_get_verification_key.py @@ -0,0 +1,31 @@ +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd +# +# SPDX-License-Identifier: LGPL-3.0+ + +from click import command, option, Context, pass_context + +from zeth.cli.utils import create_prover_client +import json +from typing import Optional + + +@command() +@option("--vk-out", "-o", help="Output file") +@pass_context +def get_verification_key(ctx: Context, vk_out: Optional[str]) -> None: + """ + Command help text. + """ + + # Get the VK (proto object) + client_ctx = ctx.obj + prover_client = create_prover_client(client_ctx) + vk = prover_client.get_verification_key() + vk_json = vk.to_json_dict() + + # Write the json to stdout or a file + if vk_out: + with open(vk_out, "w") as vk_f: + json.dump(vk_json, vk_f) + else: + print(json.dumps(vk_json)) diff --git a/client/zeth/cli/zeth_ls_commits.py b/client/zeth/cli/zeth_ls_commits.py index f0d835bf4..e658597d3 100644 --- a/client/zeth/cli/zeth_ls_commits.py +++ b/client/zeth/cli/zeth_ls_commits.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/cli/zeth_ls_notes.py b/client/zeth/cli/zeth_ls_notes.py index 961707395..b7d63509a 100644 --- a/client/zeth/cli/zeth_ls_notes.py +++ b/client/zeth/cli/zeth_ls_notes.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/cli/zeth_mix.py b/client/zeth/cli/zeth_mix.py index e35c8deb0..7025f62dc 100644 --- a/client/zeth/cli/zeth_mix.py +++ b/client/zeth/cli/zeth_mix.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -11,6 +11,7 @@ from zeth.api.zeth_messages_pb2 import ZethNote from click import command, option, pass_context, ClickException, Context from typing import List, Tuple, Optional +import json @command() @@ -26,7 +27,16 @@ @option("--eth-addr", help="Sender's eth address or address filename") @option("--eth-private-key", help="Sender's eth private key file") @option("--wait", is_flag=True, help="Wait for transaction to be mined") -@option("--show-parameters", is_flag=True, help="Show the mixer parameters") +@option( + "--for-dispatch-call", + is_flag=True, + help="Generate signature for later call to dispatch (implies --dry-run)") +@option("--dump-parameters", help="Write mix parameters to file ('-' for stdout)") +@option( + "--dump-signing-keypair", + help="Write signing keypair to file ('-' for stdout). " + "USE ONLY FOR DEBUGGING.") +@option("--dry-run", "-n", is_flag=True, help="Do not send the mix transaction") @pass_context def mix( ctx: Context, @@ -37,7 +47,10 @@ def mix( eth_addr: Optional[str], eth_private_key: Optional[str], wait: bool, - show_parameters: bool) -> None: + for_dispatch_call: bool, + dump_parameters: Optional[str], + dump_signing_keypair: Optional[str], + dry_run: bool) -> None: """ Generic mix function """ @@ -73,7 +86,6 @@ def mix( raise ClickException("input and output value mismatch") eth_address = load_eth_address(eth_addr) - eth_private_key_data = load_eth_private_key(eth_private_key) # If instance uses an ERC20 token, tx_value can be 0. Otherwise it should # match vin_pub. @@ -81,19 +93,39 @@ def mix( # Create the MixParameters object manually so they can be displayed. # TODO: support saving the generated MixParameters to be sent later. - mix_params, _ = zeth_client.create_mix_parameters_and_signing_key( - prover_client, - wallet.merkle_tree, - zeth_address.ownership_keypair(), - eth_address, - inputs, - outputs, - vin_pub, - vout_pub) + mix_params, signing_keypair = \ + zeth_client.create_mix_parameters_and_signing_key( + prover_client, + wallet.merkle_tree, + zeth_address.ownership_keypair(), + eth_address, + inputs, + outputs, + vin_pub, + vout_pub, + for_dispatch_call=for_dispatch_call) - if show_parameters: - print(f"mix_params={mix_params.to_json()}") + # Dump parameters if requested + if dump_parameters: + if dump_parameters == '-': + print(f"mix_params={mix_params.to_json()}") + else: + with open(dump_parameters, "w") as mix_params_f: + json.dump(mix_params.to_json_dict(), mix_params_f) + # Dump one-time signature keypair if requested + if dump_signing_keypair: + if dump_signing_keypair == '-': + print(f"signing_key={signing_keypair.to_json_dict()}") + else: + with open(dump_signing_keypair, "w") as signing_keypair_f: + json.dump(signing_keypair.to_json_dict(), signing_keypair_f) + + # Early-out if dry_run flag is set + if for_dispatch_call or dry_run: + return + + eth_private_key_data = load_eth_private_key(eth_private_key) tx_hash = zeth_client.mix( mix_params=mix_params, sender_eth_address=eth_address, diff --git a/client/zeth/cli/zeth_sync.py b/client/zeth/cli/zeth_sync.py index 2954806ff..929f5ff93 100644 --- a/client/zeth/cli/zeth_sync.py +++ b/client/zeth/cli/zeth_sync.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/cli/zeth_wait.py b/client/zeth/cli/zeth_wait.py new file mode 100644 index 000000000..066cd841d --- /dev/null +++ b/client/zeth/cli/zeth_wait.py @@ -0,0 +1,37 @@ +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd +# +# SPDX-License-Identifier: LGPL-3.0+ + +from zeth.cli.utils import load_mixer_description_from_ctx, open_web3_from_ctx +from zeth.core.contracts import get_event_logs_from_tx_receipt +from click import command, pass_context, Context, argument + + +@command() +@argument("transaction-id") +@pass_context +def wait(ctx: Context, transaction_id: str) -> None: + """ + Wait for a mix transaction and dump all log data. Does not update the + wallet. Use sync to scan the chain for new notes. + """ + client_ctx = ctx.obj + mixer_desc = load_mixer_description_from_ctx(client_ctx) + instance_desc = mixer_desc.mixer + + # Retrieve the tx receipt and dump logs + web3 = open_web3_from_ctx(client_ctx) # type: ignore + tx_receipt = web3.eth.waitForTransactionReceipt(transaction_id, 10000) \ + # pylint: disable=no-member + + print("LogDebug events:") + logs = get_event_logs_from_tx_receipt(instance_desc, "LogDebug", tx_receipt) + for log in logs: + print( + f" {log.args['message']}: {log.args['value']} " + f"({hex(log.args['value'])})") + + print("LogMix events:") + logs = get_event_logs_from_tx_receipt(instance_desc, "LogMix", tx_receipt) + for log in logs: + print(f" {log}") diff --git a/client/zeth/core/__init__.py b/client/zeth/core/__init__.py index 6fb9229d2..f50145434 100644 --- a/client/zeth/core/__init__.py +++ b/client/zeth/core/__init__.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/core/constants.py b/client/zeth/core/constants.py index a90a804c6..4e46d3032 100644 --- a/client/zeth/core/constants.py +++ b/client/zeth/core/constants.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -56,7 +56,7 @@ def bit_length_to_byte_length(bit_length: int) -> int: # Public value length (v_pub_in and v_pub_out) PUBLIC_VALUE_LENGTH: int = 64 PUBLIC_VALUE_LENGTH_BYTES: int = bit_length_to_byte_length(PUBLIC_VALUE_LENGTH) -PUBLIC_VALUE_MASK: int = (1 << PUBLIC_VALUE_LENGTH) - 1 +_PUBLIC_VALUE_MASK: int = (1 << PUBLIC_VALUE_LENGTH) - 1 PHI_LENGTH: int = 256 PHI_LENGTH_BYTES: int = bit_length_to_byte_length(PHI_LENGTH) @@ -73,7 +73,7 @@ def bit_length_to_byte_length(bit_length: int) -> int: NOTE_LENGTH: int = APK_LENGTH + PUBLIC_VALUE_LENGTH + RHO_LENGTH + TRAPR_LENGTH NOTE_LENGTH_BYTES: int = bit_length_to_byte_length(NOTE_LENGTH) -# Public inputs are (see BaseMixer.sol): +# Public inputs are (see AbstractMixer.sol): # [0 ] - 1 x merkle root # [1 ] - jsOut x commitment # [1 + jsOut ] - jsIn x nullifier (partial) @@ -88,7 +88,7 @@ def bit_length_to_byte_length(bit_length: int) -> int: NUM_INPUT_DIGESTS: int = (2 * JS_INPUTS) + 1 # Solidity compiler version -SOL_COMPILER_VERSION: str = 'v0.5.16' +SOL_COMPILER_VERSION: str = 'v0.8.1' # Seed for MIMC MIMC_MT_SEED: str = "clearmatics_mt_seed" diff --git a/client/zeth/core/contracts.py b/client/zeth/core/contracts.py index b86292b69..85201c57e 100644 --- a/client/zeth/core/contracts.py +++ b/client/zeth/core/contracts.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -9,6 +9,7 @@ from zeth.core.constants import SOL_COMPILER_VERSION from web3.utils.contracts import find_matching_event_abi # type: ignore from web3.utils.events import get_event_data # type: ignore +from eth_utils import event_abi_to_log_topic # type: ignore import solcx from typing import Dict, List, Iterator, Optional, Union, Iterable, Any @@ -140,7 +141,6 @@ def send_contract_call( between hosted keys (sender_eth_private_key is None) and local keys (sender_eth_private_key is not None). Returns the hash of the broadcast transaction. - """ tx_desc: Dict[str, Union[str, int]] = {'from': sender_eth_addr} if value: @@ -188,8 +188,21 @@ def get_event_logs( instance, with the given name. Yields an iterator of event-specific objects to be decoded by the caller. """ + + # It is possible to achieve this via the contract interface, with code of + # the form: + # + # event = instance.events[event_name] + # filter = event.createFilter(fromBlock=start_block, toBlock=to_block) + # logs = web3.eth.getFilterLogs(filter) + # + # However, this creates filters on the host node, which may not be + # permitted in all configurations. Hence, the code here iterates manually, + # skpping events with other topics, from the same contract. + contract_address = instance.address event_abi = find_matching_event_abi(instance.abi, event_name=event_name) + log_topic = event_abi_to_log_topic(event_abi) batch_size = batch_size or SYNC_BLOCKS_PER_BATCH while start_block <= end_block: @@ -203,5 +216,24 @@ def get_event_logs( } logs = web3.eth.getLogs(filter_params) for log in logs: - yield get_event_data(event_abi, log) + if log_topic == log['topics'][0]: + yield get_event_data(event_abi, log) start_block = to_block + 1 + + +def get_event_logs_from_tx_receipt( + instance_desc: InstanceDescription, + event_name: str, + tx_receipt: Any) -> Iterator[Any]: + """ + Query a transaction receipt for all events emitted by the given contract + instance with a given event name. Yields an iterator of event-specific + objects to be decoded by the caller. This function intentionally avoids + connecting to a node, or creating host-side filters. + """ + contract_address = instance_desc.address + event_abi = find_matching_event_abi(instance_desc.abi, event_name=event_name) + log_topic = event_abi_to_log_topic(event_abi) + for log in tx_receipt.logs: + if log.address == contract_address and log_topic == log['topics'][0]: + yield get_event_data(event_abi, log) diff --git a/client/zeth/core/encryption.py b/client/zeth/core/encryption.py index 7bd636ad2..0d3367fc5 100644 --- a/client/zeth/core/encryption.py +++ b/client/zeth/core/encryption.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/core/errors.py b/client/zeth/core/errors.py index a9fdb2896..7906354de 100644 --- a/client/zeth/core/errors.py +++ b/client/zeth/core/errors.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/core/input_hasher.py b/client/zeth/core/input_hasher.py new file mode 100644 index 000000000..09119064f --- /dev/null +++ b/client/zeth/core/input_hasher.py @@ -0,0 +1,36 @@ +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd +# +# SPDX-License-Identifier: LGPL-3.0+ + +from zeth.core.mimc import MiMCBase +from typing import List + + +# Default seed, generated as: +# zeth.core.mimc._keccak_256( +# zeth.core.mimc._str_to_bytes("clearmatics_hash_seed")) +DEFAULT_IV_UINT256 = \ + 13196537064117388418196223856311987714388543839552400408340921397545324034315 + + +class InputHasher: + """ + Note that this is currently experimental code. Hash a series of field + elements via the Merkle-Damgard construction on a MiMC compression + function. Note that since this function only accepts whole numbers of + scalar field elements, there is no ambiguity w.r.t to padding and we could + technically omit the finalization step. It has been kept for now, to allow + time for further consideration, and in case the form of the hasher changes + (e.g. in case we want to be able to hash arbitrary bit strings in the + future). + """ + def __init__(self, compression_fn: MiMCBase, iv: int = DEFAULT_IV_UINT256): + assert compression_fn.prime < (2 << 256) + self._compression_fn = compression_fn + self._iv = iv % compression_fn.prime + + def hash(self, values: List[int]) -> int: + current = self._iv + for m in values: + current = self._compression_fn.hash_int(current, m) + return self._compression_fn.hash_int(current, len(values)) diff --git a/client/zeth/core/merkle_tree.py b/client/zeth/core/merkle_tree.py index beecf66ba..db97af6c4 100644 --- a/client/zeth/core/merkle_tree.py +++ b/client/zeth/core/merkle_tree.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/core/mimc.py b/client/zeth/core/mimc.py index 23993fd08..8585cf519 100644 --- a/client/zeth/core/mimc.py +++ b/client/zeth/core/mimc.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -61,52 +61,60 @@ def hash(self, left: bytes, right: bytes) -> bytes: """ x = int.from_bytes(left, byteorder='big') % self.prime y = int.from_bytes(right, byteorder='big') % self.prime - result = (self.encrypt(x, y) + x + y) % self.prime - return result.to_bytes(32, byteorder='big') + return self.hash_int(x, y).to_bytes(32, byteorder='big') + + def hash_int(self, x: int, y: int) -> int: + """ + Similar to hash, but use field elements directly. + """ + assert x < self.prime + assert y < self.prime + return (self.encrypt(x, y) + x + y) % self.prime @abstractmethod def mimc_round(self, message: int, key: int, rc: int) -> int: pass -class MiMC7(MiMCBase): +class MiMC17Base(MiMCBase): + """ + Implementation of MiMCBase with exponent 17 + """ + def mimc_round(self, message: int, key: int, rc: int) -> int: + # May not be optimal to operate on huge numbers (256 * e bits). For + # reference, the manual version is below: + # a = (message + key + rc) % self.prime + # a_2 = (a * a) % self.prime + # a_4 = (a_2 * a_2) % self.prime + # a_8 = (a_4 * a_4) % self.prime + # a_16 = (a_8 * a_8) % self.prime + # return (a_16 * a) % self.prime + return ((message + key + rc) ** 17) % self.prime + + +class MiMCAltBN128(MiMC17Base): """ - MiMC specialized for Fr in ALT-BN128, in which the exponent is 7 and 91 - rounds are used. + MiMC specialized for Fr in ALT-BN128, using exponent 17 and 65 rounds. See + zeth specifications (Section 3.2) for details. """ def __init__(self, seed_str: str = MIMC_MT_SEED): - MiMCBase.__init__( - self, + super().__init__( seed_str, 21888242871839275222246405745257275088548364400416034343698204186575808495617, # noqa # pylint: disable=line-too-long - 91) - - def mimc_round(self, message: int, key: int, rc: int) -> int: - xored = (message + key + rc) % self.prime - return xored ** 7 % self.prime + 65) -class MiMC31(MiMCBase): +class MiMCBLS12_377(MiMC17Base): # pylint: disable=invalid-name """ - MiMC implementation using exponent of 31 and 51 rounds. Note that this is - suitable for BLS12-377, since 31=2^5-1, and 1 == gcd(31, r-1). See - [AGRRT16] for details. + MiMC specialized for Fr in BLS12-377, using exponent 17 and 62 rounds. See + zeth specifications (Section 3.2) for details. """ def __init__(self, seed_str: str = MIMC_MT_SEED): - MiMCBase.__init__( - self, + super().__init__( seed_str, 8444461749428370424248824938781546531375899335154063827935233455917409239041, # noqa - 51) - - def mimc_round(self, message: int, key: int, rc: int) -> int: - a = (message + key + rc) % self.prime - a_2 = (a * a) % self.prime - a_4 = (a_2 * a_2) % self.prime - a_8 = (a_4 * a_4) % self.prime - a_16 = (a_8 * a_8) % self.prime - return (a_16 * a_8 * a_4 * a_2 * a) % self.prime + 62) def get_tree_hash_for_pairing(pairing_name: str) -> ITreeHash: @@ -115,9 +123,9 @@ def get_tree_hash_for_pairing(pairing_name: str) -> ITreeHash: the selection logic in `libzeth/circuits/circuit_types.hpp`. """ if pairing_name == "alt-bn128": - return MiMC7() + return MiMCAltBN128() if pairing_name == "bls12-377": - return MiMC31() + return MiMCBLS12_377() raise Exception(f"no tree hash for pairing: {pairing_name}") diff --git a/client/zeth/core/mixer_client.py b/client/zeth/core/mixer_client.py index 04ba7d20e..f57ba21a1 100644 --- a/client/zeth/core/mixer_client.py +++ b/client/zeth/core/mixer_client.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -21,7 +21,7 @@ ExtendedProof from zeth.core.utils import EtherValue, digest_to_binary_string, \ int64_to_hex, message_to_bytes, eth_address_to_bytes32, to_zeth_units, \ - get_contracts_dir, hex_list_to_uint256_list + get_contracts_dir, hex_to_uint256_list from zeth.core.prover_client import ProverConfiguration, ProverClient from zeth.api.zeth_messages_pb2 import ZethNote, JoinsplitInput, ProofInputs @@ -30,6 +30,7 @@ from Crypto import Random from hashlib import blake2s, sha256 import traceback +import eth_abi from typing import Tuple, Dict, List, Iterator, Callable, Optional, Any @@ -102,23 +103,26 @@ class MixParameters: def __init__( self, extended_proof: ExtendedProof, + public_data: List[int], signature_vk: signing.SigningVerificationKey, signature: signing.Signature, ciphertexts: List[bytes]): self.extended_proof = extended_proof + self.public_data = public_data self.signature_vk = signature_vk self.signature = signature self.ciphertexts = ciphertexts @staticmethod def from_json(zksnark: IZKSnarkProvider, params_json: str) -> MixParameters: - return MixParameters._from_json_dict(zksnark, json.loads(params_json)) + return MixParameters.from_json_dict(zksnark, json.loads(params_json)) def to_json(self) -> str: - return json.dumps(self._to_json_dict()) + return json.dumps(self.to_json_dict()) - def _to_json_dict(self) -> Dict[str, Any]: + def to_json_dict(self) -> Dict[str, Any]: ext_proof_json = self.extended_proof.to_json_dict() + public_data = [hex(x) for x in self.public_data] signature_vk_json = [ str(x) for x in signing.verification_key_as_mix_parameter(self.signature_vk)] @@ -126,17 +130,19 @@ def _to_json_dict(self) -> Dict[str, Any]: ciphertexts_json = [x.hex() for x in self.ciphertexts] return { "extended_proof": ext_proof_json, + "public_data": public_data, "signature_vk": signature_vk_json, "signature": signature_json, "ciphertexts": ciphertexts_json, } @staticmethod - def _from_json_dict( + def from_json_dict( zksnark: IZKSnarkProvider, json_dict: Dict[str, Any]) -> MixParameters: ext_proof = ExtendedProof.from_json_dict( zksnark, json_dict["extended_proof"]) + public_data = [int(x, 16) for x in json_dict["public_data"]] signature_pk_param = [int(x) for x in json_dict["signature_vk"]] signature_pk = signing.verification_key_from_mix_parameter( signature_pk_param) @@ -144,7 +150,7 @@ def _from_json_dict( int(json_dict["signature"])) ciphertexts = [bytes.fromhex(x) for x in json_dict["ciphertexts"]] return MixParameters( - ext_proof, signature_pk, signature, ciphertexts) + ext_proof, public_data, signature_pk, signature, ciphertexts) def mix_parameters_to_contract_arguments( @@ -161,11 +167,28 @@ def mix_parameters_to_contract_arguments( proof_contract_params, signing.verification_key_as_mix_parameter(mix_parameters.signature_vk), signing.signature_as_mix_parameter(mix_parameters.signature), - hex_list_to_uint256_list(mix_parameters.extended_proof.inputs), + mix_parameters.public_data, mix_parameters.ciphertexts, ] +def mix_parameters_to_dispatch_parameters(mix_parameters: MixParameters) -> bytes: + """ + Encode parameters from mix_parameters into an array of uint256 values, + compatible with the `dispatch` method on Mixer. This conforms to the + `IZecaleApplication` solidity interface of Zecale + (https://github.com/clearmatics/zecale) + """ + vk_param = signing.verification_key_as_mix_parameter( + mix_parameters.signature_vk) + sigma_param = signing.signature_as_mix_parameter(mix_parameters.signature) + public_data = mix_parameters.public_data + ciphertexts = mix_parameters.ciphertexts + return eth_abi.encode_abi( + ['uint256[4]', 'uint256', 'uint256[]', 'bytes[]'], + [vk_param, sigma_param, public_data, ciphertexts]) # type: ignore + + class MixOutputEvents: """ Event data for a single joinsplit output. Holds address (in merkle tree), @@ -252,21 +275,24 @@ def deploy( deployer_eth_address: str, deployer_eth_private_key: Optional[bytes], token_address: Optional[str] = None, + permitted_dispatcher: Optional[str] = None, + vk_hash: Optional[str] = None, deploy_gas: Optional[int] = None ) -> Tuple[MixerClient, contracts.InstanceDescription]: """ Deploy Zeth contracts. """ prover_config = prover_client.get_configuration() - zksnark = get_zksnark_provider(prover_config.zksnark_name) - vk_proto = prover_client.get_verification_key() - pp = prover_config.pairing_parameters - vk = zksnark.verification_key_from_proto(vk_proto) + vk = prover_client.get_verification_key() deploy_gas = deploy_gas or constants.DEPLOYMENT_GAS_WEI contracts_dir = get_contracts_dir() + zksnark = get_zksnark_provider(prover_config.zksnark_name) + pp = prover_config.pairing_parameters mixer_name = zksnark.get_contract_name(pp) mixer_src = os.path.join(contracts_dir, mixer_name + ".sol") + vk_hash_evm = list(hex_to_uint256_list(vk_hash)) if vk_hash else [0, 0] + assert len(vk_hash_evm) == 2 # Constructor parameters have the form: # uint256 mk_depth @@ -276,6 +302,8 @@ def deploy( constants.ZETH_MERKLE_TREE_DEPTH, # mk_depth token_address or ZERO_ADDRESS, # token zksnark.verification_key_to_contract_parameters(vk, pp), # vk + permitted_dispatcher or ZERO_ADDRESS, # permitted_dispatcher + vk_hash_evm # vk_hash ] mixer_description = contracts.InstanceDescription.deploy( web3, @@ -492,8 +520,20 @@ def create_mix_parameters_from_proof( prover_inputs: ProofInputs, signing_keypair: signing.SigningKeyPair, ext_proof: ExtendedProof, - sender_eth_address: str + public_data: List[int], + sender_eth_address: str, + for_dispatch_call: bool = False ) -> MixParameters: + """ + Create the MixParameters from MixCallDescription, signing keypair, sender + address and derived data (prover inputs and proof). This includes + creating and encrypting the plaintext messages, and generating the + one-time signature. + + If for_dispatch_call is set, the parameters are to be passed to the + Mixer's `dispatch` call in a later operation (in which proof data is + not available), hence proof is ommitted from the signature. + """ # Encrypt the notes outputs_and_notes = zip(mix_call_desc.outputs, prover_inputs.js_outputs) \ @@ -511,10 +551,12 @@ def create_mix_parameters_from_proof( signing_keypair, sender_eth_address, ciphertexts, - ext_proof) + ext_proof, + public_data, + for_dispatch_call) mix_params = MixParameters( - ext_proof, signing_keypair.vk, signature, ciphertexts) + ext_proof, public_data, signing_keypair.vk, signature, ciphertexts) return mix_params def create_mix_parameters_and_signing_key( @@ -527,11 +569,15 @@ def create_mix_parameters_and_signing_key( outputs: List[Tuple[ZethAddressPub, EtherValue]], v_in: EtherValue, v_out: EtherValue, - compute_h_sig_cb: Optional[ComputeHSigCB] = None + compute_h_sig_cb: Optional[ComputeHSigCB] = None, + for_dispatch_call: bool = False ) -> Tuple[MixParameters, JoinsplitSigKeyPair]: """ Convenience function around creation of MixCallDescription, ProofInputs, - Proof and MixParameters. + Proof and MixParameters. If for_dispatch_call is set, the parameters + are to be passed to the Mixer's `dispatch` call in a later operation + (in which proof data is not available), hence proof is ommitted from + the signature. """ # Generate prover inputs and signing key mix_call_desc = MixCallDescription( @@ -545,11 +591,8 @@ def create_mix_parameters_and_signing_key( prover_inputs, signing_keypair = MixerClient.create_prover_inputs( mix_call_desc) - zksnark = get_zksnark_provider(self.prover_config.zksnark_name) - # Query the prover_server for the related proof - ext_proof_proto = prover_client.get_proof(prover_inputs) - ext_proof = zksnark.extended_proof_from_proto(ext_proof_proto) + ext_proof, public_data = prover_client.get_proof(prover_inputs) # Create the final MixParameters object mix_params = self.create_mix_parameters_from_proof( @@ -557,7 +600,9 @@ def create_mix_parameters_and_signing_key( prover_inputs, signing_keypair, ext_proof, - sender_eth_address) + public_data, + sender_eth_address, + for_dispatch_call) return mix_params, signing_keypair @@ -635,20 +680,25 @@ def joinsplit_sign( signing_keypair: JoinsplitSigKeyPair, sender_eth_address: str, ciphertexts: List[bytes], - extproof: ExtendedProof) -> int: - """ - Generate a signature on the hash of the ciphertexts, proofs and - primary inputs. This is used to solve transaction malleability. We chose - to sign the hash and not the values themselves for modularity (to use the - same code regardless of whether GROTH16 or PGHR13 proof system is chosen), - and sign the hash of the ciphers and inputs for consistency. + extproof: ExtendedProof, + public_data: List[int], + for_dispatch_call: bool = False) -> int: + """ + Generate a signature on the hash of the ciphertexts, proofs and primary + inputs. This is used to solve transaction malleability. We chose to sign + the hash and not the values themselves for modularity (to use the same code + regardless of whether GROTH16 or PGHR13 proof system is chosen), and sign + the hash of the ciphers and inputs for consistency. If for_dispatch_call is + set, the parameters are to be passed to the Mixer's `dispatch` call in a + later operation (in which proof data is not available), hence proof is + ommitted from the signature. """ assert len(ciphertexts) == constants.JS_INPUTS # The message to sign consists of (in order): # - senders Ethereum address # - ciphertexts - # - proof elements + # - proof elements (if for_dispatch_call is False) # - public input elements h = sha256() h.update(eth_address_to_bytes32(sender_eth_address)) @@ -656,8 +706,13 @@ def joinsplit_sign( h.update(ciphertext) proof_bytes, pub_inputs_bytes = _proof_and_inputs_to_bytes( - zksnark, pp, extproof) - h.update(proof_bytes) + zksnark, pp, extproof, public_data) + + # If for_dispatch_call is set, omit proof from the signature. See + # AbstractMixer.sol. + if not for_dispatch_call: + h.update(proof_bytes) + h.update(pub_inputs_bytes) message_digest = h.digest() return signing.sign(signing_keypair.sk, message_digest) @@ -707,7 +762,7 @@ def compute_h_sig( h = sha256() h.update(nf0) h.update(nf1) - h.update(signing.encode_vk_to_bytes(sign_vk)) + h.update(sign_vk.to_bytes()) return h.digest() @@ -746,7 +801,8 @@ def _create_zeth_notes( def _proof_and_inputs_to_bytes( snark: IZKSnarkProvider, pp: PairingParameters, - extproof: ExtendedProof) -> Tuple[bytes, bytes]: + extproof: ExtendedProof, + public_data: List[int]) -> Tuple[bytes, bytes]: """ Given a proof object, compute the byte encodings of the properties excluding "inputs", and the byte encoding of the "inputs". These are used @@ -757,7 +813,7 @@ def _proof_and_inputs_to_bytes( proof = extproof.proof return \ message_to_bytes(snark.proof_to_contract_parameters(proof, pp)), \ - message_to_bytes(hex_list_to_uint256_list(extproof.inputs)) + message_to_bytes(public_data) def _trap_r_randomness() -> str: diff --git a/client/zeth/core/ownership.py b/client/zeth/core/ownership.py index a684067f1..601dd4d70 100644 --- a/client/zeth/core/ownership.py +++ b/client/zeth/core/ownership.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/core/pairing.py b/client/zeth/core/pairing.py index d6d26b389..7ba927422 100644 --- a/client/zeth/core/pairing.py +++ b/client/zeth/core/pairing.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/core/proto_utils.py b/client/zeth/core/proto_utils.py index ea4db3484..a8f9def23 100644 --- a/client/zeth/core/proto_utils.py +++ b/client/zeth/core/proto_utils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/core/prover_client.py b/client/zeth/core/prover_client.py index 775d8b9bc..f4cb149df 100644 --- a/client/zeth/core/prover_client.py +++ b/client/zeth/core/prover_client.py @@ -1,13 +1,14 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ from __future__ import annotations -from .pairing import PairingParameters, pairing_parameters_from_proto +from zeth.core.zksnark import IZKSnarkProvider, get_zksnark_provider, \ + IVerificationKey, ExtendedProof +from zeth.core.pairing import PairingParameters, pairing_parameters_from_proto from zeth.api.zeth_messages_pb2 import ProofInputs -from zeth.api.snark_messages_pb2 import VerificationKey, ExtendedProof from zeth.api import prover_pb2 # type: ignore from zeth.api import prover_pb2_grpc # type: ignore import grpc # type: ignore @@ -15,7 +16,7 @@ from os import unlink import json from google.protobuf import empty_pb2 -from typing import Dict, Optional, Any +from typing import Dict, List, Tuple, Optional, Any class ProverConfiguration: @@ -93,27 +94,38 @@ def get_configuration(self) -> ProverConfiguration: return self.prover_config - def get_verification_key(self) -> VerificationKey: + def get_zksnark_provider(self) -> IZKSnarkProvider: + """ + Get the appropriate zksnark provider, based on the server configuration. + """ + config = self.get_configuration() + return get_zksnark_provider(config.zksnark_name) + + def get_verification_key(self) -> IVerificationKey: """ Fetch the verification key from the proving service """ with grpc.insecure_channel(self.endpoint) as channel: stub = prover_pb2_grpc.ProverStub(channel) # type: ignore - print("-------------- Get the verification key --------------") - verificationkey = stub.GetVerificationKey(_make_empty_message()) - return verificationkey + vk_proto = stub.GetVerificationKey(_make_empty_message()) + zksnark = self.get_zksnark_provider() + return zksnark.verification_key_from_proto(vk_proto) def get_proof( self, - proof_inputs: ProofInputs) -> ExtendedProof: + proof_inputs: ProofInputs) -> Tuple[ExtendedProof, List[int]]: """ Request a proof generation to the proving service """ with grpc.insecure_channel(self.endpoint) as channel: stub = prover_pb2_grpc.ProverStub(channel) # type: ignore print("-------------- Get the proof --------------") - proof = stub.Prove(proof_inputs) - return proof + extproof_and_pub_data = stub.Prove(proof_inputs) + zksnark = self.get_zksnark_provider() + extproof = zksnark.extended_proof_from_proto( + extproof_and_pub_data.extended_proof) + public_data = [int(x, 16) for x in extproof_and_pub_data.public_data] + return extproof, public_data def _make_empty_message() -> empty_pb2.Empty: diff --git a/client/zeth/core/py.typed b/client/zeth/core/py.typed index a0c5d77b1..58db294ed 100644 --- a/client/zeth/core/py.typed +++ b/client/zeth/core/py.typed @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/core/signing.py b/client/zeth/core/signing.py index f725647c8..777f96e2f 100644 --- a/client/zeth/core/signing.py +++ b/client/zeth/core/signing.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -11,29 +11,39 @@ BN128 """ +from __future__ import annotations from math import ceil from os import urandom from hashlib import sha256 from py_ecc import bn128 as ec -from typing import List, Tuple +from typing import Dict, List, Tuple, Any FQ = ec.FQ G1 = Tuple[ec.FQ, ec.FQ] -# pylint: disable=line-too-long -# Characteristic of the scalar field of BN128 (see comment and reference above). -SIGNATURE_PRIME = \ - 21888242871839275222246405745257275088548364400416034343698204186575808495617 # noqa -# pylint: enable=line-too-long - class SigningVerificationKey: """ An OT-Schnorr verification key. """ - def __init__(self, x_g1: G1, y_g1: G1): - self.ppk = x_g1 - self.spk = y_g1 + def __init__(self, ppk: G1, spk: G1): + self.ppk = ppk + self.spk = spk + + def to_bytes(self) -> bytes: + return g1_to_bytes(self.ppk) + g1_to_bytes(self.spk) + + def to_json_dict(self) -> Dict[str, Any]: + return { + "ppk": g1_to_json_dict(self.ppk), + "spk": g1_to_json_dict(self.spk), + } + + @staticmethod + def from_json_dict(json_dict: Dict[str, Any]) -> SigningVerificationKey: + return SigningVerificationKey( + ppk=g1_from_json_dict(json_dict["ppk"]), + spk=g1_from_json_dict(json_dict["spk"])) class SigningSecretKey: @@ -44,18 +54,40 @@ def __init__(self, x: FQ, y: FQ, y_g1: G1): self.psk = x self.ssk = (y, y_g1) + def to_json_dict(self) -> Dict[str, Any]: + return { + "psk": fq_to_hex(self.psk), + "ssk_y": fq_to_hex(self.ssk[0]), + "ssk_y_g1": g1_to_json_dict(self.ssk[1]), + } + + @staticmethod + def from_json_dict(json_dict: Dict[str, Any]) -> SigningSecretKey: + return SigningSecretKey( + x=fq_from_hex(json_dict["psk"]), + y=fq_from_hex(json_dict["ssk_y"]), + y_g1=g1_from_json_dict(json_dict["ssk_y_g1"])) + class SigningKeyPair: """ An OT-Schnorr signing and verification keypair. """ - def __init__(self, x: FQ, y: FQ, x_g1: G1, y_g1: G1): - # We include y_g1 in the signing key - self.sk = SigningSecretKey(x, y, y_g1) - self.vk = SigningVerificationKey(x_g1, y_g1) + def __init__(self, sk: SigningSecretKey, vk: SigningVerificationKey): + self.sk = sk + self.vk = vk + def to_json_dict(self) -> Dict[str, Any]: + return { + "sk": self.sk.to_json_dict(), + "vk": self.vk.to_json_dict(), + } -Signature = int + @staticmethod + def from_json_dict(json_dict: Dict[str, Any]) -> SigningKeyPair: + return SigningKeyPair( + SigningSecretKey.from_json_dict(json_dict["sk"]), + SigningVerificationKey.from_json_dict(json_dict["vk"])) def gen_signing_keypair() -> SigningKeyPair: @@ -63,32 +95,28 @@ def gen_signing_keypair() -> SigningKeyPair: Return a one-time signature key-pair composed of elements of F_q and G1. """ - key_size_byte = ceil(len("{0:b}".format(SIGNATURE_PRIME)) / 8) + key_size_byte = ceil(len("{0:b}".format(ec.curve_order)) / 8) x = FQ( - int(bytes(urandom(key_size_byte)).hex(), 16) % SIGNATURE_PRIME) + int(bytes(urandom(key_size_byte)).hex(), 16) % ec.curve_order) y = FQ( - int(bytes(urandom(key_size_byte)).hex(), 16) % SIGNATURE_PRIME) + int(bytes(urandom(key_size_byte)).hex(), 16) % ec.curve_order) X = ec.multiply(ec.G1, x.n) Y = ec.multiply(ec.G1, y.n) - return SigningKeyPair(x, y, X, Y) + # We include y_g1 in the signing key + sk = SigningSecretKey(x, y, Y) + vk = SigningVerificationKey(X, Y) + return SigningKeyPair(sk, vk) -def encode_vk_to_bytes(vk: SigningVerificationKey) -> bytes: - """ - Encode a verification key as a byte string - We assume here the group prime $p$ is written in less than 256 bits - to conform with Ethereum bytes32 type - """ - vk_byte = g1_to_bytes(vk.ppk) - vk_byte += g1_to_bytes(vk.spk) - return vk_byte + +Signature = int -def encode_signature_to_bytes(signature: Signature) -> bytes: +def signature_to_bytes(signature: Signature) -> bytes: return signature.to_bytes(32, byteorder='big') -def decode_signature_from_bytes(sig_bytes: bytes) -> Signature: +def signature_from_bytes(sig_bytes: bytes) -> Signature: return int.from_bytes(sig_bytes, byteorder='big') @@ -107,11 +135,10 @@ def sign( # Convert the hex digest into a field element challenge = int(sha256(challenge_to_hash).hexdigest(), 16) - challenge = challenge % SIGNATURE_PRIME + challenge = challenge % ec.curve_order # Compute the signature sigma - sigma = (sk.ssk[0].n + challenge * sk.psk.n) % SIGNATURE_PRIME - + sigma = (sk.ssk[0].n + challenge * sk.psk.n) % ec.curve_order return sigma @@ -128,7 +155,7 @@ def verify( challenge_to_hash = g1_to_bytes(vk.spk) + m challenge = int(sha256(challenge_to_hash).hexdigest(), 16) - challenge = challenge % SIGNATURE_PRIME + challenge = challenge % ec.curve_order left_part = ec.multiply(ec.G1, FQ(sigma).n) right_part = ec.add(vk.spk, ec.multiply(vk.ppk, FQ(challenge).n)) @@ -168,6 +195,24 @@ def signature_from_mix_parameter(param: int) -> Signature: """ return param +# Low level encoding / decoding functions + + +def fq_to_bytes(fq_element: FQ) -> bytes: + return int(fq_element.n).to_bytes(32, byteorder='big') + + +def fq_from_bytes(fq_bytes: bytes) -> FQ: + return FQ(int.from_bytes(fq_bytes, byteorder='big')) + + +def fq_to_hex(fq_element: FQ) -> str: + return fq_to_bytes(fq_element).hex() + + +def fq_from_hex(fq_hex: str) -> FQ: + return fq_from_bytes(bytes.fromhex(fq_hex)) + def g1_to_bytes(group_el: G1) -> bytes: """ @@ -178,3 +223,14 @@ def g1_to_bytes(group_el: G1) -> bytes: return \ int(group_el[0]).to_bytes(32, byteorder='big') + \ int(group_el[1]).to_bytes(32, byteorder='big') + + +def g1_to_json_dict(group_el: G1) -> Dict[str, Any]: + return { + "x": fq_to_hex(group_el[0]), + "y": fq_to_hex(group_el[1]), + } + + +def g1_from_json_dict(json_dict: Dict[str, Any]) -> G1: + return (fq_from_hex(json_dict["x"]), fq_from_hex(json_dict["y"])) diff --git a/client/zeth/core/timer.py b/client/zeth/core/timer.py index c56eadda1..94c3632cc 100644 --- a/client/zeth/core/timer.py +++ b/client/zeth/core/timer.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/core/utils.py b/client/zeth/core/utils.py index f97fc0cdd..9e0587fbd 100644 --- a/client/zeth/core/utils.py +++ b/client/zeth/core/utils.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/core/wallet.py b/client/zeth/core/wallet.py index 029688937..2ae054ca1 100644 --- a/client/zeth/core/wallet.py +++ b/client/zeth/core/wallet.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/core/zeth_address.py b/client/zeth/core/zeth_address.py index 30d692114..0479920ce 100644 --- a/client/zeth/core/zeth_address.py +++ b/client/zeth/core/zeth_address.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/core/zksnark.py b/client/zeth/core/zksnark.py index d795c43e1..d8ecca224 100644 --- a/client/zeth/core/zksnark.py +++ b/client/zeth/core/zksnark.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -496,4 +496,4 @@ def _contract_name(zksnark_name: str, pp: PairingParameters) -> str: Given a snark name fragment (as used in contract naming conventions) and pairing parameters, determine the full contract name. """ - return zksnark_name + PAIRING_NAME_TO_CONTRACT_NAME[pp.name] + "Mixer" + return "Mixer" + zksnark_name + PAIRING_NAME_TO_CONTRACT_NAME[pp.name] diff --git a/client/zeth/helper/__init__.py b/client/zeth/helper/__init__.py index b6e016d34..fca72ddb7 100644 --- a/client/zeth/helper/__init__.py +++ b/client/zeth/helper/__init__.py @@ -1,3 +1,3 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/helper/eth_fund.py b/client/zeth/helper/eth_fund.py index 634f1f3ed..3e02fac6f 100644 --- a/client/zeth/helper/eth_fund.py +++ b/client/zeth/helper/eth_fund.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/helper/eth_gen_address.py b/client/zeth/helper/eth_gen_address.py index 1a30e71c7..8b2ba0520 100644 --- a/client/zeth/helper/eth_gen_address.py +++ b/client/zeth/helper/eth_gen_address.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/helper/eth_gen_network_config.py b/client/zeth/helper/eth_gen_network_config.py index bad321885..b1b907a4b 100644 --- a/client/zeth/helper/eth_gen_network_config.py +++ b/client/zeth/helper/eth_gen_network_config.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/helper/eth_get_balance.py b/client/zeth/helper/eth_get_balance.py index c3ebeb948..4aa5e662a 100644 --- a/client/zeth/helper/eth_get_balance.py +++ b/client/zeth/helper/eth_get_balance.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/helper/eth_get_contract_address.py b/client/zeth/helper/eth_get_contract_address.py new file mode 100644 index 000000000..bb454ec82 --- /dev/null +++ b/client/zeth/helper/eth_get_contract_address.py @@ -0,0 +1,15 @@ +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd +# +# SPDX-License-Identifier: LGPL-3.0+ + +from zeth.cli.utils import load_contract_address +from click import command, argument + + +@command() +@argument("instance-file") +def eth_get_contract_address(instance_file: str) -> None: + """ + Extract the address from a contract instance description file. + """ + print(load_contract_address(instance_file)) diff --git a/client/zeth/helper/eth_send.py b/client/zeth/helper/eth_send.py index 6c4caf542..c1906e282 100644 --- a/client/zeth/helper/eth_send.py +++ b/client/zeth/helper/eth_send.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/helper/token_approve.py b/client/zeth/helper/token_approve.py index 54904bc21..350400efc 100644 --- a/client/zeth/helper/token_approve.py +++ b/client/zeth/helper/token_approve.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/client/zeth/helper/zeth_helper b/client/zeth/helper/zeth_helper index 2264e1204..f0d1a3964 100644 --- a/client/zeth/helper/zeth_helper +++ b/client/zeth/helper/zeth_helper @@ -1,15 +1,15 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ - from zeth.helper.eth_gen_network_config import eth_gen_network_config from zeth.helper.eth_gen_address import eth_gen_address from zeth.helper.eth_get_balance import eth_get_balance from zeth.helper.eth_fund import eth_fund from zeth.helper.eth_send import eth_send +from zeth.helper.eth_get_contract_address import eth_get_contract_address from zeth.helper.token_approve import token_approve from zeth.cli.constants import ETH_NETWORK_FILE_DEFAULT, ETH_NETWORK_DEFAULT from click import group, command, option, pass_context, ClickException, Context @@ -50,6 +50,7 @@ zeth_helper.add_command(eth_gen_address) zeth_helper.add_command(eth_get_balance) zeth_helper.add_command(eth_fund) zeth_helper.add_command(eth_send) +zeth_helper.add_command(eth_get_contract_address) zeth_helper.add_command(token_approve) zeth_helper.add_command(help) diff --git a/debug/analyzer/README.md b/debug/analyzer/README.md index cdc24c594..9b1685d1e 100644 --- a/debug/analyzer/README.md +++ b/debug/analyzer/README.md @@ -6,7 +6,7 @@ Basic set of functionalities to parse and run basic queries on the r1cs exported ```json { - "scalar_field_characteristic": "Not yet supported. Should be bigint in hexadecimal", + "scalar_field_characteristic": "0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001", "num_variables": 549746, # Number of wires "num_constraints": 433391, # Number of gates "num_inputs": 17, # Number of primary inputs diff --git a/debug/analyzer/__init__.py b/debug/analyzer/__init__.py index 2bb908d40..830ea092f 100644 --- a/debug/analyzer/__init__.py +++ b/debug/analyzer/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/debug/analyzer/parse_r1cs.py b/debug/analyzer/parse_r1cs.py index 75c2ebb2a..8e9836f21 100644 --- a/debug/analyzer/parse_r1cs.py +++ b/debug/analyzer/parse_r1cs.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/depends/CMakeLists.txt b/depends/CMakeLists.txt index a805df286..f2c8836e7 100644 --- a/depends/CMakeLists.txt +++ b/depends/CMakeLists.txt @@ -23,11 +23,12 @@ endif() # libsodium set(LIBSODIUM_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libsodium) +set(LIBSODIUM_CONFIGURE_CMD "CC=\"${CMAKE_C_COMPILER_LAUNCHER} ${CMAKE_C_COMPILER}\" CXX=\"${CMAKE_C_COMPILER_LAUNCHER} ${CMAKE_CXX_COMPILER}\" ./configure --prefix= --enable-minimal --disable-shared --enable-static") include(ExternalProject) ExternalProject_Add( libsodium SOURCE_DIR ${LIBSODIUM_SOURCE_DIR} - CONFIGURE_COMMAND cd ${LIBSODIUM_SOURCE_DIR} && DO_NOT_UPDATE_CONFIG_SCRIPTS=1 ./autogen.sh && ./configure --prefix= --enable-minimal --disable-shared --enable-static + CONFIGURE_COMMAND cd ${LIBSODIUM_SOURCE_DIR} && DO_NOT_UPDATE_CONFIG_SCRIPTS=1 ./autogen.sh && eval ${LIBSODIUM_CONFIGURE_CMD} BUILD_COMMAND cd ${LIBSODIUM_SOURCE_DIR} && make ${MAKE_BUILD_FLAGS} INSTALL_COMMAND cd ${LIBSODIUM_SOURCE_DIR} && make install) diff --git a/depends/ganache-cli b/depends/ganache-cli index 8adb190c9..3b6ba1adf 160000 --- a/depends/ganache-cli +++ b/depends/ganache-cli @@ -1 +1 @@ -Subproject commit 8adb190c983436467dfa6ad71d49c5d830bcab32 +Subproject commit 3b6ba1adf0f7ad08bc46171370fe29bd1d52ecab diff --git a/depends/libsnark b/depends/libsnark index 39bb0b9f1..104ed9021 160000 --- a/depends/libsnark +++ b/depends/libsnark @@ -1 +1 @@ -Subproject commit 39bb0b9f193511a22a94d35bb3829afc9671b55d +Subproject commit 104ed902141d41dfb4efb2b9270386031d98e12e diff --git a/libzeth/circuits/binary_operation.hpp b/libzeth/circuits/binary_operation.hpp index 159d4d872..543eb6846 100644 --- a/libzeth/circuits/binary_operation.hpp +++ b/libzeth/circuits/binary_operation.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/circuits/binary_operation.tcc b/libzeth/circuits/binary_operation.tcc index f65cd766a..1f812f9be 100644 --- a/libzeth/circuits/binary_operation.tcc +++ b/libzeth/circuits/binary_operation.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/circuits/blake2s/blake2s.hpp b/libzeth/circuits/blake2s/blake2s.hpp index c7d593ebc..707ede6b5 100644 --- a/libzeth/circuits/blake2s/blake2s.hpp +++ b/libzeth/circuits/blake2s/blake2s.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/circuits/blake2s/blake2s.tcc b/libzeth/circuits/blake2s/blake2s.tcc index 6060e7c63..2191d666b 100644 --- a/libzeth/circuits/blake2s/blake2s.tcc +++ b/libzeth/circuits/blake2s/blake2s.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/circuits/blake2s/blake2s_comp.hpp b/libzeth/circuits/blake2s/blake2s_comp.hpp index 3ada4f95d..55b635996 100644 --- a/libzeth/circuits/blake2s/blake2s_comp.hpp +++ b/libzeth/circuits/blake2s/blake2s_comp.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/circuits/blake2s/blake2s_comp.tcc b/libzeth/circuits/blake2s/blake2s_comp.tcc index 5a0aef193..e09ea6626 100644 --- a/libzeth/circuits/blake2s/blake2s_comp.tcc +++ b/libzeth/circuits/blake2s/blake2s_comp.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/circuits/blake2s/blake2s_comp_setup.tcc b/libzeth/circuits/blake2s/blake2s_comp_setup.tcc index bebf05148..8612af1c7 100644 --- a/libzeth/circuits/blake2s/blake2s_comp_setup.tcc +++ b/libzeth/circuits/blake2s/blake2s_comp_setup.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/circuits/blake2s/g_primitive.hpp b/libzeth/circuits/blake2s/g_primitive.hpp index dc5c08df0..70c07a31c 100644 --- a/libzeth/circuits/blake2s/g_primitive.hpp +++ b/libzeth/circuits/blake2s/g_primitive.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/circuits/blake2s/g_primitive.tcc b/libzeth/circuits/blake2s/g_primitive.tcc index b05ad3dea..8cd6b8bb6 100644 --- a/libzeth/circuits/blake2s/g_primitive.tcc +++ b/libzeth/circuits/blake2s/g_primitive.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/circuits/circuit_types.hpp b/libzeth/circuits/circuit_types.hpp index 83305eefd..e8b02c923 100644 --- a/libzeth/circuits/circuit_types.hpp +++ b/libzeth/circuits/circuit_types.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -28,22 +28,22 @@ template class tree_hash_selector { }; -// For alt-bn128, use MiMC7 with 91 rounds +// For alt-bn128, use MiMC17 with 65 rounds. template<> class tree_hash_selector { public: using tree_hash = MiMC_mp_gadget< libff::alt_bn128_Fr, - MiMC_permutation_gadget>; + MiMC_permutation_gadget>; }; -// For bls12-377, use MiMC31 with 51 rounds +// For bls12-377, use MiMC17 with 62 rounds template<> class tree_hash_selector { public: using tree_hash = MiMC_mp_gadget< libff::bls12_377_Fr, - MiMC_permutation_gadget>; + MiMC_permutation_gadget>; }; // Hash function to be used in the Merkle Tree diff --git a/libzeth/circuits/circuit_utils.hpp b/libzeth/circuits/circuit_utils.hpp index e97616116..43f029c52 100644 --- a/libzeth/circuits/circuit_utils.hpp +++ b/libzeth/circuits/circuit_utils.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/circuits/circuit_utils.tcc b/libzeth/circuits/circuit_utils.tcc index 4ac2b7a2c..197ea9e6e 100644 --- a/libzeth/circuits/circuit_utils.tcc +++ b/libzeth/circuits/circuit_utils.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/circuits/circuit_wrapper.hpp b/libzeth/circuits/circuit_wrapper.hpp index cbc885be8..1eaf34a51 100644 --- a/libzeth/circuits/circuit_wrapper.hpp +++ b/libzeth/circuits/circuit_wrapper.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -6,6 +6,7 @@ #define __ZETH_CIRCUITS_CIRCUIT_WRAPPER_HPP__ #include "libzeth/circuits/joinsplit.tcc" +#include "libzeth/circuits/mimc/mimc_input_hasher.hpp" #include "libzeth/core/extended_proof.hpp" #include "libzeth/core/note.hpp" #include "libzeth/zeth_constants.hpp" @@ -25,26 +26,29 @@ template< size_t TreeDepth> class circuit_wrapper { -private: - std::shared_ptr, +public: + using Field = libff::Fr; + // Both `joinsplit` and `joinsplit_gadget` are already used in the + // namespace. + using joinsplit_type = joinsplit_gadget< + Field, HashT, HashTreeT, NumInputs, NumOutputs, - TreeDepth>> - joinsplit_g; - -public: - using Field = libff::Fr; + TreeDepth>; + using input_hasher_type = mimc_input_hasher; circuit_wrapper(); + circuit_wrapper(const circuit_wrapper &) = delete; + circuit_wrapper &operator=(const circuit_wrapper &) = delete; // Generate the trusted setup typename snarkT::keypair generate_trusted_setup() const; // Retrieve the constraint system (intended for debugging purposes). - libsnark::protoboard get_constraint_system() const; + const libsnark::r1cs_constraint_system &get_constraint_system() + const; // Generate a proof and returns an extended proof extended_proof prove( @@ -55,7 +59,17 @@ class circuit_wrapper const bits64 &vpub_out, const bits256 &h_sig_in, const bits256 &phi_in, - const typename snarkT::proving_key &proving_key) const; + const typename snarkT::proving_key &proving_key, + std::vector &out_public_data) const; + + const std::vector &get_last_assignment() const; + +private: + libsnark::protoboard pb; + libsnark::pb_variable public_data_hash; + libsnark::pb_variable_array public_data; + std::shared_ptr joinsplit; + std::shared_ptr input_hasher; }; } // namespace libzeth diff --git a/libzeth/circuits/circuit_wrapper.tcc b/libzeth/circuits/circuit_wrapper.tcc index cc0ec0792..a77344178 100644 --- a/libzeth/circuits/circuit_wrapper.tcc +++ b/libzeth/circuits/circuit_wrapper.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -27,6 +27,33 @@ circuit_wrapper< NumOutputs, TreeDepth>::circuit_wrapper() { + // Allocate a single public variable to hold the hash of the public + // joinsplit inputs. The public joinsplit inputs are then allocated + // immediately following this. + public_data_hash.allocate(pb, "public_data_hash"); + pb.set_input_sizes(1); + + // Joinsplit gadget internally allocates its public data first. + // TODO: joinsplit_gadget should be refactored to be properly composable. + joinsplit = std::make_shared(pb); + const size_t num_public_elements = joinsplit->get_num_public_elements(); + + // Populate public_data to represent the joinsplit public data. Skip + // the first 2 variables (the constant 1, and the digest of the + // public_data), and use the num_public_elements variables that follow. + public_data.reserve(num_public_elements); + for (size_t i = 0; i < num_public_elements; ++i) { + public_data.emplace_back(i + 2); + } + assert(public_data.size() == num_public_elements); + + // Initialize the input hasher gadget + input_hasher = std::make_shared( + pb, public_data, public_data_hash, "input_hasher"); + + // Generate constraints + joinsplit->generate_r1cs_constraints(); + input_hasher->generate_r1cs_constraints(); } template< @@ -46,11 +73,6 @@ typename snarkT::keypair circuit_wrapper< NumOutputs, TreeDepth>::generate_trusted_setup() const { - libsnark::protoboard pb; - joinsplit_gadget - g(pb); - g.generate_r1cs_constraints(); - // Generate a verification and proving key (trusted setup) and write them // in a file return snarkT::generate_setup(pb); @@ -64,7 +86,7 @@ template< size_t NumInputs, size_t NumOutputs, size_t TreeDepth> -libsnark::protoboard> circuit_wrapper< +const libsnark::r1cs_constraint_system> &circuit_wrapper< HashT, HashTreeT, ppT, @@ -73,11 +95,7 @@ libsnark::protoboard> circuit_wrapper< NumOutputs, TreeDepth>::get_constraint_system() const { - libsnark::protoboard pb; - joinsplit_gadget - g(pb); - g.generate_r1cs_constraints(); - return pb; + return pb.get_constraint_system(); } template< @@ -104,7 +122,8 @@ extended_proof circuit_wrapper< const bits64 &vpub_out, const bits256 &h_sig_in, const bits256 &phi_in, - const typename snarkT::proving_key &proving_key) const + const typename snarkT::proving_key &proving_key, + std::vector &out_public_data) const { // left hand side and right hand side of the joinsplit bits64 lhs_value = vpub_in; @@ -128,22 +147,47 @@ extended_proof circuit_wrapper< throw std::invalid_argument("invalid joinsplit balance"); } - libsnark::protoboard pb; - - joinsplit_gadget - g(pb); - g.generate_r1cs_constraints(); - g.generate_r1cs_witness( + joinsplit->generate_r1cs_witness( root, inputs, outputs, vpub_in, vpub_out, h_sig_in, phi_in); + input_hasher->generate_r1cs_witness(); bool is_valid_witness = pb.is_satisfied(); std::cout << "******* [DEBUG] Satisfiability result: " << is_valid_witness << " *******" << std::endl; + // Fill out the public data vector + const size_t num_public_elements = + joinsplit_type::get_num_public_elements(); + out_public_data.resize(0); + out_public_data.reserve(num_public_elements); + for (size_t i = 0; i < num_public_elements; ++i) { + out_public_data.push_back(pb.val(public_data[i])); + } + // Instantiate an extended_proof from the proof we generated and the given // primary_input return extended_proof( - snarkT::generate_proof(pb, proving_key), pb.primary_input()); + snarkT::generate_proof(proving_key, pb), pb.primary_input()); +} + +template< + typename HashT, + typename HashTreeT, + typename ppT, + typename snarkT, + size_t NumInputs, + size_t NumOutputs, + size_t TreeDepth> +const std::vector> &circuit_wrapper< + HashT, + HashTreeT, + ppT, + snarkT, + NumInputs, + NumOutputs, + TreeDepth>::get_last_assignment() const +{ + return pb.full_variable_assignment(); } } // namespace libzeth diff --git a/libzeth/circuits/joinsplit.tcc b/libzeth/circuits/joinsplit.tcc index d04341df0..b6dcd183d 100644 --- a/libzeth/circuits/joinsplit.tcc +++ b/libzeth/circuits/joinsplit.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -63,7 +63,8 @@ private: libsnark::pb_variable ZERO; - // ---- Primary inputs (public) ---- // + // PUBLIC DATA: to be made available to the mixer + // Merkle Root std::shared_ptr> merkle_root; // List of nullifiers of the notes to spend @@ -82,7 +83,11 @@ private: std::array>, NumInputs> h_is; - // ---- Auxiliary inputs (private) ---- // + // PRIVATE DATA: must be auxiliary (private) inputs to the statement. + // Protoboard owner is responsible for ensuring this. (Note that the PUBLIC + // inputs above are allocated first, so only the first + // get_num_public_elements allocated by this gadget are "public"). + // Total amount transfered in the transaction libsnark::pb_variable_array zk_total_uint64; // List of all spending keys @@ -125,29 +130,26 @@ public: { // Block dedicated to generate the verifier inputs { - // The verification inputs are, except for the root, all bit-strings - // of various lengths (256-bit digests and 64-bit integers) and so - // we pack them into as few field elements as possible. (The more - // verification inputs you have, the more expensive verification - // is.) - - // --------- ALLOCATION OF PRIMARY INPUTS -------- // - // We make sure to have the primary inputs ordered as follow: - // [Root, NullifierS, CommitmentS, h_sig, h_iS, Residual field - // element(S)] ie, below is the index mapping of the primary input - // elements on the protoboard: - // - Index of the "Root" field element: {0} - // - Index of the "NullifierS" field elements: [1, 1 + NumInputs[ - // - Index of the "CommitmentS" field elements: [1 + NumInputs, - // 1 + NumInputs + NumOutputs[ - // - Index of the "h_sig" field element: {1 + NumInputs + - // NumOutputs} - // - Index of the "h_iS" field elements: [1 + NumInputs + NumOutputs - // + 1, 1 + NumInputs + NumOutputs + NumInputs[ - // - Index of the "Residual field element(S)", ie "v_pub_in", - // "v_pub_out", and bits of previous variables not fitting within - // FieldT::capacity() [1 + NumInputs + NumOutputs + NumInputs, - // 1 + NumInputs + NumOutputs + NumInputs + nb_field_residual[ + // PUBLIC DATA: allocated first so that the protoboard has access. + // + // Allocation is currently performed here in the following order + // (with the protoboard owner determining whether these are primary + // or auxiliary inputs to the circuit): + // - Root + // - NullifierS + // - CommitmentS + // - h_sig + // - h_iS + // - Residual field element(S) + // + // This yields the following index mappings: + // 0 : "Root" + // 1, ... : Nullifiers (NumInputs) + // 1 + NumInputs, ... : Commitments (Num Outputs) + // 1 + NumInputs + NumOutputs : h_sig + // 2 + NumInputs + NumOutputs, ... : h_iS (NumInputs) + // 2 + 2xNumInputs + NumOutputs, ... : v_in, v_out, residual + // (nb_field_residual) // We first allocate the root merkle_root.reset(new libsnark::pb_variable); @@ -184,20 +186,20 @@ public: nb_field_residual, FMT(this->annotation_prefix, " residual_bits")); - // The primary inputs are: - // [Root, NullifierS, CommitmentS, h_sig, h_iS, Residual Field - // Element(S)]. The root is represented on a single field element. - // H_sig, as well as each nullifier, commitment and h_i are in - // {0,1}^256 and thus take 1 field element and a few bits to be - // represented. The aggregation of these bits plus of value_pub_in, - // and value_pub_out take `nb_field_residual` field element(s) to be - // represented - const size_t nb_packed_inputs = + // Compute the number of packed public elements, and the total + // number of public elements (see table above). The "packed" inputs + // (those represented as a field element and some residual bits) + // are: + // H_sig, nullifier, commitments and h_iS + const size_t num_packed_public_elements = 2 * NumInputs + 1 + nb_field_residual; - const size_t nb_inputs = 1 + NumOutputs + nb_packed_inputs; - pb.set_input_sizes(nb_inputs); - // --------------------------------------------------------------- + const size_t num_public_elements = + 1 + NumOutputs + num_packed_public_elements; + + // PRIVATE DATA: + // Allocate a ZERO variable + // TODO: check whether/why this is actually needed ZERO.allocate(pb, FMT(this->annotation_prefix, " ZERO")); // Initialize the digest_variables @@ -277,14 +279,15 @@ public: // since we are packing all the inputs nullifiers + the h_is + // + the h_sig + the residual bits assert(packed_inputs.size() == NumInputs + 1 + NumInputs + 1); - assert(nb_packed_inputs == [this]() { + assert(num_packed_public_elements == [this]() { size_t sum = 0; for (const auto &i : packed_inputs) { sum = sum + i.size(); } return sum; }()); - assert(nb_inputs == get_inputs_field_element_size()); + assert(num_public_elements == get_num_public_elements()); + (void)num_public_elements; // [SANITY CHECK] Total size of unpacked inputs size_t total_size_unpacked_inputs = 0; @@ -599,8 +602,8 @@ public: return get_inputs_bit_size() - (1 + NumOutputs) * FieldT::capacity(); } - // Computes the number of field elements in the primary inputs - static size_t get_inputs_field_element_size() + // Computes the number of field elements in the public data + static size_t get_num_public_elements() { size_t nb_elements = 0; diff --git a/libzeth/circuits/merkle_tree/merkle_path_compute.hpp b/libzeth/circuits/merkle_tree/merkle_path_compute.hpp index abec3e2a0..32ab1f5b7 100644 --- a/libzeth/circuits/merkle_tree/merkle_path_compute.hpp +++ b/libzeth/circuits/merkle_tree/merkle_path_compute.hpp @@ -23,10 +23,12 @@ class merkle_path_compute : public libsnark::gadget // Merkle Authentication path const libsnark::pb_variable_array path; + // Digests + libsnark::pb_variable_array digests; // Gadget informing the position in the three of the computed // hash and authentication node std::vector> selectors; - // Vector of hash gadgets to compute the intermediary hashes + // Vector of hash gadgets to compute the intermediary digests std::vector hashers; merkle_path_compute( diff --git a/libzeth/circuits/merkle_tree/merkle_path_compute.tcc b/libzeth/circuits/merkle_tree/merkle_path_compute.tcc index e6cdf6dab..1f9f5dbde 100644 --- a/libzeth/circuits/merkle_tree/merkle_path_compute.tcc +++ b/libzeth/circuits/merkle_tree/merkle_path_compute.tcc @@ -29,7 +29,9 @@ merkle_path_compute::merkle_path_compute( assert(address_bits.size() == depth); // For each layer of the tree + digests.allocate(pb, depth, FMT(annotation_prefix, " digests")); for (size_t i = 0; i < depth; i++) { + // We first initialize the gadget to order the computed hash and the // authentication node to know which one is the first to be hashed and // which one is the second (as in mimc_hash(left, right)) We also append @@ -44,7 +46,7 @@ merkle_path_compute::merkle_path_compute( } else { selectors.push_back(merkle_path_selector( pb, - hashers[i - 1].result(), + digests[i - 1], path[i], address_bits[i], FMT(this->annotation_prefix, " selector[%zu]", i))); @@ -56,6 +58,7 @@ merkle_path_compute::merkle_path_compute( pb, {selectors[i].get_left()}, selectors[i].get_right(), + digests[i], FMT(this->annotation_prefix, " hasher[%zu]", i)); // We append the initialized hasher in the vector of hashers @@ -91,11 +94,11 @@ const libsnark::pb_variable merkle_path_compute:: result() { // We first check that we are not working with an empty tree - assert(hashers.size() > 0); + assert(digests.size() > 0); // We return the last hasher result, that is to say the computed root, // generated out of leaf, leaf address and merkle authentication path - return hashers.back().result(); + return digests[digests.size() - 1]; }; } // namespace libzeth diff --git a/libzeth/circuits/mimc/mimc.hpp b/libzeth/circuits/mimc/mimc.hpp deleted file mode 100644 index 9948d019f..000000000 --- a/libzeth/circuits/mimc/mimc.hpp +++ /dev/null @@ -1,54 +0,0 @@ -// DISCLAIMER: -// Content taken and adapted from: -// https://github.com/HarryR/ethsnarks/blob/master/src/gadgets/mimc.hpp - -#ifndef __ZETH_CIRCUITS_MIMC_HPP__ -#define __ZETH_CIRCUITS_MIMC_HPP__ - -#include "libzeth/circuits/mimc/mimc_round.hpp" - -namespace libzeth -{ - -/// MiMC_permutation_gadget enforces correct computation of a MiMC round -/// function applied some number of rounds. -template -class MiMC_permutation_gadget : public libsnark::gadget -{ -private: - // Round constants only available up to 91 rounds - static_assert(NumRounds <= 91, "NumRounds must be less than 91"); - - // Instantiate round gadget with exponent = Exponent - using RoundT = MiMC_round_gadget; - - // Vector of round constants - static std::vector round_constants; - static bool round_constants_initialized; - - // Vector of intermediate result values - std::array, NumRounds> round_results; - // Vector of MiMC round_gadgets - std::vector round_gadgets; - -public: - MiMC_permutation_gadget( - libsnark::protoboard &pb, - const libsnark::pb_variable &msg, - const libsnark::pb_variable &key, - const std::string &annotation_prefix = "MiMCe7_permutation_gadget"); - - void generate_r1cs_constraints(); - void generate_r1cs_witness() const; - - const libsnark::pb_variable &result() const; - - // Constants vector initialization - void setup_sha3_constants(); -}; - -} // namespace libzeth - -#include "libzeth/circuits/mimc/mimc.tcc" - -#endif // __ZETH_CIRCUITS_MIMC_HPP__ diff --git a/libzeth/circuits/mimc/mimc_input_hasher.hpp b/libzeth/circuits/mimc/mimc_input_hasher.hpp new file mode 100644 index 000000000..a6395593a --- /dev/null +++ b/libzeth/circuits/mimc/mimc_input_hasher.hpp @@ -0,0 +1,46 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +#ifndef __ZETH_CIRCUITS_MIMC_MIMC_INPUT_HASHER_HPP__ +#define __ZETH_CIRCUITS_MIMC_MIMC_INPUT_HASHER_HPP__ + +#include + +namespace libzeth +{ + +/// Given a list of variables, hash the variables to a value which can be used +/// as a public input bound to the original variables. +template +class mimc_input_hasher : public libsnark::gadget +{ +private: + // Output variable + libsnark::pb_variable _result; + + // Compression function constraints + std::vector> _compression_functions; + + // Intermediate values + libsnark::pb_variable_array _intermediate_values; + +public: + mimc_input_hasher( + libsnark::protoboard &pb, + const libsnark::pb_linear_combination_array &inputs, + const libsnark::pb_variable hash_output, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness() const; + + static FieldT get_iv(); + static FieldT compute_hash(const std::vector &values); +}; + +} // namespace libzeth + +#include "mimc_input_hasher.tcc" + +#endif // __ZETH_CIRCUITS_MIMC_MIMC_INPUT_HASHER_HPP__ diff --git a/libzeth/circuits/mimc/mimc_input_hasher.tcc b/libzeth/circuits/mimc/mimc_input_hasher.tcc new file mode 100644 index 000000000..f00c0de8f --- /dev/null +++ b/libzeth/circuits/mimc/mimc_input_hasher.tcc @@ -0,0 +1,113 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +#ifndef __ZETH_CIRCUITS_MIMC_MIMC_INPUT_HASHER_TCC__ +#define __ZETH_CIRCUITS_MIMC_MIMC_INPUT_HASHER_TCC__ + +#include "mimc_input_hasher.hpp" + +namespace libzeth +{ + +template +mimc_input_hasher::mimc_input_hasher( + libsnark::protoboard &pb, + const libsnark::pb_linear_combination_array &inputs, + const libsnark::pb_variable result, + const std::string &annotation_prefix) + : libsnark::gadget(pb, annotation_prefix), _result(result) +{ + const size_t num_inputs = inputs.size(); + if (num_inputs < 2) { + // Although it would be superfluous, we could support 1 entry. However + // it would add some complexity to the code below. For now, assume + // strictly more than 1 entry. + throw std::invalid_argument( + "inputs array must have at least 2 entries"); + } + + // Require one compression function invocation per element in the array, + // followed by the finalization step. Each invocation except the last + // requires an intermediate output variable. + _compression_functions.reserve(num_inputs + 1); + _intermediate_values.allocate( + pb, num_inputs, FMT(annotation_prefix, "intermediate_values")); + + libsnark::pb_linear_combination iv; + iv.assign(pb, get_iv()); + + // First step: hash_output[0] <- mimc_mp(iv, i[0]) + + _compression_functions.emplace_back(new compFnT( + pb, + iv, + inputs[0], + _intermediate_values[0], + FMT(annotation_prefix, " compression_functions[0]"))); + + // Intermediate invocations of the compression function. + for (size_t i = 1; i < num_inputs; ++i) { + _compression_functions.emplace_back(new compFnT( + pb, + _intermediate_values[i - 1], + inputs[i], + _intermediate_values[i], + FMT(annotation_prefix, " compression_functions[%zu]", i))); + } + + // Last invocation of compression function to finalize. + libsnark::pb_linear_combination num_inputs_lc; + num_inputs_lc.assign(pb, FieldT(num_inputs)); + _compression_functions.emplace_back(new compFnT( + pb, + _intermediate_values[num_inputs - 1], + num_inputs_lc, + result, + FMT(annotation_prefix, " compression_functions[%zu]", num_inputs))); + + assert(_compression_functions.size() == num_inputs + 1); +} + +template +void mimc_input_hasher::generate_r1cs_constraints() +{ + for (const std::shared_ptr &cf : _compression_functions) { + cf->generate_r1cs_constraints(); + } +} + +template +void mimc_input_hasher::generate_r1cs_witness() const +{ + for (const std::shared_ptr &cf : _compression_functions) { + cf->generate_r1cs_witness(); + } +} + +template +FieldT mimc_input_hasher::get_iv() +{ + // IV generated as: + // zeth.core.mimc._keccak_256( + // zeth.core.mimc._str_to_bytes("clearmatics_hash_seed")) + // See: client/zeth/core/mimc.py + return FieldT( + "1319653706411738841819622385631198771438854383955240040834092139" + "7545324034315"); +} + +template +FieldT mimc_input_hasher::compute_hash( + const std::vector &values) +{ + FieldT h = get_iv(); + for (const FieldT &v : values) { + h = compFnT::get_hash(h, v); + } + return compFnT::get_hash(h, FieldT(values.size())); +} + +} // namespace libzeth + +#endif // __ZETH_CIRCUITS_MIMC_MIMC_INPUT_HASHER_TCC__ diff --git a/libzeth/circuits/mimc/mimc_mp.hpp b/libzeth/circuits/mimc/mimc_mp.hpp index 757ae5d22..3933a8947 100644 --- a/libzeth/circuits/mimc/mimc_mp.hpp +++ b/libzeth/circuits/mimc/mimc_mp.hpp @@ -5,43 +5,44 @@ #ifndef __ZETH_CIRCUITS_MIMC_MP_HPP__ #define __ZETH_CIRCUITS_MIMC_MP_HPP__ -#include "libzeth/circuits/mimc/mimc.hpp" +#include "libzeth/circuits/mimc/mimc_permutation.hpp" namespace libzeth { -/// This gadget implements the interface of the HashTreeT template +/// This gadget implements the interface of the HashTreeT template. /// -/// MiMC_mp_gadget enforces correct computation of MiMC compression function -/// based on a the Miyaguchi-Preneel compression construct and a +/// MiMC_mp_gadget enforces correct computation of the MiMC compression +/// function, based on a the Miyaguchi-Preneel compression construct using a /// MiMC_permutation_gadget instance, PermutationT, operating on FieldT /// elements. -template -class MiMC_mp_gadget : public libsnark::gadget +/// +/// This class contains only an instance of PermutationT, with parameters +/// configured to make it efficiently compute Miyaguchi-Preneel. As such, it +/// may appear as first sight that it should inherit from PermutationT. We do +/// not inherit from PermutationT, either publicly (because the "is-a" +/// relationship does not hold in general), or privately (because the +/// pb_linear_combination interface does not support immediate construction of +/// `x + y`, making the constructor very awkard - this is also the reason that +/// a pointer is required, rather than a simple instance of PermutationT). +/// Further, we do not inherit from libsnark::gadget<>, as it is not necessary +/// and would just add unused data to the class. +template class MiMC_mp_gadget { private: - // First input - libsnark::pb_variable x; - // Second input - libsnark::pb_variable y; - // Permutation gadget - PermutationT permutation_gadget; - // Output variable - libsnark::pb_variable output; + std::shared_ptr permutation_gadget; public: MiMC_mp_gadget( libsnark::protoboard &pb, - const libsnark::pb_variable x, - const libsnark::pb_variable y, + const libsnark::pb_linear_combination &x, + const libsnark::pb_linear_combination &y, + const libsnark::pb_variable &result, const std::string &annotation_prefix = "MiMC_mp_gadget"); void generate_r1cs_constraints(); void generate_r1cs_witness() const; - // Returns the hash computed - const libsnark::pb_variable &result() const; - // Returns the hash (field element) static FieldT get_hash(const FieldT x, FieldT y); }; diff --git a/libzeth/circuits/mimc/mimc_mp.tcc b/libzeth/circuits/mimc/mimc_mp.tcc index bb86ef0ec..332dae5d7 100644 --- a/libzeth/circuits/mimc/mimc_mp.tcc +++ b/libzeth/circuits/mimc/mimc_mp.tcc @@ -5,59 +5,40 @@ #ifndef __ZETH_CIRCUITS_MIMC_MP_TCC__ #define __ZETH_CIRCUITS_MIMC_MP_TCC__ +#include "mimc_mp.hpp" + namespace libzeth { template MiMC_mp_gadget::MiMC_mp_gadget( libsnark::protoboard &pb, - const libsnark::pb_variable x, - const libsnark::pb_variable y, + const libsnark::pb_linear_combination &x, + const libsnark::pb_linear_combination &y, + const libsnark::pb_variable &result, const std::string &annotation_prefix) - : libsnark::gadget(pb, annotation_prefix) - , x(x) - , y(y) - , permutation_gadget( - pb, x, y, FMT(this->annotation_prefix, " permutation_gadget")) { - // Allocates output variable - output.allocate(pb, FMT(this->annotation_prefix, " output")); + // Adding x+y to the output of the permutation yields the Miyaguchi-Preneel + // equation: + // + // result = permutation(x, y) + x + y + + libsnark::pb_linear_combination x_plus_y; + x_plus_y.assign(pb, x + y); + permutation_gadget.reset(new PermutationT( + pb, x, y, result, x_plus_y, FMT(annotation_prefix, " MP"))); } template void MiMC_mp_gadget::generate_r1cs_constraints() { - // Setting constraints for the permutation gadget - permutation_gadget.generate_r1cs_constraints(); - - const libsnark::pb_variable &m = x; - const libsnark::pb_variable &key = y; - - // Adding constraint for the Miyaguchi-Preneel equation - this->pb.add_r1cs_constraint( - libsnark::r1cs_constraint( - permutation_gadget.result() + m + key, 1, output), - FMT(this->annotation_prefix, " out=k+E_k(m_i)+m_i")); + permutation_gadget->generate_r1cs_constraints(); } template void MiMC_mp_gadget::generate_r1cs_witness() const { - // Generating witness for the gadget - permutation_gadget.generate_r1cs_witness(); - - // Filling output variables for Miyaguchi-Preenel equation - this->pb.val(output) = this->pb.val(y) + - this->pb.val(permutation_gadget.result()) + - this->pb.val(x); -} - -template -const libsnark::pb_variable - &MiMC_mp_gadget::result() const -{ - // Returns the output - return output; + permutation_gadget->generate_r1cs_witness(); } // Returns the hash of two elements @@ -68,23 +49,27 @@ FieldT MiMC_mp_gadget::get_hash(const FieldT x, FieldT y) libsnark::pb_variable pb_x; libsnark::pb_variable pb_y; + libsnark::pb_variable result; // Allocates and fill with the x and y - pb_x.allocate(pb, " x"); + pb_x.allocate(pb, "x"); pb.val(pb_x) = x; - pb_y.allocate(pb, " y"); + pb_y.allocate(pb, "y"); pb.val(pb_y) = y; + result.allocate(pb, "result"); + // Initialize the Hash MiMC_mp_gadget mimc_hasher( - pb, pb_x, pb_y, " mimc_hash"); + pb, pb_x, pb_y, result, " mimc_hash"); // Computes the hash + mimc_hasher.generate_r1cs_constraints(); mimc_hasher.generate_r1cs_witness(); // Returns the hash - return pb.val(mimc_hasher.result()); + return pb.val(result); } } // namespace libzeth diff --git a/libzeth/circuits/mimc/mimc_permutation.hpp b/libzeth/circuits/mimc/mimc_permutation.hpp new file mode 100644 index 000000000..2cc82f9e1 --- /dev/null +++ b/libzeth/circuits/mimc/mimc_permutation.hpp @@ -0,0 +1,78 @@ +// DISCLAIMER: +// Content taken and adapted from: +// https://github.com/HarryR/ethsnarks/blob/master/src/gadgets/mimc.hpp + +#ifndef __ZETH_CIRCUITS_MIMC_PERMUTATION_HPP__ +#define __ZETH_CIRCUITS_MIMC_PERMUTATION_HPP__ + +#include "libzeth/circuits/mimc/mimc_round.hpp" + +namespace libzeth +{ + +/// MiMC_permutation_gadget enforces correct computation of the MiMC +/// permutation, denoted MiMC_r(k, m) in the Zeth specifications +/// (https://github.com/clearmatics/zeth-specifications), by peforming +/// NumRounds MiMC rounds with the given Exponent. An optional `add_to_result` +/// value can be passed in to be added to the result of the regular MiMC +/// permutation (without requiring extra constraints). +template +class MiMC_permutation_gadget : public libsnark::gadget +{ +private: + // Round constants only available up to some maximum number of rounds + static const size_t MaxRounds = 65; + static_assert( + NumRounds <= MaxRounds, "NumRounds must be less than MaxRounds"); + + // Instantiate round gadget with exponent = Exponent + using RoundT = MiMC_round_gadget; + + // Vector of round constants + static std::vector round_constants; + static bool round_constants_initialized; + + // Vector of intermediate result values + std::array, NumRounds> round_results; + + // Vector of MiMC round_gadgets + std::vector round_gadgets; + + // Common initialization + MiMC_permutation_gadget( + libsnark::protoboard &pb, + const libsnark::pb_linear_combination &msg, + const libsnark::pb_linear_combination &key, + const libsnark::pb_variable &result, + const libsnark::pb_linear_combination &add_to_result, + const bool add_to_result_is_valid, + const std::string &annotation_prefix); + +public: + MiMC_permutation_gadget( + libsnark::protoboard &pb, + const libsnark::pb_linear_combination &msg, + const libsnark::pb_linear_combination &key, + const libsnark::pb_variable &result, + const std::string &annotation_prefix = "MiMC_permutation_gadget"); + + MiMC_permutation_gadget( + libsnark::protoboard &pb, + const libsnark::pb_linear_combination &msg, + const libsnark::pb_linear_combination &key, + const libsnark::pb_variable &result, + const libsnark::pb_linear_combination &add_to_result, + const std::string &annotation_prefix = "MiMC_permutation_gadget"); + + void generate_r1cs_constraints(); + void generate_r1cs_witness() const; + + // Constants vector initialization + void setup_sha3_constants(); +}; + +} // namespace libzeth + +#include "libzeth/circuits/mimc/mimc_permutation.tcc" + +#endif // __ZETH_CIRCUITS_MIMC_PERMUTATION_HPP__ diff --git a/libzeth/circuits/mimc/mimc.tcc b/libzeth/circuits/mimc/mimc_permutation.tcc similarity index 71% rename from libzeth/circuits/mimc/mimc.tcc rename to libzeth/circuits/mimc/mimc_permutation.tcc index 6f6487646..827b53cf2 100644 --- a/libzeth/circuits/mimc/mimc.tcc +++ b/libzeth/circuits/mimc/mimc_permutation.tcc @@ -2,10 +2,10 @@ // Content taken and adapted from: // https://github.com/HarryR/ethsnarks/blob/master/src/gadgets/mimc.hpp -#ifndef __ZETH_CIRCUITS_MIMC_TCC__ -#define __ZETH_CIRCUITS_MIMC_TCC__ +#ifndef __ZETH_CIRCUITS_MIMC_PERMUTATION_TCC__ +#define __ZETH_CIRCUITS_MIMC_PERMUTATION_TCC__ -#include "libzeth/circuits/mimc/mimc.hpp" +#include "libzeth/circuits/mimc/mimc_permutation.hpp" namespace libzeth { @@ -21,8 +21,11 @@ bool MiMC_permutation_gadget:: template MiMC_permutation_gadget::MiMC_permutation_gadget( libsnark::protoboard &pb, - const libsnark::pb_variable &msg, - const libsnark::pb_variable &key, + const libsnark::pb_linear_combination &msg, + const libsnark::pb_linear_combination &key, + const libsnark::pb_variable &result, + const libsnark::pb_linear_combination &add_to_result, + const bool add_to_result_is_valid, const std::string &annotation_prefix) : libsnark::gadget(pb, annotation_prefix) { @@ -31,36 +34,100 @@ MiMC_permutation_gadget::MiMC_permutation_gadget( // Initialize the round gadgets round_gadgets.reserve(NumRounds); - const libsnark::pb_variable *round_msg = &msg; - for (size_t i = 0; i < NumRounds; i++) { - // Set the input of the next round with the output variable of the - // previous round (except for round 0) + + // First round uses round_msg as an input. + round_results[0].allocate( + this->pb, FMT(this->annotation_prefix, " round_result[0]")); + round_gadgets.emplace_back( + this->pb, + msg, + key, + round_constants[0], + round_results[0], + FMT(this->annotation_prefix, " round[0]")); + + // Intermediate rounds use the output of the previous round and output to + // an intermediate variable (allocated here) + for (size_t i = 1; i < NumRounds - 1; i++) { + // Allocate intermediate round result. round_results[i].allocate( this->pb, FMT(this->annotation_prefix, " round_result[%zu]", i)); - const bool is_last = (i == (NumRounds - 1)); - - // Initialize and add the current round gadget into the rounds gadget - // vector, picking the relative constant + // Initialize the current round gadget into the vector of round gadgets + // vector, picking the correct round constant. round_gadgets.emplace_back( this->pb, - *round_msg, + round_results[i - 1], key, round_constants[i], round_results[i], - is_last, FMT(this->annotation_prefix, " round[%zu]", i)); + } - round_msg = &round_results[i]; + // For last round, output to the result variable and add `key` to the + // result, along with any add_to_result. + round_results[NumRounds - 1] = result; + + if (add_to_result_is_valid) { + libsnark::pb_linear_combination key_plus_add_to_result; + key_plus_add_to_result.assign(this->pb, key + add_to_result); + round_gadgets.emplace_back( + this->pb, + round_results[NumRounds - 2], + key, + round_constants[NumRounds - 1], + round_results[NumRounds - 1], + key_plus_add_to_result, + FMT(this->annotation_prefix, " round[%zu]", NumRounds - 1)); + } else { + round_gadgets.emplace_back( + this->pb, + round_results[NumRounds - 2], + key, + round_constants[NumRounds - 1], + round_results[NumRounds - 1], + key, + FMT(this->annotation_prefix, " round[%zu]", NumRounds - 1)); } } +template +MiMC_permutation_gadget::MiMC_permutation_gadget( + libsnark::protoboard &pb, + const libsnark::pb_linear_combination &msg, + const libsnark::pb_linear_combination &key, + const libsnark::pb_variable &result, + const std::string &annotation_prefix) + : MiMC_permutation_gadget( + pb, + msg, + key, + result, + libsnark::pb_linear_combination(), + false, + annotation_prefix) +{ +} + +template +MiMC_permutation_gadget::MiMC_permutation_gadget( + libsnark::protoboard &pb, + const libsnark::pb_linear_combination &msg, + const libsnark::pb_linear_combination &key, + const libsnark::pb_variable &result, + const libsnark::pb_linear_combination &add_to_result, + const std::string &annotation_prefix) + : MiMC_permutation_gadget( + pb, msg, key, result, add_to_result, true, annotation_prefix) +{ +} + template void MiMC_permutation_gadget:: generate_r1cs_constraints() { // For each round, generates the constraints for the corresponding round - // gadget + // gadget. for (auto &gadget : round_gadgets) { gadget.generate_r1cs_constraints(); } @@ -70,20 +137,13 @@ template void MiMC_permutation_gadget:: generate_r1cs_witness() const { - // For each round, generates the witness for the corresponding round gadget + // For each round, generates the witness for the corresponding round + // gadget. for (auto &gadget : round_gadgets) { gadget.generate_r1cs_witness(); } } -template -const libsnark::pb_variable - &MiMC_permutation_gadget::result() const -{ - // Returns the result of the last encryption/permutation - return round_results.back(); -} - // The following constants correspond to the iterative computation of sha3_256 // hash function over the initial seed "clearmatics_mt_seed". See: // client/zethCodeConstantsGeneration.py for more details @@ -95,7 +155,8 @@ void MiMC_permutation_gadget:: return; } - round_constants.reserve(NumRounds); + // For simplicity, always generate constants for MaxRounds. + round_constants.reserve(MaxRounds); // The constant is set to "0" in the first round of MiMC permutation (see: // https://eprint.iacr.org/2016/492.pdf) @@ -233,63 +294,12 @@ void MiMC_permutation_gadget:: "74544443080560119509560262720937836494902079641131221139823065933367514898276")); round_constants.push_back(FieldT( "36856043990250139109110674451326757800006928098085552406998173198427373834846")); - round_constants.push_back(FieldT( - "89876265522016337550524744707009312276376790319197860491657618155961055194949")); - round_constants.push_back(FieldT( - "110827903006446644954303964609043521818500007209339765337677716791359271709709")); - round_constants.push_back(FieldT( - "19507166101303357762640682204614541813131172968402646378144792525256753001746")); - round_constants.push_back(FieldT( - "107253144238416209039771223682727408821599541893659793703045486397265233272366")); - round_constants.push_back(FieldT( - "50595349797145823467207046063156205987118773849740473190540000392074846997926")); - round_constants.push_back(FieldT( - "44703482889665897122601827877356260454752336134846793080442136212838463818460")); - round_constants.push_back(FieldT( - "72587689163044446617379334085046687704026377073069181869522598220420039333904")); - round_constants.push_back(FieldT( - "102651401786920090371975453907921346781687924794638352783098945209363379010084")); - round_constants.push_back(FieldT( - "93452870373806728605513560063145330258676656934938716540885043830342716774537")); - round_constants.push_back(FieldT( - "78296669596559313198894751403351590225284664485458045241864014863714864424243")); - round_constants.push_back(FieldT( - "115089219682233450926699488628267277641700041858332325616476033644461392438459")); - round_constants.push_back(FieldT( - "12503229023709380637667243769419362848195673442247523096260626221166887267863")); - round_constants.push_back(FieldT( - "4710254915107472945023322521703570589554948344762175784852248799008742965033")); - round_constants.push_back(FieldT( - "7718237385336937042064321465151951780913850666971695410931421653062451982185")); - round_constants.push_back(FieldT( - "115218487714637830492048339157964615618803212766527542809597433013530253995292")); - round_constants.push_back(FieldT( - "30146276054995781136885926012526705051587400199196161599789168368938819073525")); - round_constants.push_back(FieldT( - "81645575619063610562025782726266715757461113967190574155696199274188206173145")); - round_constants.push_back(FieldT( - "103065286526250765895346723898189993161715212663393551904337911885906019058491")); - round_constants.push_back(FieldT( - "19401253163389218637767300383887292725233192135251696535631823232537040754970")); - round_constants.push_back(FieldT( - "39843332085422732827481601668576197174769872102167705377474553046529879993254")); - round_constants.push_back(FieldT( - "27288628349107331632228897768386713717171618488175838305048363657709955104492")); - round_constants.push_back(FieldT( - "63512042813079522866974560192099016266996589861590638571563519363305976473166")); - round_constants.push_back(FieldT( - "88099896769123586138541398153669061847681467623298355942484821247745931328016")); - round_constants.push_back(FieldT( - "69497565113721491657291572438744729276644895517335084478398926389231201598482")); - round_constants.push_back(FieldT( - "17118586436782638926114048491697362406660860405685472757612739816905521144705")); - round_constants.push_back(FieldT( - "50507769484714413215987736701379019852081133212073163694059431350432441698257")); // clang-format on + assert(round_constants.size() == MaxRounds); round_constants_initialized = true; } } // namespace libzeth -#endif // __ZETH_CIRCUITS_MIMC_TCC__ +#endif // __ZETH_CIRCUITS_MIMC_PERMUTATION_TCC__ diff --git a/libzeth/circuits/mimc/mimc_round.hpp b/libzeth/circuits/mimc/mimc_round.hpp index 508bf0fbb..2c7084e74 100644 --- a/libzeth/circuits/mimc/mimc_round.hpp +++ b/libzeth/circuits/mimc/mimc_round.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -25,10 +25,10 @@ class MiMC_round_gadget : public libsnark::gadget bit_utils::hamming_weight() - 2; // Message of the current round - const libsnark::pb_variable msg; + const libsnark::pb_linear_combination msg; // Key of the current round - const libsnark::pb_variable key; + const libsnark::pb_linear_combination key; // Round constant of the current round const FieldT round_const; @@ -36,20 +36,36 @@ class MiMC_round_gadget : public libsnark::gadget // Result variable const libsnark::pb_variable result; - // Boolean variable to add the key after the round - const bool add_key_to_result; + // Optional linear combination to add after the final round + const libsnark::pb_linear_combination add_to_result; + + // Flag indicating whether add_to_result is valid + const bool add_to_result_is_valid; // Intermediate values std::vector> exponents; + // Initialization code shared by constructors. + void initialize(); + public: MiMC_round_gadget( libsnark::protoboard &pb, - const libsnark::pb_variable &msg, - const libsnark::pb_variable &key, + const libsnark::pb_linear_combination &msg, + const libsnark::pb_linear_combination &key, + const FieldT &round_const, + libsnark::pb_variable &result, + const std::string &annotation_prefix = "MiMC_round_gadget"); + + /// Constructor that supports adding some linear_combination to the final + /// result. + MiMC_round_gadget( + libsnark::protoboard &pb, + const libsnark::pb_linear_combination &msg, + const libsnark::pb_linear_combination &key, const FieldT &round_const, libsnark::pb_variable &result, - const bool add_k_to_result, + const libsnark::pb_linear_combination &add_to_result, const std::string &annotation_prefix = "MiMC_round_gadget"); void generate_r1cs_constraints(); diff --git a/libzeth/circuits/mimc/mimc_round.tcc b/libzeth/circuits/mimc/mimc_round.tcc index 7185104de..a54c12eca 100644 --- a/libzeth/circuits/mimc/mimc_round.tcc +++ b/libzeth/circuits/mimc/mimc_round.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -10,25 +10,50 @@ namespace libzeth { +template +void MiMC_round_gadget::initialize() +{ + // Each condition requires an intermediate variable, except the final one, + // which uses _result (and optionally _k). + exponents.resize(NUM_CONDITIONS - 1); +} + template MiMC_round_gadget::MiMC_round_gadget( libsnark::protoboard &pb, - const libsnark::pb_variable &msg, - const libsnark::pb_variable &key, + const libsnark::pb_linear_combination &msg, + const libsnark::pb_linear_combination &key, const FieldT &round_const, libsnark::pb_variable &result, - const bool add_key_to_result, const std::string &annotation_prefix) : libsnark::gadget(pb, annotation_prefix) , msg(msg) , key(key) , round_const(round_const) , result(result) - , add_key_to_result(add_key_to_result) + , add_to_result_is_valid(false) { - // Each condition requires an intermediate variable, except the final one, - // which uses _result (and optionally _k). - exponents.resize(NUM_CONDITIONS - 1); + initialize(); +} + +template +MiMC_round_gadget::MiMC_round_gadget( + libsnark::protoboard &pb, + const libsnark::pb_linear_combination &msg, + const libsnark::pb_linear_combination &key, + const FieldT &round_const, + libsnark::pb_variable &result, + const libsnark::pb_linear_combination &add_to_result, + const std::string &annotation_prefix) + : libsnark::gadget(pb, annotation_prefix) + , msg(msg) + , key(key) + , round_const(round_const) + , result(result) + , add_to_result(add_to_result) + , add_to_result_is_valid(true) +{ + initialize(); } template @@ -82,14 +107,16 @@ void MiMC_round_gadget::generate_r1cs_constraints() } assert(exp_idx == exponents.size()); - // Final multiply (lowest-order bit is known to be 1): - // result = last * t (+ k) - // such that: - // result (- k) = last * t - if (add_key_to_result) { + // Final multiply (lowest-order bit is known to be 1), + if (add_to_result_is_valid) { + // addition of add_to_result: + // result = last * t + add_to_result + // <=> result - add_to_result = last * t this->pb.add_r1cs_constraint( - libsnark::r1cs_constraint(*last, t, result - key), - FMT(this->annotation_prefix, " calc_t^%zu_add_key", Exponent)); + libsnark::r1cs_constraint(*last, t, result - add_to_result), + FMT(this->annotation_prefix, + " calc_t^%zu_add_to_result", + Exponent)); } else { this->pb.add_r1cs_constraint( libsnark::r1cs_constraint(*last, t, result), @@ -100,9 +127,12 @@ void MiMC_round_gadget::generate_r1cs_constraints() template void MiMC_round_gadget::generate_r1cs_witness() const { + key.evaluate(this->pb); + msg.evaluate(this->pb); + constexpr size_t mask = 1 << (EXPONENT_NUM_BITS - 1); - const FieldT k_val = this->pb.val(key); - const FieldT t = this->pb.val(msg) + k_val + round_const; + const FieldT k_val = this->pb.lc_val(key); + const FieldT t = this->pb.lc_val(msg) + k_val + round_const; // First intermediate variable has value t^2 size_t exp = Exponent << 1; @@ -122,10 +152,11 @@ void MiMC_round_gadget::generate_r1cs_witness() const this->pb.val(exponents[var_idx++]) = v; } - // v = v * t (+ k) + // v = v * t + add_to_result v = v * t; - if (add_key_to_result) { - v = v + k_val; + if (add_to_result_is_valid) { + add_to_result.evaluate(this->pb); + v = v + this->pb.lc_val(add_to_result); } this->pb.val(result) = v; } diff --git a/libzeth/circuits/prfs/prf.hpp b/libzeth/circuits/prfs/prf.hpp index 20fa67e95..d0b12090b 100644 --- a/libzeth/circuits/prfs/prf.hpp +++ b/libzeth/circuits/prfs/prf.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/circuits/prfs/prf.tcc b/libzeth/circuits/prfs/prf.tcc index b74c2f883..644975507 100644 --- a/libzeth/circuits/prfs/prf.tcc +++ b/libzeth/circuits/prfs/prf.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/circuits/safe_arithmetic.cpp b/libzeth/circuits/safe_arithmetic.cpp index 978fe6cb3..29b4640f6 100644 --- a/libzeth/circuits/safe_arithmetic.cpp +++ b/libzeth/circuits/safe_arithmetic.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/circuits/safe_arithmetic.hpp b/libzeth/circuits/safe_arithmetic.hpp index 4ba0c87f6..48e1aa2c4 100644 --- a/libzeth/circuits/safe_arithmetic.hpp +++ b/libzeth/circuits/safe_arithmetic.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/bits.cpp b/libzeth/core/bits.cpp index 557a56fec..28dbe8f2e 100644 --- a/libzeth/core/bits.cpp +++ b/libzeth/core/bits.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/bits.hpp b/libzeth/core/bits.hpp index ba8c6ee96..e717796dc 100644 --- a/libzeth/core/bits.hpp +++ b/libzeth/core/bits.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/bits.tcc b/libzeth/core/bits.tcc index e12453f1b..8c0bb87a9 100644 --- a/libzeth/core/bits.tcc +++ b/libzeth/core/bits.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/chacha_rng.cpp b/libzeth/core/chacha_rng.cpp index 13c04ed32..2e062b850 100644 --- a/libzeth/core/chacha_rng.cpp +++ b/libzeth/core/chacha_rng.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/chacha_rng.hpp b/libzeth/core/chacha_rng.hpp index 6aec3801a..4425f34c8 100644 --- a/libzeth/core/chacha_rng.hpp +++ b/libzeth/core/chacha_rng.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/evaluator_from_lagrange.hpp b/libzeth/core/evaluator_from_lagrange.hpp index 4c641b63c..c72a0231b 100644 --- a/libzeth/core/evaluator_from_lagrange.hpp +++ b/libzeth/core/evaluator_from_lagrange.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/evaluator_from_lagrange.tcc b/libzeth/core/evaluator_from_lagrange.tcc index f21ffd59d..331510e4c 100644 --- a/libzeth/core/evaluator_from_lagrange.tcc +++ b/libzeth/core/evaluator_from_lagrange.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/extended_proof.hpp b/libzeth/core/extended_proof.hpp index 01462aca2..8aa4d51d3 100644 --- a/libzeth/core/extended_proof.hpp +++ b/libzeth/core/extended_proof.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -24,6 +24,7 @@ template class extended_proof extended_proof( typename snarkT::proof &&in_proof, libsnark::r1cs_primary_input> &&in_primary_inputs); + const typename snarkT::proof &get_proof() const; const libsnark::r1cs_primary_input> &get_primary_inputs() diff --git a/libzeth/core/extended_proof.tcc b/libzeth/core/extended_proof.tcc index 4886330ec..de99118ac 100644 --- a/libzeth/core/extended_proof.tcc +++ b/libzeth/core/extended_proof.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/field_element_utils.hpp b/libzeth/core/field_element_utils.hpp index eb0fbb1c6..84e6c68e7 100644 --- a/libzeth/core/field_element_utils.hpp +++ b/libzeth/core/field_element_utils.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -58,6 +58,17 @@ template std::string field_element_to_json(const FieldT &el); template FieldT field_element_from_json(const std::string &json); +/// Write a field element as bytes. Base field elements are written in plain +/// (non-Montgomery) form as fixed-size big-endian integers. Extension field +/// elements are written as a series of components. +template +void field_element_write_bytes(const FieldT &el, std::ostream &out_s); + +/// Read a field element as bytes, in the format described for +/// field_element_write_bytes. +template +void field_element_read_bytes(FieldT &el, std::istream &in_s); + } // namespace libzeth #include "libzeth/core/field_element_utils.tcc" diff --git a/libzeth/core/field_element_utils.tcc b/libzeth/core/field_element_utils.tcc index 07ec8046a..824f2cd61 100644 --- a/libzeth/core/field_element_utils.tcc +++ b/libzeth/core/field_element_utils.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -100,6 +100,49 @@ public: } }; +// Generic reader and write for fields and field extensions. +template class field_element_bytes +{ +public: + static void write(const FieldT &field_el, std::ostream &out_s) + { + for (size_t i = 0; i < FieldT::tower_extension_degree; ++i) { + field_element_write_bytes(field_el.coeffs[i], out_s); + } + } + static void read(FieldT &field_el, std::istream &in_s) + { + for (size_t i = 0; i < FieldT::tower_extension_degree; ++i) { + field_element_read_bytes(field_el.coeffs[i], in_s); + } + } +}; + +/// Implementation of field_element_bytes for the base-case of Fp_model types. +/// Big-endian bigint values (i.e. not in montgomery form). +template &modulus> +class field_element_bytes> +{ +public: + using Field = libff::Fp_model; + static void write(const Field &field_el, std::ostream &out_s) + { + // Convert to bigint, reverse bytes in-place, and write to stream. + const libff::bigint bi = field_el.as_bigint(); + std::reverse((char *)(&bi), (char *)(&bi + 1)); + out_s.write((const char *)(&bi.data[0]), sizeof(bi)); + } + static void read(Field &field_el, std::istream &in_s) + { + // Read bigint from stream, reverse bytes in-place and convert to field + // element. + libff::bigint res; + in_s.read((char *)(&res.data[0]), sizeof(res)); + std::reverse((char *)(&res), (char *)(&res + 1)); + field_el = Field(res); + } +}; + } // namespace internal template @@ -161,6 +204,18 @@ FieldT field_element_from_json(const std::string &json) return result; } +template +void field_element_write_bytes(const FieldT &el, std::ostream &out_s) +{ + internal::field_element_bytes::write(el, out_s); +} + +template +void field_element_read_bytes(FieldT &el, std::istream &in_s) +{ + internal::field_element_bytes::read(el, in_s); +} + } // namespace libzeth #endif // __ZETH_FIELD_ELEMENT_UTILS_TCC__ diff --git a/libzeth/core/group_element_utils.hpp b/libzeth/core/group_element_utils.hpp index 306254853..d22a9733d 100644 --- a/libzeth/core/group_element_utils.hpp +++ b/libzeth/core/group_element_utils.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -12,18 +12,41 @@ namespace libzeth /// Write a group element as a json string to a stream template -void point_affine_write_json(const GroupT &point, std::ostream &out_s); +void group_element_write_json(const GroupT &point, std::ostream &out_s); /// Read a JSON string from a stream and convert it into a group element template -void point_affine_read_json(GroupT &point, std::istream &in_s); +void group_element_read_json(GroupT &point, std::istream &in_s); /// Convert a group element to a json string (array of hexadecimal strings). -template std::string point_affine_to_json(const GroupT &point); +template +std::string group_element_to_json(const GroupT &point); /// Convert a JSON string into a group element template -GroupT point_affine_from_json(const std::string &json); +GroupT group_element_from_json(const std::string &json); + +/// Write a group element as bytes to a stream. The elements are written as X +/// and Y coordinates of the affine form, where each coordinate is written +/// using field_element_write_bytes. +template +void group_element_write_bytes(const GroupT &point, std::ostream &out_s); + +/// Read a group elements as bytes from a stream, in the format described for +/// group_element_write_bytes. +template +void group_element_read_bytes(GroupT &point, std::istream &in_s); + +/// Write a collection of group elements as bytes to a stream, using +/// group_element_write_bytes. +template +void group_elements_write_bytes( + const GroupCollectionT &points, std::ostream &out_s); + +/// Read a collection of group elements as bytes, using +/// group_elements_read_bytes. +template +void group_elements_read_bytes(GroupCollectionT &points, std::istream &in_s); } // namespace libzeth diff --git a/libzeth/core/group_element_utils.tcc b/libzeth/core/group_element_utils.tcc index 28ef5ec74..6dbadcc6b 100644 --- a/libzeth/core/group_element_utils.tcc +++ b/libzeth/core/group_element_utils.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -6,6 +6,7 @@ #define __ZETH_CORE_GROUP_ELEMENT_UTILS_TCC__ #include "libzeth/core/field_element_utils.hpp" +#include "libzeth/serialization/stream_utils.hpp" namespace libzeth { @@ -30,7 +31,7 @@ template static bool coordinate_equals_one(const FieldT &f) } // namespace internal template -void point_affine_write_json(const GroupT &point, std::ostream &out_s) +void group_element_write_json(const GroupT &point, std::ostream &out_s) { GroupT affine_p = point; affine_p.to_affine_coordinates(); @@ -42,7 +43,7 @@ void point_affine_write_json(const GroupT &point, std::ostream &out_s) } template -void point_affine_read_json(GroupT &point, std::istream &in_s) +void group_element_read_json(GroupT &point, std::istream &in_s) { char sep; @@ -73,21 +74,59 @@ void point_affine_read_json(GroupT &point, std::istream &in_s) } } -template std::string point_affine_to_json(const GroupT &point) +template std::string group_element_to_json(const GroupT &point) { std::stringstream ss; - point_affine_write_json(point, ss); + group_element_write_json(point, ss); return ss.str(); } -template GroupT point_affine_from_json(const std::string &json) +template +GroupT group_element_from_json(const std::string &json) { std::stringstream ss(json); GroupT result; - point_affine_read_json(result, ss); + group_element_read_json(result, ss); return result; } +template +void group_element_write_bytes(const GroupT &point, std::ostream &out_s) +{ + typename std::decay::type affine_p = point; + affine_p.to_affine_coordinates(); + field_element_write_bytes(affine_p.X, out_s); + field_element_write_bytes(affine_p.Y, out_s); +} + +template +void group_element_read_bytes(GroupT &point, std::istream &in_s) +{ + field_element_read_bytes(point.X, in_s); + field_element_read_bytes(point.Y, in_s); + if (internal::coordinate_equals_zero(point.X) && + internal::coordinate_equals_one(point.Y)) { + point.Z = point.Z.zero(); + } else { + point.Z = point.Z.one(); + } +} + +template +void group_elements_write_bytes( + const GroupCollectionT &points, std::ostream &out_s) +{ + collection_write_bytes( + points, out_s); +} + +template +void group_elements_read_bytes(GroupCollectionT &points, std::istream &in_s) +{ + collection_read_bytes( + points, in_s); +} + } // namespace libzeth #endif // __ZETH_CORE_GROUP_ELEMENT_UTILS_TCC__ diff --git a/libzeth/core/hash_stream.hpp b/libzeth/core/hash_stream.hpp index b5e03004e..5210797a5 100644 --- a/libzeth/core/hash_stream.hpp +++ b/libzeth/core/hash_stream.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/hash_stream.tcc b/libzeth/core/hash_stream.tcc index 82091fa4a..09a9a6439 100644 --- a/libzeth/core/hash_stream.tcc +++ b/libzeth/core/hash_stream.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/include_libff.hpp b/libzeth/core/include_libff.hpp index 5ece1eda2..77fb90edf 100644 --- a/libzeth/core/include_libff.hpp +++ b/libzeth/core/include_libff.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/include_libsnark.hpp b/libzeth/core/include_libsnark.hpp index ceccc7d5e..65cb132d8 100644 --- a/libzeth/core/include_libsnark.hpp +++ b/libzeth/core/include_libsnark.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/joinsplit_input.hpp b/libzeth/core/joinsplit_input.hpp index 84001110b..1cf4bd36b 100644 --- a/libzeth/core/joinsplit_input.hpp +++ b/libzeth/core/joinsplit_input.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/multi_exp.hpp b/libzeth/core/multi_exp.hpp index 6a4032e39..5ef1ce866 100644 --- a/libzeth/core/multi_exp.hpp +++ b/libzeth/core/multi_exp.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/multi_exp.tcc b/libzeth/core/multi_exp.tcc index 209a2ded8..daf71016d 100644 --- a/libzeth/core/multi_exp.tcc +++ b/libzeth/core/multi_exp.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/note.hpp b/libzeth/core/note.hpp index 609c36ec6..0e00a5987 100644 --- a/libzeth/core/note.hpp +++ b/libzeth/core/note.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/utils.cpp b/libzeth/core/utils.cpp index 350380014..3c479ada0 100644 --- a/libzeth/core/utils.cpp +++ b/libzeth/core/utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/utils.hpp b/libzeth/core/utils.hpp index becd8e54d..7bc2685c9 100644 --- a/libzeth/core/utils.hpp +++ b/libzeth/core/utils.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/core/utils.tcc b/libzeth/core/utils.tcc index a05bd665c..14f791ce6 100644 --- a/libzeth/core/utils.tcc +++ b/libzeth/core/utils.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/mpc/groth16/mpc_hash.cpp b/libzeth/mpc/groth16/mpc_hash.cpp index d5931e5c5..253020e9c 100644 --- a/libzeth/mpc/groth16/mpc_hash.cpp +++ b/libzeth/mpc/groth16/mpc_hash.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/mpc/groth16/mpc_hash.hpp b/libzeth/mpc/groth16/mpc_hash.hpp index 2bd2c3aab..660b8fc7a 100644 --- a/libzeth/mpc/groth16/mpc_hash.hpp +++ b/libzeth/mpc/groth16/mpc_hash.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/mpc/groth16/mpc_utils.hpp b/libzeth/mpc/groth16/mpc_utils.hpp index ce1d190b8..84c043236 100644 --- a/libzeth/mpc/groth16/mpc_utils.hpp +++ b/libzeth/mpc/groth16/mpc_utils.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/mpc/groth16/mpc_utils.tcc b/libzeth/mpc/groth16/mpc_utils.tcc index ea609a294..a51004834 100644 --- a/libzeth/mpc/groth16/mpc_utils.tcc +++ b/libzeth/mpc/groth16/mpc_utils.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/mpc/groth16/phase2.hpp b/libzeth/mpc/groth16/phase2.hpp index 552a4cddd..c89021d1e 100644 --- a/libzeth/mpc/groth16/phase2.hpp +++ b/libzeth/mpc/groth16/phase2.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/mpc/groth16/phase2.tcc b/libzeth/mpc/groth16/phase2.tcc index 7aa8b7ee7..2806e7241 100644 --- a/libzeth/mpc/groth16/phase2.tcc +++ b/libzeth/mpc/groth16/phase2.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/mpc/groth16/powersoftau_utils.cpp b/libzeth/mpc/groth16/powersoftau_utils.cpp index c29ba2915..49c8628f0 100644 --- a/libzeth/mpc/groth16/powersoftau_utils.cpp +++ b/libzeth/mpc/groth16/powersoftau_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/mpc/groth16/powersoftau_utils.hpp b/libzeth/mpc/groth16/powersoftau_utils.hpp index 0415387ea..0b48a3954 100644 --- a/libzeth/mpc/groth16/powersoftau_utils.hpp +++ b/libzeth/mpc/groth16/powersoftau_utils.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/mpc/groth16/powersoftau_utils.tcc b/libzeth/mpc/groth16/powersoftau_utils.tcc index e5b350ac9..945b5e63f 100644 --- a/libzeth/mpc/groth16/powersoftau_utils.tcc +++ b/libzeth/mpc/groth16/powersoftau_utils.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/serialization/filesystem_util.cpp b/libzeth/serialization/filesystem_util.cpp index 387f5fa3f..fd8de20bc 100644 --- a/libzeth/serialization/filesystem_util.cpp +++ b/libzeth/serialization/filesystem_util.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/serialization/filesystem_util.hpp b/libzeth/serialization/filesystem_util.hpp index c9bf1509b..d9e2ab63c 100644 --- a/libzeth/serialization/filesystem_util.hpp +++ b/libzeth/serialization/filesystem_util.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/serialization/proto_utils.cpp b/libzeth/serialization/proto_utils.cpp index 9dfe5dfce..1717d6dd3 100644 --- a/libzeth/serialization/proto_utils.cpp +++ b/libzeth/serialization/proto_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/serialization/proto_utils.hpp b/libzeth/serialization/proto_utils.hpp index 23822fd67..260e7405f 100644 --- a/libzeth/serialization/proto_utils.hpp +++ b/libzeth/serialization/proto_utils.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/serialization/proto_utils.tcc b/libzeth/serialization/proto_utils.tcc index afed1e955..00b7859c9 100644 --- a/libzeth/serialization/proto_utils.tcc +++ b/libzeth/serialization/proto_utils.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/serialization/r1cs_serialization.hpp b/libzeth/serialization/r1cs_serialization.hpp index 56b4949ca..04514eaaa 100644 --- a/libzeth/serialization/r1cs_serialization.hpp +++ b/libzeth/serialization/r1cs_serialization.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -35,9 +35,17 @@ template libsnark::accumulation_vector> accumulation_vector_from_json( const std::string &acc_vector_str); -template +template std::ostream &r1cs_write_json( - const libsnark::protoboard> &pb, std::ostream &out_s); + const libsnark::r1cs_constraint_system &r1cs, std::ostream &out_s); + +template +void r1cs_read_bytes( + libsnark::r1cs_constraint_system &r1cs, std::istream &in_s); + +template +void r1cs_write_bytes( + const libsnark::r1cs_constraint_system &r1cs, std::ostream &out_s); } // namespace libzeth diff --git a/libzeth/serialization/r1cs_serialization.tcc b/libzeth/serialization/r1cs_serialization.tcc index 29998c9fa..54947f57d 100644 --- a/libzeth/serialization/r1cs_serialization.tcc +++ b/libzeth/serialization/r1cs_serialization.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -8,6 +8,7 @@ #include "libzeth/core/field_element_utils.hpp" #include "libzeth/core/group_element_utils.hpp" #include "libzeth/serialization/r1cs_serialization.hpp" +#include "libzeth/serialization/stream_utils.hpp" namespace libzeth { @@ -15,14 +16,14 @@ namespace libzeth namespace internal { -template +template void constraints_write_json( - const libsnark::linear_combination> &constraints, + const libsnark::linear_combination &constraints, std::ostream &out_s) { out_s << "["; size_t count = 0; - for (const libsnark::linear_term> < : constraints.terms) { + for (const libsnark::linear_term < : constraints.terms) { if (count != 0) { out_s << ","; } @@ -30,7 +31,7 @@ void constraints_write_json( out_s << "{"; out_s << "\"index\":" << lt.index << ","; out_s << "\"value\":" - << "\"0x" + bigint_to_hex>(lt.coeff.as_bigint()) + << "\"" + bigint_to_hex(lt.coeff.as_bigint(), true) << "\""; out_s << "}"; count++; @@ -79,9 +80,9 @@ std::string accumulation_vector_to_json( { std::stringstream ss; unsigned vect_length = acc_vector.rest.indices.size() + 1; - ss << "[" << point_affine_to_json(acc_vector.first); + ss << "[" << group_element_to_json(acc_vector.first); for (size_t i = 0; i < vect_length - 1; ++i) { - ss << ", " << point_affine_to_json(acc_vector.rest.values[i]); + ss << ", " << group_element_to_json(acc_vector.rest.values[i]); } ss << "]"; std::string vect_json_str = ss.str(); @@ -121,7 +122,7 @@ libsnark::accumulation_vector> accumulation_vector_from_json( // start_idx end_idx element_str = acc_vector_str.substr(start_idx, end_idx + 2 - start_idx); - libff::G1 front = point_affine_from_json>(element_str); + libff::G1 front = group_element_from_json>(element_str); start_idx = acc_vector_str.find(prefix, end_idx); // Extract remaining elements @@ -133,7 +134,7 @@ libsnark::accumulation_vector> accumulation_vector_from_json( } element_str = acc_vector_str.substr(start_idx, end_idx + 2 - start_idx); - rest.push_back(point_affine_from_json>(element_str)); + rest.push_back(group_element_from_json>(element_str)); start_idx = acc_vector_str.find(prefix, end_idx); } while (start_idx != std::string::npos); @@ -141,30 +142,28 @@ libsnark::accumulation_vector> accumulation_vector_from_json( std::move(front), std::move(rest)); } -template +template std::ostream &r1cs_write_json( - const libsnark::protoboard> &pb, std::ostream &out_s) + const libsnark::r1cs_constraint_system &r1cs, std::ostream &out_s) { // output inputs, right now need to compile with debug flag so that the // `variable_annotations` exists. Having trouble setting that up so will // leave for now. - libsnark::r1cs_constraint_system> constraints = - pb.get_constraint_system(); out_s << "{\n"; out_s << "\"scalar_field_characteristic\":" - << "\"Not yet supported. Should be bigint in hexadecimal\"" - << ",\n"; - out_s << "\"num_variables\":" << pb.num_variables() << ",\n"; - out_s << "\"num_constraints\":" << pb.num_constraints() << ",\n"; - out_s << "\"num_inputs\": " << pb.num_inputs() << ",\n"; + << "\"" + bigint_to_hex(FieldT::field_char(), true) + << "\",\n"; + out_s << "\"num_variables\":" << r1cs.num_variables() << ",\n"; + out_s << "\"num_constraints\":" << r1cs.num_constraints() << ",\n"; + out_s << "\"num_inputs\": " << r1cs.num_inputs() << ",\n"; out_s << "\"variables_annotations\":["; - for (size_t i = 0; i < constraints.num_variables(); ++i) { + for (size_t i = 0; i < r1cs.num_variables(); ++i) { out_s << "{"; out_s << "\"index\":" << i << ","; out_s << "\"annotation\":" - << "\"" << constraints.variable_annotations[i].c_str() << "\""; - if (i == constraints.num_variables() - 1) { + << "\"" << r1cs.variable_annotations.at(i).c_str() << "\""; + if (i == r1cs.num_variables() - 1) { out_s << "}"; } else { out_s << "},"; @@ -172,26 +171,23 @@ std::ostream &r1cs_write_json( } out_s << "],\n"; out_s << "\"constraints\":["; - for (size_t c = 0; c < constraints.num_constraints(); ++c) { + for (size_t c = 0; c < r1cs.num_constraints(); ++c) { out_s << "{"; out_s << "\"constraint_id\": " << c << ","; out_s << "\"constraint_annotation\": " - << "\"" << constraints.constraint_annotations[c].c_str() << "\","; + << "\"" << r1cs.constraint_annotations.at(c).c_str() << "\","; out_s << "\"linear_combination\":"; out_s << "{"; out_s << "\"A\":"; - internal::constraints_write_json( - constraints.constraints[c].a, out_s); + internal::constraints_write_json(r1cs.constraints[c].a, out_s); out_s << ","; out_s << "\"B\":"; - internal::constraints_write_json( - constraints.constraints[c].b, out_s); + internal::constraints_write_json(r1cs.constraints[c].b, out_s); out_s << ","; out_s << "\"C\":"; - internal::constraints_write_json( - constraints.constraints[c].c, out_s); + internal::constraints_write_json(r1cs.constraints[c].c, out_s); out_s << "}"; - if (c == constraints.num_constraints() - 1) { + if (c == r1cs.num_constraints() - 1) { out_s << "}"; } else { out_s << "},"; @@ -202,6 +198,84 @@ std::ostream &r1cs_write_json( return out_s; } +template +void linear_combination_read_bytes( + libsnark::linear_combination &linear_combination, + std::istream &in_s) +{ + const uint32_t num_terms = read_bytes(in_s); + + linear_combination.terms.clear(); + linear_combination.terms.reserve(num_terms); + for (uint32_t i = 0; i < num_terms; ++i) { + const libsnark::var_index_t idx = + read_bytes(in_s); + FieldT coeff; + field_element_read_bytes(coeff, in_s); + linear_combination.terms.emplace_back(idx, coeff); + } +} + +template +void linear_combination_write_bytes( + const libsnark::linear_combination &linear_combination, + std::ostream &out_s) +{ + // Write the number of terms as a uint32_t to save space. If this assert + // fires (a single linear combination contains 2^32 terms), change to + // size_t. + assert( + linear_combination.terms.size() <= + (size_t)std::numeric_limits::max); + const uint32_t num_terms = (uint32_t)linear_combination.terms.size(); + write_bytes(num_terms, out_s); + + for (const libsnark::linear_term &term : linear_combination.terms) { + write_bytes(term.index, out_s); + field_element_write_bytes(term.coeff, out_s); + } +} + +template +void r1cs_constraint_read_bytes( + libsnark::r1cs_constraint &constraint, std::istream &in_s) +{ + linear_combination_read_bytes(constraint.a, in_s); + linear_combination_read_bytes(constraint.b, in_s); + linear_combination_read_bytes(constraint.c, in_s); +} + +template +void r1cs_constraint_write_bytes( + const libsnark::r1cs_constraint &constraint, std::ostream &out_s) +{ + linear_combination_write_bytes(constraint.a, out_s); + linear_combination_write_bytes(constraint.b, out_s); + linear_combination_write_bytes(constraint.c, out_s); +} + +template +void r1cs_read_bytes( + libsnark::r1cs_constraint_system &r1cs, std::istream &in_s) +{ + read_bytes(r1cs.primary_input_size, in_s); + read_bytes(r1cs.auxiliary_input_size, in_s); + collection_read_bytes< + std::vector>, + r1cs_constraint_read_bytes>(r1cs.constraints, in_s); +} + +template +void r1cs_write_bytes( + const libsnark::r1cs_constraint_system &r1cs, std::ostream &out_s) +{ + write_bytes(r1cs.primary_input_size, out_s); + write_bytes(r1cs.auxiliary_input_size, out_s); + collection_write_bytes< + std::vector>, + r1cs_constraint_write_bytes>(r1cs.constraints, out_s); +} + } // namespace libzeth #endif // __ZETH_SERIALIZATION_R1CS_SERIALIZATION_TCC__ diff --git a/libzeth/serialization/r1cs_variable_assignment_serialization.hpp b/libzeth/serialization/r1cs_variable_assignment_serialization.hpp new file mode 100644 index 000000000..f5de76000 --- /dev/null +++ b/libzeth/serialization/r1cs_variable_assignment_serialization.hpp @@ -0,0 +1,32 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +#ifndef __ZETH_SERIALIZATION_R1CS_VARIABLE_ASSIGNMENT_SERIALIZATION_HPP__ +#define __ZETH_SERIALIZATION_R1CS_VARIABLE_ASSIGNMENT_SERIALIZATION_HPP__ + +#include + +namespace libzeth +{ + +template +void r1cs_variable_assignment_read_bytes( + libsnark::r1cs_variable_assignment &assignment, std::istream &in_s); + +template +void r1cs_variable_assignment_write_bytes( + const libsnark::r1cs_variable_assignment &assignment, + std::ostream &out_s); + +template +void r1cs_variable_assignment_write_bytes( + const libsnark::r1cs_primary_input &primary, + const libsnark::r1cs_auxiliary_input &auxiliary, + std::ostream &out_s); + +} // namespace libzeth + +#include "libzeth/serialization/r1cs_variable_assignment_serialization.tcc" + +#endif // __ZETH_SERIALIZATION_R1CS_VARIABLE_ASSIGNMENT_SERIALIZATION_HPP__ diff --git a/libzeth/serialization/r1cs_variable_assignment_serialization.tcc b/libzeth/serialization/r1cs_variable_assignment_serialization.tcc new file mode 100644 index 000000000..de5a85726 --- /dev/null +++ b/libzeth/serialization/r1cs_variable_assignment_serialization.tcc @@ -0,0 +1,54 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +#ifndef __ZETH_SERIALIZATION_R1CS_VARIABLE_ASSIGNMENT_SERIALIZATION_TCC__ +#define __ZETH_SERIALIZATION_R1CS_VARIABLE_ASSIGNMENT_SERIALIZATION_TCC__ + +#include "libzeth/core/field_element_utils.hpp" +#include "libzeth/serialization/r1cs_variable_assignment_serialization.hpp" +#include "libzeth/serialization/stream_utils.hpp" + +namespace libzeth +{ + +template +void r1cs_variable_assignment_read_bytes( + libsnark::r1cs_variable_assignment &assignment, std::istream &in_s) +{ + collection_read_bytes< + libsnark::r1cs_variable_assignment, + field_element_read_bytes>(assignment, in_s); +} + +template +void r1cs_variable_assignment_write_bytes( + const libsnark::r1cs_variable_assignment &assignment, + std::ostream &out_s) +{ + collection_write_bytes< + libsnark::r1cs_variable_assignment, + field_element_write_bytes>(assignment, out_s); +} + +template +void r1cs_variable_assignment_write_bytes( + const libsnark::r1cs_primary_input &primary, + const libsnark::r1cs_auxiliary_input &auxiliary, + std::ostream &out_s) +{ + // Manually write out the aggregation of primary and auxiliary as a single + // collection. + const size_t total_size = primary.size() + auxiliary.size(); + write_bytes(total_size, out_s); + collection_n_write_bytes< + libsnark::r1cs_primary_input, + field_element_write_bytes>(primary, primary.size(), out_s); + collection_n_write_bytes< + libsnark::r1cs_primary_input, + field_element_write_bytes>(auxiliary, auxiliary.size(), out_s); +} + +} // namespace libzeth + +#endif // __ZETH_SERIALIZATION_R1CS_VARIABLE_ASSIGNMENT_SERIALIZATION_TCC__ diff --git a/libzeth/serialization/stream_utils.hpp b/libzeth/serialization/stream_utils.hpp new file mode 100644 index 000000000..f9c00d79e --- /dev/null +++ b/libzeth/serialization/stream_utils.hpp @@ -0,0 +1,103 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +#ifndef __ZETH_SERIALIZATION_STREAM_UTILS_HPP__ +#define __ZETH_SERIALIZATION_STREAM_UTILS_HPP__ + +#include "libzeth/core/include_libsnark.hpp" + +#include +#include + +namespace libzeth +{ + +/// Statically derive the type of the element contained in a (vector-like) +/// collection. +template +using MemberT = + typename std::decay::type; + +/// Read a primitive datatype from a stream as raw bytes. +template +typename std::enable_if::value, T>::type read_bytes( + std::istream &in_s); + +/// Read a primitive datatype from a stream as raw bytes. +template +typename std::enable_if::value, void>::type read_bytes( + T &val, std::istream &in_s); + +// Write a primitive datatype to a stream as raw bytes. +template +typename std::enable_if::value, void>::type write_bytes( + const T &val, std::ostream &out_s); + +/// Write the first n from a collection of values, using a specified writer +/// function. +template< + typename CollectionT, + void(WriterT)(const MemberT &, std::ostream &)> +void collection_n_write_bytes( + const CollectionT &collection, const size_t n, std::ostream &out_s); + +/// Read n element using a specified reader function, appending to the given +/// collection. +template< + typename CollectionT, + void(ReaderT)(MemberT &, std::istream &)> +void collection_n_read_bytes_n( + CollectionT &collection, const size_t n, std::istream &in_s); + +/// Write a full collection of group elements to a stream as bytes, using +/// a specific writer function. +template< + typename CollectionT, + void(WriterT)(const MemberT &, std::ostream &)> +void collection_write_bytes(const CollectionT &collection, std::ostream &out_s); + +/// Read a collection of group elements as bytes, usinng +/// group_elements_read_bytes. +template< + typename CollectionT, + void(ReaderT)(MemberT &, std::istream &)> +void collection_read_bytes(CollectionT &points, std::istream &in_s); + +template +void sparse_vector_read_bytes( + libsnark::sparse_vector &sparse_vector, std::istream &in_s); + +template +void sparse_vector_write_bytes( + const libsnark::sparse_vector &sparse_vector, std::ostream &out_s); + +template +void accumulation_vector_read_bytes( + libsnark::accumulation_vector &acc_vector, std::istream &in_s); + +template +void accumulation_vector_write_bytes( + const libsnark::accumulation_vector &acc_vector, std::ostream &out_s); + +template +void knowledge_commitment_read_bytes( + kcT &knowledge_commitment, std::istream &in_s); + +template +void knowledge_commitment_write_bytes( + const kcT &knowledge_commitment, std::ostream &out_s); + +template +void knowledge_commitment_vector_read_bytes( + kcvectorT &knowledge_commitment, std::istream &in_s); + +template +void knowledge_commitment_vector_write_bytes( + const kcvectorT &knowledge_commitment, std::ostream &out_s); + +} // namespace libzeth + +#include "libzeth/serialization/stream_utils.tcc" + +#endif // __ZETH_SERIALIZATION_STREAM_UTILS_HPP__ diff --git a/libzeth/serialization/stream_utils.tcc b/libzeth/serialization/stream_utils.tcc new file mode 100644 index 000000000..d33c40cfc --- /dev/null +++ b/libzeth/serialization/stream_utils.tcc @@ -0,0 +1,179 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +#ifndef __ZETH_SERIALIZATION_STREAM_UTILS_TCC__ +#define __ZETH_SERIALIZATION_STREAM_UTILS_TCC__ + +#include "libzeth/core/group_element_utils.hpp" +#include "libzeth/serialization/stream_utils.hpp" + +namespace libzeth +{ + +template +typename std::enable_if::value, T>::type read_bytes( + std::istream &in_s) +{ + T val; + read_bytes(val, in_s); + return val; +} + +template +typename std::enable_if::value, void>::type read_bytes( + T &val, std::istream &in_s) +{ + in_s.read((char *)(&val), sizeof(T)); +} + +template +typename std::enable_if::value, void>::type write_bytes( + const T &val, std::ostream &out_s) +{ + out_s.write((const char *)(&val), sizeof(T)); +} + +/// Write the first n from a collection of values, using a specified writer +/// function. +template< + // typename ValueT, + typename CollectionT, + void(WriterFn)(const MemberT &, std::ostream &)> +void collection_n_write_bytes( + const CollectionT &collection, const size_t n, std::ostream &out_s) +{ + for (size_t i = 0; i < n; ++i) { + WriterFn(collection[i], out_s); + } +} + +/// Read n element using a specified reader function, appending to the given +/// collection. +template< + typename CollectionT, + void(ReaderFn)(MemberT &, std::istream &)> +void collection_n_read_bytes( + CollectionT &collection, const size_t n, std::istream &in_s) +{ + for (size_t i = 0; i < n; ++i) { + collection.emplace_back(); + ReaderFn(collection.back(), in_s); + } +} + +/// Write a full collection of values to a stream as bytes, using +/// a specific writer function. +template< + typename CollectionT, + void(WriterT)(const MemberT &, std::ostream &)> +void collection_write_bytes(const CollectionT &collection, std::ostream &out_s) +{ + write_bytes(collection.size(), out_s); + collection_n_write_bytes( + collection, collection.size(), out_s); +} + +/// Read a collection of values, from a stream of bytes, using +/// a specific reader function. +template< + // typename ValueT, + typename CollectionT, + void(ReaderT)(MemberT &, std::istream &)> +void collection_read_bytes(CollectionT &collection, std::istream &in_s) +{ + const size_t n = read_bytes(in_s); + + collection.clear(); + collection.reserve(n); + collection_n_read_bytes(collection, n, in_s); +} + +template +void sparse_vector_read_bytes( + libsnark::sparse_vector &sparse_vector, std::istream &in_s) +{ + sparse_vector.domain_size_ = read_bytes(in_s); + const size_t num_entries = read_bytes(in_s); + sparse_vector.indices.clear(); + sparse_vector.indices.reserve(num_entries); + sparse_vector.values.clear(); + sparse_vector.values.reserve(num_entries); + + for (size_t i = 0; i < num_entries; ++i) { + sparse_vector.indices.push_back(read_bytes(in_s)); + sparse_vector.values.emplace_back(); + ReaderFn(sparse_vector.values.back(), in_s); + } +} + +template +void sparse_vector_write_bytes( + const libsnark::sparse_vector &sparse_vector, std::ostream &out_s) +{ + const size_t num_entries = sparse_vector.indices.size(); + assert(num_entries == sparse_vector.values.size()); + + write_bytes(sparse_vector.domain_size_, out_s); + write_bytes(num_entries, out_s); + for (size_t i = 0; i < num_entries; ++i) { + write_bytes(sparse_vector.indices[i], out_s); + WriterFn(sparse_vector.values[i], out_s); + } +} + +template +void accumulation_vector_read_bytes( + libsnark::accumulation_vector &acc_vector, std::istream &in_s) +{ + ReaderFn(acc_vector.first, in_s); + sparse_vector_read_bytes(acc_vector.rest, in_s); +} + +template +void accumulation_vector_write_bytes( + const libsnark::accumulation_vector &acc_vector, std::ostream &out_s) +{ + WriterFn(acc_vector.first, out_s); + sparse_vector_write_bytes(acc_vector.rest, out_s); +} + +template +void knowledge_commitment_read_bytes( + kcT &knowledge_commitment, std::istream &in_s) +{ + group_element_read_bytes(knowledge_commitment.g, in_s); + group_element_read_bytes(knowledge_commitment.h, in_s); +} + +template +void knowledge_commitment_write_bytes( + const kcT &knowledge_commitment, std::ostream &out_s) +{ + group_element_write_bytes(knowledge_commitment.g, out_s); + group_element_write_bytes(knowledge_commitment.h, out_s); +} + +template +void knowledge_commitment_vector_read_bytes( + kcvectorT &knowledge_commitment_vector, std::istream &in_s) +{ + using kcT = typename std::decay::type; + sparse_vector_read_bytes>( + knowledge_commitment_vector, in_s); +} + +template +void knowledge_commitment_vector_write_bytes( + const kcvectorT &knowledge_commitment_vector, std::ostream &out_s) +{ + using kcT = typename std::decay::type; + sparse_vector_write_bytes>( + knowledge_commitment_vector, out_s); +} + +} // namespace libzeth + +#endif // __ZETH_SERIALIZATION_STREAM_UTILS_TCC__ diff --git a/libzeth/snarks/groth16/groth16_api_handler.hpp b/libzeth/snarks/groth16/groth16_api_handler.hpp index 334b17f35..311b85ada 100644 --- a/libzeth/snarks/groth16/groth16_api_handler.hpp +++ b/libzeth/snarks/groth16/groth16_api_handler.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/snarks/groth16/groth16_api_handler.tcc b/libzeth/snarks/groth16/groth16_api_handler.tcc index 062788f09..80166f8f9 100644 --- a/libzeth/snarks/groth16/groth16_api_handler.tcc +++ b/libzeth/snarks/groth16/groth16_api_handler.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/snarks/groth16/groth16_snark.hpp b/libzeth/snarks/groth16/groth16_snark.hpp index 94b3ac141..09c82bdc6 100644 --- a/libzeth/snarks/groth16/groth16_snark.hpp +++ b/libzeth/snarks/groth16/groth16_snark.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -28,10 +28,16 @@ template class groth16_snark static keypair generate_setup( const libsnark::protoboard> &pb); - /// Generate the proof + /// Generate the proof (from the values set to the protoboard) static proof generate_proof( - const libsnark::protoboard> &pb, - const proving_key &proving_key); + const proving_key &proving_key, + const libsnark::protoboard> &pb); + + /// Generate the proof (from given primary and auxiliary values) + static proof generate_proof( + const proving_key &proving_key, + const libsnark::r1cs_primary_input> &primary_input, + const libsnark::r1cs_auxiliary_input> auxiliary_input); /// Verify proof static bool verify( @@ -40,33 +46,36 @@ template class groth16_snark const verification_key &verification_key); /// Write verification as json - static std::ostream &verification_key_write_json( + static void verification_key_write_json( const verification_key &, std::ostream &); /// Write verification key as bytes - static std::ostream &verification_key_write_bytes( + static void verification_key_write_bytes( const verification_key &, std::ostream &); /// Read a verification key as bytes - static verification_key verification_key_read_bytes(std::istream &); + static void verification_key_read_bytes(verification_key &, std::istream &); /// Write proving key as bytes - static std::ostream &proving_key_write_bytes( - const proving_key &, std::ostream &); + static void proving_key_write_bytes(const proving_key &, std::ostream &); /// Read proving key as bytes - static proving_key proving_key_read_bytes(std::istream &); + static void proving_key_read_bytes(proving_key &, std::istream &); /// Write proof as json. - static std::ostream &proof_write_json( - const proof &proof, std::ostream &out_s); + static void proof_write_json(const proof &, std::ostream &); + + /// Write proof as bytes + static void proof_write_bytes(const proof &, std::ostream &); + + /// Read proof as bytes + static void proof_read_bytes(proof &, std::istream &); /// Write a keypair as bytes - static std::ostream &keypair_write_bytes( - const keypair &keypair, std::ostream &out_s); + static void keypair_write_bytes(const keypair &, std::ostream &); /// Read a keypair from a stream. - static keypair keypair_read_bytes(std::istream &); + static void keypair_read_bytes(keypair &, std::istream &); }; /// Check well-formedness of a proving key diff --git a/libzeth/snarks/groth16/groth16_snark.tcc b/libzeth/snarks/groth16/groth16_snark.tcc index 5d43de065..a58291b84 100644 --- a/libzeth/snarks/groth16/groth16_snark.tcc +++ b/libzeth/snarks/groth16/groth16_snark.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -7,6 +7,7 @@ #include "libzeth/core/group_element_utils.hpp" #include "libzeth/core/utils.hpp" +#include "libzeth/serialization/r1cs_serialization.hpp" #include "libzeth/snarks/groth16/groth16_snark.hpp" namespace libzeth @@ -25,14 +26,19 @@ typename groth16_snark::keypair groth16_snark::generate_setup( template typename groth16_snark::proof groth16_snark::generate_proof( - const libsnark::protoboard> &pb, - const typename groth16_snark::proving_key &proving_key) + const typename groth16_snark::proving_key &proving_key, + const libsnark::protoboard> &pb) { - libsnark::r1cs_primary_input> primary_input = - pb.primary_input(); - libsnark::r1cs_auxiliary_input> auxiliary_input = - pb.auxiliary_input(); + return generate_proof( + proving_key, pb.primary_input(), pb.auxiliary_input()); +} +template +typename groth16_snark::proof groth16_snark::generate_proof( + const proving_key &proving_key, + const libsnark::r1cs_primary_input> &primary_input, + const libsnark::r1cs_auxiliary_input> auxiliary_input) +{ // Generate proof from public input, auxiliary input and proving key. // For now, force a pow2 domain, in case the key came from the MPC. return libsnark::r1cs_gg_ppzksnark_prover( @@ -50,94 +56,138 @@ bool groth16_snark::verify( } template -std::ostream &groth16_snark::verification_key_write_json( +void groth16_snark::verification_key_write_json( const verification_key &vk, std::ostream &out_s) { const size_t abc_length = vk.ABC_g1.rest.indices.size() + 1; out_s << "{" << "\n" - << " \"alpha\": " << point_affine_to_json(vk.alpha_g1) << ",\n" - << " \"beta\": " << point_affine_to_json(vk.beta_g2) << ",\n" - << " \"delta\": " << point_affine_to_json(vk.delta_g2) << ",\n" - << " \"ABC\": [\n " << point_affine_to_json(vk.ABC_g1.first); + << " \"alpha\": " << group_element_to_json(vk.alpha_g1) << ",\n" + << " \"beta\": " << group_element_to_json(vk.beta_g2) << ",\n" + << " \"delta\": " << group_element_to_json(vk.delta_g2) << ",\n" + << " \"ABC\": [\n " << group_element_to_json(vk.ABC_g1.first); for (size_t i = 1; i < abc_length; ++i) { out_s << ",\n " - << point_affine_to_json(vk.ABC_g1.rest.values[i - 1]); + << group_element_to_json(vk.ABC_g1.rest.values[i - 1]); } - return out_s << "\n ]\n}"; + out_s << "\n ]\n}"; } template -std::ostream &groth16_snark::verification_key_write_bytes( +void groth16_snark::verification_key_write_bytes( const verification_key &vk, std::ostream &out_s) { + using G1 = libff::G1; + if (!is_well_formed(vk)) { throw std::invalid_argument("verification key (write) not well-formed"); } - return out_s << vk; + + group_element_write_bytes(vk.alpha_g1, out_s); + group_element_write_bytes(vk.beta_g2, out_s); + group_element_write_bytes(vk.delta_g2, out_s); + accumulation_vector_write_bytes>( + vk.ABC_g1, out_s); } template -std::ostream &groth16_snark::proving_key_write_bytes( +void groth16_snark::proving_key_write_bytes( const proving_key &pk, std::ostream &out_s) { if (!is_well_formed(pk)) { throw std::invalid_argument("proving key (write) not well-formed"); } - return out_s << pk; + + group_element_write_bytes(pk.alpha_g1, out_s); + group_element_write_bytes(pk.beta_g1, out_s); + group_element_write_bytes(pk.beta_g2, out_s); + group_element_write_bytes(pk.delta_g1, out_s); + group_element_write_bytes(pk.delta_g2, out_s); + group_elements_write_bytes(pk.A_query, out_s); + knowledge_commitment_vector_write_bytes(pk.B_query, out_s); + group_elements_write_bytes(pk.H_query, out_s); + group_elements_write_bytes(pk.L_query, out_s); + r1cs_write_bytes(pk.constraint_system, out_s); } template -typename groth16_snark::verification_key groth16_snark< - ppT>::verification_key_read_bytes(std::istream &in_s) +void groth16_snark::verification_key_read_bytes( + groth16_snark::verification_key &vk, std::istream &in_s) { - verification_key vk; - in_s >> vk; + using G1 = libff::G1; + + group_element_read_bytes(vk.alpha_g1, in_s); + group_element_read_bytes(vk.beta_g2, in_s); + group_element_read_bytes(vk.delta_g2, in_s); + accumulation_vector_read_bytes>( + vk.ABC_g1, in_s); + if (!is_well_formed(vk)) { throw std::invalid_argument("verification key (read) not well-formed"); } - return vk; } template -typename groth16_snark::proving_key groth16_snark< - ppT>::proving_key_read_bytes(std::istream &in_s) +void groth16_snark::proving_key_read_bytes( + groth16_snark::proving_key &pk, std::istream &in_s) { - proving_key pk; - in_s >> pk; + group_element_read_bytes(pk.alpha_g1, in_s); + group_element_read_bytes(pk.beta_g1, in_s); + group_element_read_bytes(pk.beta_g2, in_s); + group_element_read_bytes(pk.delta_g1, in_s); + group_element_read_bytes(pk.delta_g2, in_s); + group_elements_read_bytes(pk.A_query, in_s); + knowledge_commitment_vector_read_bytes(pk.B_query, in_s); + group_elements_read_bytes(pk.H_query, in_s); + group_elements_read_bytes(pk.L_query, in_s); + r1cs_read_bytes(pk.constraint_system, in_s); + if (!is_well_formed(pk)) { throw std::invalid_argument("proving key (read) not well-formed"); } - return pk; } template -std::ostream &groth16_snark::keypair_write_bytes( - const typename groth16_snark::keypair &keypair, std::ostream &out_s) +void groth16_snark::proof_write_json( + const typename groth16_snark::proof &proof, std::ostream &out_s) { - proving_key_write_bytes(keypair.pk, out_s); - verification_key_write_bytes(keypair.vk, out_s); - return out_s; + out_s << "{\n \"a\": " << group_element_to_json(proof.g_A) + << ",\n \"b\": " << group_element_to_json(proof.g_B) + << ",\n \"c\": " << group_element_to_json(proof.g_C) << "\n}"; } template -typename groth16_snark::keypair groth16_snark::keypair_read_bytes( - std::istream &in_s) +void groth16_snark::proof_write_bytes( + const typename groth16_snark::proof &proof, std::ostream &out_s) { - proving_key pk = proving_key_read_bytes(in_s); - verification_key vk = verification_key_read_bytes(in_s); - return libsnark::r1cs_gg_ppzksnark_keypair( - std::move(pk), std::move(vk)); + group_element_write_bytes(proof.g_A, out_s); + group_element_write_bytes(proof.g_B, out_s); + group_element_write_bytes(proof.g_C, out_s); } template -std::ostream &groth16_snark::proof_write_json( - const typename groth16_snark::proof &proof, std::ostream &out_s) +void groth16_snark::proof_read_bytes( + typename groth16_snark::proof &proof, std::istream &in_s) +{ + group_element_read_bytes(proof.g_A, in_s); + group_element_read_bytes(proof.g_B, in_s); + group_element_read_bytes(proof.g_C, in_s); +} + +template +void groth16_snark::keypair_write_bytes( + const typename groth16_snark::keypair &keypair, std::ostream &out_s) +{ + proving_key_write_bytes(keypair.pk, out_s); + verification_key_write_bytes(keypair.vk, out_s); +} + +template +void groth16_snark::keypair_read_bytes( + typename groth16_snark::keypair &keypair, std::istream &in_s) { - out_s << "{\n \"a\": " << point_affine_to_json(proof.g_A) - << ",\n \"b\": " << point_affine_to_json(proof.g_B) - << ",\n \"c\": " << point_affine_to_json(proof.g_C) << "\n}"; - return out_s; + proving_key_read_bytes(keypair.pk, in_s); + verification_key_read_bytes(keypair.vk, in_s); } template diff --git a/libzeth/snarks/pghr13/pghr13_api_handler.hpp b/libzeth/snarks/pghr13/pghr13_api_handler.hpp index 728628712..d87182f94 100644 --- a/libzeth/snarks/pghr13/pghr13_api_handler.hpp +++ b/libzeth/snarks/pghr13/pghr13_api_handler.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/snarks/pghr13/pghr13_api_handler.tcc b/libzeth/snarks/pghr13/pghr13_api_handler.tcc index 448c89a50..17190b8a8 100644 --- a/libzeth/snarks/pghr13/pghr13_api_handler.tcc +++ b/libzeth/snarks/pghr13/pghr13_api_handler.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/snarks/pghr13/pghr13_snark.hpp b/libzeth/snarks/pghr13/pghr13_snark.hpp index 9dde87a32..d42570f9b 100644 --- a/libzeth/snarks/pghr13/pghr13_snark.hpp +++ b/libzeth/snarks/pghr13/pghr13_snark.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -28,10 +28,16 @@ template class pghr13_snark static keypair generate_setup( const libsnark::protoboard> &pb); - /// Generate the proof + /// Generate the proof (from the values set to the protoboard) static proof generate_proof( - const libsnark::protoboard> &pb, - const proving_key &proving_key); + const proving_key &proving_key, + const libsnark::protoboard> &pb); + + /// Generate the proof (from given primary and auxiliary values) + static proof generate_proof( + const proving_key &proving_key, + const libsnark::r1cs_primary_input> &primary_input, + const libsnark::r1cs_auxiliary_input> auxiliary_input); /// Verify proof static bool verify( @@ -40,31 +46,36 @@ template class pghr13_snark const verification_key &verification_key); /// Write verification as json - static std::ostream &verification_key_write_json( + static void verification_key_write_json( const verification_key &, std::ostream &); /// Write verification key as bytes - static std::ostream &verification_key_write_bytes( + static void verification_key_write_bytes( const verification_key &, std::ostream &); /// Read a verification key as bytes - static verification_key verification_key_read_bytes(std::istream &); + static void verification_key_read_bytes(verification_key &, std::istream &); /// Write proving key as bytes - static std::ostream &proving_key_write_bytes( - const proving_key &, std::ostream &); + static void proving_key_write_bytes(const proving_key &, std::ostream &); /// Read proving key as bytes - static proving_key proving_key_read_bytes(std::istream &); + static void proving_key_read_bytes(proving_key &, std::istream &); + + /// Write proof as json. + static void proof_write_json(const proof &, std::ostream &); + + /// Write proof as bytes + static void proof_write_bytes(const proof &, std::ostream &); - /// Write proof as json - static std::ostream &proof_write_json(const proof &, std::ostream &); + /// Read proof as bytes + static void proof_read_bytes(proof &, std::istream &); - /// Write a keypair to a stream. - static std::ostream &keypair_write_bytes(const keypair &, std::ostream &); + /// Write a keypair as bytes + static void keypair_write_bytes(const keypair &, std::ostream &); /// Read a keypair from a stream. - static keypair keypair_read_bytes(std::istream &); + static void keypair_read_bytes(keypair &, std::istream &); }; } // namespace libzeth diff --git a/libzeth/snarks/pghr13/pghr13_snark.tcc b/libzeth/snarks/pghr13/pghr13_snark.tcc index be5977428..a6a45d10f 100644 --- a/libzeth/snarks/pghr13/pghr13_snark.tcc +++ b/libzeth/snarks/pghr13/pghr13_snark.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -23,17 +23,19 @@ typename pghr13_snark::keypair pghr13_snark::generate_setup( template typename pghr13_snark::proof pghr13_snark::generate_proof( - const libsnark::protoboard> &pb, - const pghr13_snark::proving_key &proving_key) + const pghr13_snark::proving_key &proving_key, + const libsnark::protoboard> &pb) { - // See: - // https://github.com/scipr-lab/libsnark/blob/92a80f74727091fdc40e6021dc42e9f6b67d5176/libsnark/relations/constraint_satisfaction_problems/r1cs/r1cs.hpp#L81 - // For the definition of r1cs_primary_input and r1cs_auxiliary_input - libsnark::r1cs_primary_input> primary_input = - pb.primary_input(); - libsnark::r1cs_auxiliary_input> auxiliary_input = - pb.auxiliary_input(); + return generate_proof( + proving_key, pb.primary_input(), pb.auxiliary_input()); +} +template +typename pghr13_snark::proof pghr13_snark::generate_proof( + const pghr13_snark::proving_key &proving_key, + const libsnark::r1cs_primary_input> &primary_input, + const libsnark::r1cs_auxiliary_input> auxiliary_input) +{ // Generate proof from public input, auxiliary input (private/secret data), // and proving key return libsnark::r1cs_ppzksnark_prover( @@ -51,95 +53,111 @@ bool pghr13_snark::verify( } template -std::ostream &pghr13_snark::verification_key_write_json( +void pghr13_snark::verification_key_write_json( const pghr13_snark::verification_key &vk, std::ostream &os) { unsigned ic_length = vk.encoded_IC_query.rest.indices.size() + 1; os << "{\n"; - os << " \"a\": " << point_affine_to_json(vk.alphaA_g2) << ",\n"; - os << " \"b\": " << point_affine_to_json(vk.alphaB_g1) << ",\n"; - os << " \"c\": " << point_affine_to_json(vk.alphaC_g2) << ",\n"; - os << " \"g\": " << point_affine_to_json(vk.gamma_g2) << ",\n"; - os << " \"gb1\": " << point_affine_to_json(vk.gamma_beta_g1) << ",\n"; - os << " \"gb2\": " << point_affine_to_json(vk.gamma_beta_g2) << ",\n"; - os << " \"z\": " << point_affine_to_json(vk.rC_Z_g2) << ",\n"; + os << " \"a\": " << group_element_to_json(vk.alphaA_g2) << ",\n"; + os << " \"b\": " << group_element_to_json(vk.alphaB_g1) << ",\n"; + os << " \"c\": " << group_element_to_json(vk.alphaC_g2) << ",\n"; + os << " \"g\": " << group_element_to_json(vk.gamma_g2) << ",\n"; + os << " \"gb1\": " << group_element_to_json(vk.gamma_beta_g1) << ",\n"; + os << " \"gb2\": " << group_element_to_json(vk.gamma_beta_g2) << ",\n"; + os << " \"z\": " << group_element_to_json(vk.rC_Z_g2) << ",\n"; - os << "\"IC\" :[" << point_affine_to_json(vk.encoded_IC_query.first); + os << "\"IC\" :[" << group_element_to_json(vk.encoded_IC_query.first); for (size_t i = 1; i < ic_length; ++i) { os << "," - << point_affine_to_json(vk.encoded_IC_query.rest.values[i - 1]); + << group_element_to_json(vk.encoded_IC_query.rest.values[i - 1]); } os << "]\n"; os << "}"; - return os; } template -std::ostream &pghr13_snark::verification_key_write_bytes( +void pghr13_snark::verification_key_write_bytes( const typename pghr13_snark::verification_key &vk, std::ostream &os) { - return os << vk; + os << vk; } template -typename pghr13_snark::verification_key pghr13_snark< - ppT>::verification_key_read_bytes(std::istream &in_s) +void pghr13_snark::verification_key_read_bytes( + typename pghr13_snark::verification_key &vk, std::istream &in_s) { - verification_key vk; in_s >> vk; - return vk; } template -std::ostream &pghr13_snark::proving_key_write_bytes( +void pghr13_snark::proving_key_write_bytes( const typename pghr13_snark::proving_key &pk, std::ostream &os) { - return os << pk; + os << pk; } template -typename pghr13_snark::proving_key pghr13_snark< - ppT>::proving_key_read_bytes(std::istream &in_s) +void pghr13_snark::proving_key_read_bytes( + typename pghr13_snark::proving_key &pk, std::istream &in_s) { - proving_key pk; in_s >> pk; - return pk; } template -std::ostream &pghr13_snark::proof_write_json( +void pghr13_snark::proof_write_json( const typename pghr13_snark::proof &proof, std::ostream &os) { os << "{\n"; - os << " \"a\": " << point_affine_to_json(proof.g_A.g) << ",\n"; - os << " \"a_p\": " << point_affine_to_json(proof.g_A.h) << ",\n"; - os << " \"b\": " << point_affine_to_json(proof.g_B.g) << ",\n"; - os << " \"b_p\": " << point_affine_to_json(proof.g_B.h) << ",\n"; - os << " \"c\": " << point_affine_to_json(proof.g_C.g) << ",\n"; - os << " \"c_p\": " << point_affine_to_json(proof.g_C.h) << ",\n"; - os << " \"h\": " << point_affine_to_json(proof.g_H) << ",\n"; - os << " \"k\": " << point_affine_to_json(proof.g_K) << "\n"; + os << " \"a\": " << group_element_to_json(proof.g_A.g) << ",\n"; + os << " \"a_p\": " << group_element_to_json(proof.g_A.h) << ",\n"; + os << " \"b\": " << group_element_to_json(proof.g_B.g) << ",\n"; + os << " \"b_p\": " << group_element_to_json(proof.g_B.h) << ",\n"; + os << " \"c\": " << group_element_to_json(proof.g_C.g) << ",\n"; + os << " \"c_p\": " << group_element_to_json(proof.g_C.h) << ",\n"; + os << " \"h\": " << group_element_to_json(proof.g_H) << ",\n"; + os << " \"k\": " << group_element_to_json(proof.g_K) << "\n"; os << "}"; - return os; } template -std::ostream &pghr13_snark::keypair_write_bytes( +void pghr13_snark::proof_write_bytes( + const typename pghr13_snark::proof &proof, std::ostream &out_s) +{ + knowledge_commitment_write_bytes(proof.g_A, out_s); + knowledge_commitment_write_bytes(proof.g_B, out_s); + knowledge_commitment_write_bytes(proof.g_C, out_s); + group_element_write_bytes(proof.g_H, out_s); + group_element_write_bytes(proof.g_K, out_s); +} + +template +void pghr13_snark::proof_read_bytes( + typename pghr13_snark::proof &proof, std::istream &in_s) +{ + knowledge_commitment_read_bytes(proof.g_A, in_s); + knowledge_commitment_read_bytes(proof.g_B, in_s); + knowledge_commitment_read_bytes(proof.g_C, in_s); + group_element_read_bytes(proof.g_H, in_s); + group_element_read_bytes(proof.g_K, in_s); +} + +template +void pghr13_snark::keypair_write_bytes( const typename pghr13_snark::keypair &keypair, std::ostream &os) { proving_key_write_bytes(keypair.pk, os); - return verification_key_write_bytes(keypair.vk, os); + verification_key_write_bytes(keypair.vk, os); } template -typename pghr13_snark::keypair pghr13_snark::keypair_read_bytes( - std::istream &in_s) +void pghr13_snark::keypair_read_bytes( + typename pghr13_snark::keypair &keypair, std::istream &in_s) { - return keypair( - proving_key_read_bytes(in_s), verification_key_read_bytes(in_s)); + proving_key_read_bytes(keypair.pk, in_s); + verification_key_read_bytes(keypair.vk, in_s); } } // namespace libzeth diff --git a/libzeth/tests/circuits/binary_operation_test.cpp b/libzeth/tests/circuits/binary_operation_test.cpp index 23c463c13..5a6911b27 100644 --- a/libzeth/tests/circuits/binary_operation_test.cpp +++ b/libzeth/tests/circuits/binary_operation_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/tests/circuits/blake2s_test.cpp b/libzeth/tests/circuits/blake2s_test.cpp index cdb756ba7..f96b561de 100644 --- a/libzeth/tests/circuits/blake2s_test.cpp +++ b/libzeth/tests/circuits/blake2s_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/tests/circuits/commitments_test.cpp b/libzeth/tests/circuits/commitments_test.cpp index 28f113989..49e2d33f4 100644 --- a/libzeth/tests/circuits/commitments_test.cpp +++ b/libzeth/tests/circuits/commitments_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/tests/circuits/merkle_tree_test.cpp b/libzeth/tests/circuits/merkle_tree_test.cpp index 728582315..0b1bc15b3 100644 --- a/libzeth/tests/circuits/merkle_tree_test.cpp +++ b/libzeth/tests/circuits/merkle_tree_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -12,7 +12,7 @@ using namespace libzeth; template -using MiMCe7_permutation_gadget = MiMC_permutation_gadget; +using MiMCe7_permutation_gadget = MiMC_permutation_gadget; // Instantiation of the templates for the tests. Data here assumes alt_bn128 using pp = libff::alt_bn128_pp; @@ -84,13 +84,12 @@ bool test_merkle_path_authenticator_depth1() // right leaf: // 134551314051432487569247388144051420116740427803855572138106146683954151557, // root: - // 7121700468981037559893852455893095765125417767594185027454590493596569372187 + // hash(left, right) Field left = Field("37031414935355631796575317199601601742960852086719193" "16200479060314459804651"); Field right = Field("1345513140514324875692473881440514201167404278038555" "72138106146683954151557"); - Field root = Field("71217004689810375598938524558930957651254177675941850" - "27454590493596569372187"); + Field root = HashTree::get_hash(left, right); // Set the authenticator for right leaf (`is_right` = 1) Field is_right = 1; @@ -167,8 +166,8 @@ bool test_merkle_path_authenticator_depth3() "3374630310875272509334396"); Field left2 = Field("9881790034808292405036271961589462686158587796044671" "417688221824074647491645"); - Field root = Field("13476730430097836153970274382710787532919044453117948" - "373701924629587143655224"); + Field root = HashTree::get_hash( + left2, HashTree::get_hash(left1, HashTree::get_hash(left0, right0))); Field is_right = 1; // Bit representation of the leaf to authenticate @@ -227,33 +226,24 @@ bool test_merkle_path_authenticator_depth3() return true; } -TEST(MainTests, TestMerkleTreeField) +TEST(MerkleTreeTest, PathSelector0) { - bool res = false; - - res = test_merkle_path_selector(0); - std::cout - << "[test_merkle_path_selector 0] Expected (True), Obtained result: " - << res << std::endl; - ASSERT_TRUE(res); - - res = test_merkle_path_selector(1); - std::cout - << "[test_merkle_path_selector 1] Expected (True), Obtained result: " - << res << std::endl; - ASSERT_TRUE(res); - - res = test_merkle_path_authenticator_depth1(); - std::cout << "[test_merkle_path_authenticator_depth1] Expected (True), " - "Obtained result: " - << res << std::endl; - ASSERT_TRUE(res); - - res = test_merkle_path_authenticator_depth3(); - std::cout << "[test_merkle_path_authenticator_depth3] Expected (True), " - "Obtained result: " - << res << std::endl; - ASSERT_TRUE(res); + ASSERT_TRUE(test_merkle_path_selector(0)); +} + +TEST(MerkleTreeTest, PathSelector1) +{ + ASSERT_TRUE(test_merkle_path_selector(1)); +} + +TEST(MerkleTreeTest, PathAuthenticatorDepth1) +{ + ASSERT_TRUE(test_merkle_path_authenticator_depth1()); +} + +TEST(MerkleTreeTest, PathAuthenticatorDepth3) +{ + ASSERT_TRUE(test_merkle_path_authenticator_depth3()); } } // namespace diff --git a/libzeth/tests/circuits/mimc_input_hasher_test.cpp b/libzeth/tests/circuits/mimc_input_hasher_test.cpp new file mode 100644 index 000000000..0f5669910 --- /dev/null +++ b/libzeth/tests/circuits/mimc_input_hasher_test.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +#include "libzeth/circuits/circuit_types.hpp" +#include "libzeth/circuits/mimc/mimc_input_hasher.hpp" +#include "libzeth/circuits/mimc/mimc_mp.hpp" + +#include +#include + +namespace +{ + +using pp = libff::bls12_377_pp; +using Field = libff::Fr; +using comp_fn = libzeth::tree_hash_selector::tree_hash; +using input_hasher = libzeth::mimc_input_hasher; + +TEST(MiMCInputHasherTest, SimpleInputValues) +{ + // Test data generated by test_input_hasher_mimc_bls12_377: + // $ python + // >>> from zeth.core.mimc import MiMCBLS12_377 + // >>> from zeth.core.input_hasher import InputHasher + // >>> InputHasher(MiMCBLS12_377()).hash([0,1,-1,2,-2]) + const std::vector simple_values{{ + Field::zero(), + Field::one(), + -Field::one(), + Field("2"), + -Field("2"), + }}; + const Field expect_hash("24413724837428607704066638210762875107426186849414" + "55909840195654797701248623"); + + libsnark::protoboard pb; + + // Public input: hash of multiple values + libsnark::pb_variable hashed_inputs; + hashed_inputs.allocate(pb, "hashed_inputs"); + pb.set_input_sizes(1); + + // Values to hash + libsnark::pb_variable_array orig_inputs; + orig_inputs.allocate(pb, simple_values.size(), "orig_inputs"); + + // Input hasher + input_hasher hasher(pb, orig_inputs, hashed_inputs, "hasher"); + + // Constraints + hasher.generate_r1cs_constraints(); + + // Witness + for (size_t i = 0; i < simple_values.size(); ++i) { + pb.val(orig_inputs[i]) = simple_values[i]; + } + hasher.generate_r1cs_witness(); + + ASSERT_EQ(expect_hash, pb.val(hashed_inputs)); + ASSERT_EQ(expect_hash, input_hasher::compute_hash(simple_values)); + ASSERT_TRUE(pb.is_satisfied()); +} + +} // namespace + +int main(int argc, char **argv) +{ + pp::init_public_params(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/libzeth/tests/circuits/mimc_mp_test.cpp b/libzeth/tests/circuits/mimc_mp_test.cpp index 7563b9553..6c692d974 100644 --- a/libzeth/tests/circuits/mimc_mp_test.cpp +++ b/libzeth/tests/circuits/mimc_mp_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -11,49 +11,54 @@ using namespace libzeth; template -using MiMCe7_round_gadget = MiMC_round_gadget; +using MiMCe17_round_gadget = MiMC_round_gadget; template -using MiMCe31_round_gadget = MiMC_round_gadget; +using MiMCe17_ALT_BN128_permutation_gadget = + MiMC_permutation_gadget; template -using MiMCe7_permutation_gadget = MiMC_permutation_gadget; -template -using MiMCe31_permutation_gadget = MiMC_permutation_gadget; - -// Test data here specialized for alt_bn128 -using pp = libff::alt_bn128_pp; -using Field = libff::Fr; +using MiMCe17_BLS12_377_permutation_gadget = + MiMC_permutation_gadget; namespace { -// Testing that (15212 + 98645 + 216319)**7 = -// 427778066313557225181231220812180094976 -TEST(TestMiMC, MiMC7RoundTrueNoAddKToResult) +TEST(TestMiMC, MiMC17_ALT_BN128_Round) { + using Field = libff::alt_bn128_Fr; + libsnark::protoboard pb; libsnark::pb_variable in_x; libsnark::pb_variable in_k; libsnark::pb_variable result; - Field in_C = Field("216319"); in_x.allocate(pb, "x"); in_k.allocate(pb, "k"); - pb.val(in_x) = Field("15212"); - pb.val(in_k) = Field("98645"); - result.allocate(pb, "result"); - MiMCe7_round_gadget round_gadget( - pb, in_x, in_k, in_C, result, false, "round_gadget"); + + // Test data from client test (test_mimc.py), to ensure consistency between + // implementations. + + pb.val(in_x) = Field("340282366920938463463374607431768211456"); + pb.val(in_k) = Field("28948022309329048855892746252171976963317496166410141" + "009864396001978282409983"); + Field in_C = Field("1422006791884799603110814443576367281105075806594536430" + "8986253046354060608451"); + + MiMCe17_round_gadget round_gadget( + pb, in_x, in_k, in_C, result, "round_gadget"); round_gadget.generate_r1cs_constraints(); round_gadget.generate_r1cs_witness(); - Field expected_out = Field("427778066313557225181231220812180094976"); + Field expected_out = Field("15194574649778181158537940501307832704788048781" + "286507777438072456493095881604"); ASSERT_TRUE(pb.is_satisfied()); ASSERT_TRUE(expected_out == pb.val(result)); } -TEST(TestMiMC, MiMC7RoundFalseNoAddKToResult) +TEST(TestMiMC, MiMC17_ALT_BN128_Round_Incorrect) { + using Field = libff::alt_bn128_Fr; + libsnark::protoboard pb; libsnark::pb_variable in_x; libsnark::pb_variable in_k; @@ -66,25 +71,32 @@ TEST(TestMiMC, MiMC7RoundFalseNoAddKToResult) pb.val(in_k) = Field("98645"); result.allocate(pb, "result"); - MiMCe7_round_gadget round_gadget( - pb, in_x, in_k, in_C, result, false, "round_gadget"); + MiMCe17_round_gadget round_gadget( + pb, in_x, in_k, in_C, result, "round_gadget"); round_gadget.generate_r1cs_constraints(); round_gadget.generate_r1cs_witness(); - Field unexpected_out = Field("427778066313557225181231220812180094976"); - ASSERT_TRUE(pb.is_satisfied()); - ASSERT_FALSE(unexpected_out == pb.val(result)); + // Force incorrect result value and check that circuit is not satisfied. + pb.val(result) = Field("427778066313557225181231220812180094976"); + ASSERT_FALSE(pb.is_satisfied()); } -// Testing that (15212 + 98645 + 216319)**7 + 98645 = -// 427778066313557225181231220812180193621 -TEST(TestMiMC, MiMC7RoundTrueAddKToResult) +TEST(TestMiMC, MiMC17_ALT_BN128_Round_AddToResult) { + using Field = libff::alt_bn128_Fr; + libsnark::protoboard pb; libsnark::pb_variable in_x; libsnark::pb_variable in_k; libsnark::pb_variable result; + // Test data generated by: + // $ python + // >>> r = + // 21888242871839275222246405745257275088548364400416034343698204186575808495617 + // >>> ((15212 + 98645 + 216319)**17) % r + 98645 + // 15395941923199146769662515429975727210847361451765447661089541693078496064650 + Field in_C = Field("216319"); in_x.allocate(pb, "x"); in_k.allocate(pb, "k"); @@ -92,18 +104,21 @@ TEST(TestMiMC, MiMC7RoundTrueAddKToResult) pb.val(in_k) = Field("98645"); result.allocate(pb, "result"); - MiMCe7_round_gadget round_gadget( - pb, in_x, in_k, in_C, result, true, "round_gadget"); + MiMCe17_round_gadget round_gadget( + pb, in_x, in_k, in_C, result, in_k, "round_gadget"); round_gadget.generate_r1cs_constraints(); round_gadget.generate_r1cs_witness(); - Field expected_out = Field("427778066313557225181231220812180193621"); + Field expected_out = Field("15395941923199146769662515429975727210847361451" + "765447661089541693078496064650"); ASSERT_TRUE(pb.is_satisfied()); ASSERT_TRUE(expected_out == pb.val(result)); } -TEST(TestMiMC, MiMC7RoundFalseAddKToResult) +TEST(TestMiMC, MiMC17_ALT_BN128_Round_AddToResult_Incorrect) { + using Field = libff::alt_bn128_Fr; + libsnark::protoboard pb; libsnark::pb_variable in_x; libsnark::pb_variable in_k; @@ -116,127 +131,199 @@ TEST(TestMiMC, MiMC7RoundFalseAddKToResult) pb.val(in_k) = Field("98645"); result.allocate(pb, "result"); - MiMCe7_round_gadget round_gadget( - pb, in_x, in_k, in_C, result, true, "round_gadget"); + MiMCe17_round_gadget round_gadget( + pb, in_x, in_k, in_C, result, in_k, "round_gadget"); round_gadget.generate_r1cs_constraints(); round_gadget.generate_r1cs_witness(); - Field unexpected_out = Field("427778066313557225181231220812180193621"); - ASSERT_TRUE(pb.is_satisfied()); - ASSERT_FALSE(unexpected_out == pb.val(result)); + // Force incorrect result value and check that circuit is not satisfied. + pb.val(result) = Field("427778066313557225181231220812180193621"); + ASSERT_FALSE(pb.is_satisfied()); } -TEST(TestMiMC, MiMC7PermTrue) +TEST(TestMiMC, MiMC17_ALT_BN128_Permutation) { + using Field = libff::alt_bn128_Fr; + libsnark::protoboard pb; libsnark::pb_variable in_x; libsnark::pb_variable in_k; + libsnark::pb_variable result; in_x.allocate(pb, "x"); in_k.allocate(pb, "k"); + result.allocate(pb, "result"); + + // Test data generated: + // $ python + // >>> import zeth.core.mimc + // >>> MiMCAltBN128().encrypt( + // 3703141493535563179657531719960160174296085208671919316200479060314459804651, + // 15683951496311901749339509118960676303290224812129752890706581988986633412003) + // 2950769369933203897475350343792407636840104781947445379481145971915829708697 pb.val(in_x) = Field("3703141493535563179657531719960160174296085208671919" "316200479060314459804651"); pb.val(in_k) = Field("1568395149631190174933950911896067630329022481212975" "2890706581988986633412003"); - MiMC_permutation_gadget mimc_gadget( - pb, in_x, in_k, "mimc_gadget"); - mimc_gadget.generate_r1cs_constraints(); - mimc_gadget.generate_r1cs_witness(); + MiMCe17_ALT_BN128_permutation_gadget mimc_perm_gadget( + pb, in_x, in_k, result, "mimc_gadget"); + mimc_perm_gadget.generate_r1cs_constraints(); + mimc_perm_gadget.generate_r1cs_witness(); - Field expected_out = Field("192990723315478049773124691205698348115617480" - "95378968014959488920239255590840"); + Field expected_out = Field("29507693699332038974753503437924076368401047819" + "47445379481145971915829708697"); ASSERT_TRUE(pb.is_satisfied()); - ASSERT_TRUE(expected_out == pb.val(mimc_gadget.result())); + ASSERT_TRUE(expected_out == pb.val(result)); } -TEST(TestMiMC, MiMC7PermFalse) +TEST(TestMiMC, MiMC17_ALT_BN128_Permutation_Incorrect) { + using Field = libff::alt_bn128_Fr; + libsnark::protoboard pb; libsnark::pb_variable in_x; libsnark::pb_variable in_k; + libsnark::pb_variable result; in_x.allocate(pb, "x"); in_k.allocate(pb, "k"); + result.allocate(pb, "result"); pb.val(in_x) = Field("3703141493535563179657531719960160174296085208671919" "316200479060314459804651"); pb.val(in_k) = Field("13455131405143248756924738814405142"); - MiMCe7_permutation_gadget mimc_gadget(pb, in_x, in_k, "mimc_gadget"); + MiMCe17_ALT_BN128_permutation_gadget mimc_gadget( + pb, in_x, in_k, result, "mimc_gadget"); mimc_gadget.generate_r1cs_constraints(); mimc_gadget.generate_r1cs_witness(); - Field unexpected_out = Field("1929907233154780497731246912056983481156174" - "8095378968014959488920239255590840"); - ASSERT_TRUE(pb.is_satisfied()); - ASSERT_FALSE(unexpected_out == pb.val(mimc_gadget.result())); + // Force invalid result + pb.val(result) = Field("1929907233154780497731246912056983481156174" + "8095378968014959488920239255590840"); + ASSERT_FALSE(pb.is_satisfied()); } -TEST(TestMiMC, MiMC7MpTrue) +TEST(TestMiMC, MiMC17_ALT_BN128_MP) { + using Field = libff::alt_bn128_Fr; + + // Test data from client test (test_mimc.py), to ensure consistency between + // implementations. + + const Field m_val("340282366920938463463374607431768211456"); + const Field k_val("28948022309329048855892746252171976963317496166410141009" + "864396001978282409983"); + const Field h_val("14599678357063082723814206975733222579132256174923645170" + "354481857040188426666"); + libsnark::protoboard pb; // Public input - libsnark::pb_variable y; - y.allocate(pb, "y"); + libsnark::pb_variable k; + k.allocate(pb, "k"); pb.set_input_sizes(1); - - // y = sha3_256("mimc") - pb.val(y) = Field("1568395149631190174933950911896067630329022481212975289" - "0706581988986633412003"); + pb.val(k) = k_val; // Private inputs - libsnark::pb_variable x; - x.allocate(pb, "x"); - pb.val(x) = Field("3703141493535563179657531719960160174296085208671919316" - "200479060314459804651"); + libsnark::pb_variable m; + m.allocate(pb, "m"); + pb.val(m) = m_val; + + libsnark::pb_variable h; + h.allocate(pb, "h"); - MiMC_mp_gadget> mimc_mp_gadget( - pb, x, y, "gadget"); + MiMC_mp_gadget> + mimc_mp_gadget(pb, m, k, h, "mimc_mp"); mimc_mp_gadget.generate_r1cs_constraints(); mimc_mp_gadget.generate_r1cs_witness(); - Field expected_out = Field("167979224495559946840631042142333962005996937" - "15764605878168345782964540311877"); + // Check that the circuit is satisfied, and that the expected result is + // generated. ASSERT_TRUE(pb.is_satisfied()); - ASSERT_TRUE(expected_out == pb.val(mimc_mp_gadget.result())); + ASSERT_EQ(h_val, pb.val(h)); } -TEST(TestMiMC, MiMC7MpFalse) +TEST(TestMiMC, MiMC17_ALT_BN128_MP_Incorrect) { + using Field = libff::alt_bn128_Fr; + + // Test data from client test (test_mimc.py), to ensure consistency between + // implementations. + + const Field m_val("340282366920938463463374607431768211456"); + const Field k_val("28948022309329048855892746252171976963317496166410141009" + "864396001978282409983"); + libsnark::protoboard pb; // Public input - libsnark::pb_variable y; - y.allocate(pb, "y"); + libsnark::pb_variable k; + k.allocate(pb, "k"); pb.set_input_sizes(1); - pb.val(y) = Field("8272473133185905403731511349671041314111289765433456653" - "2528783843265082629790"); + pb.val(k) = k_val; // Private inputs - libsnark::pb_variable x; - x.allocate(pb, "x"); - pb.val(x) = Field("3703141493535563179657531719960160174296085208671919316" - "200479060314459804651"); + libsnark::pb_variable m; + m.allocate(pb, "m"); + pb.val(m) = m_val; + + libsnark::pb_variable h; + h.allocate(pb, "h"); - MiMC_mp_gadget> mimc_mp_gadget( - pb, x, y, "gadget"); + MiMC_mp_gadget> + mimc_mp_gadget(pb, m, k, h, "mimc_mp"); mimc_mp_gadget.generate_r1cs_constraints(); mimc_mp_gadget.generate_r1cs_witness(); - Field unexpected_out = Field("1679792244955599468406310421423339620059969" - "3715764605878168345782964540311877"); + // Force incorrect result value and check that circuit is not satisfied. + pb.val(h) = Field("145996783570630827238142069757332225791322561749236" + "45170354481857040188426667"); + ASSERT_FALSE(pb.is_satisfied()); +} + +TEST(TestMiMC, MiMC17_BLS12_377_Round) +{ + using Field = libff::bls12_377_Fr; + + libsnark::protoboard pb; + libsnark::pb_variable in_x; + libsnark::pb_variable in_k; + libsnark::pb_variable result; + + in_x.allocate(pb, "x"); + in_k.allocate(pb, "k"); + result.allocate(pb, "result"); + + // Test data from client test (test_mimc.py), to ensure consistency between + // implementations. + + pb.val(in_x) = Field("340282366920938463463374607431768211456"); + pb.val(in_k) = Field("36146370610439375831462714358273373691897981609479495" + "26058695634226054692860"); + Field in_C = Field("5775606169419625606859319496982126279674858730791300481" + "051019590436651369410"); + + MiMCe17_round_gadget round_gadget( + pb, in_x, in_k, in_C, result, "round_gadget"); + round_gadget.generate_r1cs_constraints(); + round_gadget.generate_r1cs_witness(); + + Field expected_out = Field("70652923384013840748711649474482841764205668417" + "1152884149736992660816802274"); ASSERT_TRUE(pb.is_satisfied()); - ASSERT_FALSE(unexpected_out == pb.val(mimc_mp_gadget.result())); + ASSERT_EQ(expected_out, pb.val(result)); } -TEST(TestMiMC, TestMiMC31) +TEST(TestMiMC, MiMC17_BLS12_377_MP) { using Field = libff::bls12_377_Fr; - // Test data from client test + // Test data from client test (test_mimc.py), to ensure consistency between + // implementations. + const Field m_val( "361463706104393758314627143582733736918979816094794952605869" "5634226054692860"); @@ -244,8 +331,8 @@ TEST(TestMiMC, TestMiMC31) "577560616941962560685931949698212627967485873079130048105101" "9590436651369410"); const Field h_val( - "757520454940410747883073955769867933053765668805066446289274" - "1835534561279075"); + "580310635483157120553405751259383795319189070903739068091192" + "5249983717812220"); libsnark::protoboard pb; @@ -260,15 +347,18 @@ TEST(TestMiMC, TestMiMC31) m.allocate(pb, "m"); pb.val(m) = m_val; - MiMC_mp_gadget> mimc_mp_gadget( - pb, m, k, "mimc_mp"); + libsnark::pb_variable h; + h.allocate(pb, "h"); + + MiMC_mp_gadget> + mimc_mp_gadget(pb, m, k, h, "mimc_mp"); mimc_mp_gadget.generate_r1cs_constraints(); mimc_mp_gadget.generate_r1cs_witness(); // Check that the circuit is satisfied, and that the expected result is // generated. ASSERT_TRUE(pb.is_satisfied()); - ASSERT_EQ(h_val, pb.val(mimc_mp_gadget.result())); + ASSERT_EQ(h_val, pb.val(h)); } } // namespace @@ -277,7 +367,7 @@ int main(int argc, char **argv) { // /!\ WARNING: Do once for all tests. Do not // forget to do this !!!! - pp::init_public_params(); + libff::alt_bn128_pp::init_public_params(); libff::bls12_377_pp::init_public_params(); ::testing::InitGoogleTest(&argc, argv); diff --git a/libzeth/tests/circuits/note_test.cpp b/libzeth/tests/circuits/note_test.cpp index be8d6bf21..8bf3315ed 100644 --- a/libzeth/tests/circuits/note_test.cpp +++ b/libzeth/tests/circuits/note_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -20,7 +20,7 @@ using namespace libzeth; using pp = defaults::pp; using Field = defaults::Field; using Hash = BLAKE2s_256; -using HashTree = MiMC_mp_gadget>; +using HashTree = MiMC_mp_gadget>; static const size_t TreeDepth = 4; namespace diff --git a/libzeth/tests/circuits/packed_addition_test.cpp b/libzeth/tests/circuits/packed_addition_test.cpp index 8daf05c79..067773056 100644 --- a/libzeth/tests/circuits/packed_addition_test.cpp +++ b/libzeth/tests/circuits/packed_addition_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/tests/circuits/prfs_test.cpp b/libzeth/tests/circuits/prfs_test.cpp index dbd4c8223..670b5527a 100644 --- a/libzeth/tests/circuits/prfs_test.cpp +++ b/libzeth/tests/circuits/prfs_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/tests/circuits/sha256_test.cpp b/libzeth/tests/circuits/sha256_test.cpp index c4b52ccea..9458188e9 100644 --- a/libzeth/tests/circuits/sha256_test.cpp +++ b/libzeth/tests/circuits/sha256_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/tests/circuits/simple_test.cpp b/libzeth/tests/circuits/simple_test.cpp index 2f65f85d9..dc33cc80c 100644 --- a/libzeth/tests/circuits/simple_test.cpp +++ b/libzeth/tests/circuits/simple_test.cpp @@ -1,35 +1,54 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ #include "simple_test.hpp" #include "core/utils.hpp" -#include "zeth_config.h" +#include "libzeth/serialization/r1cs_serialization.hpp" +#include "libzeth/snarks/groth16/groth16_snark.hpp" +#include "libzeth/snarks/pghr13/pghr13_snark.hpp" +#include "serialization/proto_utils.hpp" +#include "serialization/r1cs_variable_assignment_serialization.hpp" +#include #include +#include +#include using namespace libsnark; using namespace libzeth; -using pp = defaults::pp; -using Field = defaults::Field; +boost::filesystem::path g_output_dir = boost::filesystem::path(""); namespace { -TEST(SimpleTests, SimpleCircuitProof) +template +void test_simple_circuit_proof(bool support_output = true) { + using Field = libff::Fr; + // Simple circuit protoboard pb; libzeth::tests::simple_circuit(pb); // Constraint system - const r1cs_constraint_system constraint_system = + const r1cs_constraint_system &constraint_system = pb.get_constraint_system(); - const r1cs_primary_input primary{12}; - const r1cs_auxiliary_input auxiliary{1, 1, 1}; + // Write to file if output directory is given. + if (!g_output_dir.empty() && support_output) { + + boost::filesystem::path outpath = + g_output_dir / ("simple_circuit_r1cs_" + pp_name() + ".json"); + std::ofstream r1cs_stream(outpath.c_str()); + libzeth::r1cs_write_json(pb.get_constraint_system(), r1cs_stream); + } + + r1cs_primary_input primary; + r1cs_auxiliary_input auxiliary; + libzeth::tests::simple_circuit_assignment(Field("78"), primary, auxiliary); { // Test solution x = 1 (g1 = 1, g2 = 1), y = 12 @@ -45,23 +64,82 @@ TEST(SimpleTests, SimpleCircuitProof) } } - const r1cs_gg_ppzksnark_keypair keypair = - r1cs_gg_ppzksnark_generator(constraint_system); + const typename snarkT::keypair keypair = snarkT::generate_setup(pb); - const r1cs_gg_ppzksnark_proof proof = - r1cs_gg_ppzksnark_prover(keypair.pk, primary, auxiliary); + const typename snarkT::proof proof = + snarkT::generate_proof(keypair.pk, primary, auxiliary); - ASSERT_TRUE( - r1cs_gg_ppzksnark_verifier_strong_IC(keypair.vk, primary, proof)); + ASSERT_TRUE(snarkT::verify(primary, proof, keypair.vk)); + + if (!g_output_dir.empty() && support_output) { + { + boost::filesystem::path proving_key_path = + g_output_dir / ("simple_proving_key_" + snarkT::name + "_" + + pp_name() + ".bin"); + std::ofstream out_s(proving_key_path.c_str()); + snarkT::proving_key_write_bytes(keypair.pk, out_s); + } + { + boost::filesystem::path verification_key_path = + g_output_dir / ("simple_verification_key_" + snarkT::name + + "_" + pp_name() + ".bin"); + std::ofstream out_s(verification_key_path.c_str()); + snarkT::verification_key_write_bytes(keypair.vk, out_s); + } + { + boost::filesystem::path primary_inputs_path = + g_output_dir / + ("simple_primary_input_" + pp_name() + ".bin"); + std::ofstream out_s(primary_inputs_path.c_str()); + r1cs_variable_assignment_write_bytes(primary, out_s); + } + { + boost::filesystem::path assignment_path = + g_output_dir / ("simple_assignment_" + pp_name() + ".bin"); + std::ofstream out_s(assignment_path.c_str()); + r1cs_variable_assignment_write_bytes(primary, auxiliary, out_s); + } + { + boost::filesystem::path proof_path = + g_output_dir / ("simple_proof_" + snarkT::name + "_" + + pp_name() + ".bin"); + std::ofstream out_s(proof_path.c_str()); + snarkT::proof_write_bytes(proof, out_s); + } + } +} + +TEST(SimpleTests, SimpleCircuitProofGroth16AltBN128) +{ + test_simple_circuit_proof< + libff::alt_bn128_pp, + libzeth::groth16_snark>(); +} + +TEST(SimpleTests, SimpleCircuitProofGroth16BLS12_377) +{ + test_simple_circuit_proof< + libff::bls12_377_pp, + libzeth::groth16_snark>(); +} + +TEST(SimpleTests, SimpleCircuitProofPghr13) +{ + test_simple_circuit_proof< + libff::alt_bn128_pp, + pghr13_snark>(false); } TEST(SimpleTests, SimpleCircuitProofPow2Domain) { + using pp = libff::alt_bn128_pp; + using Field = libff::Fr; + // Simple circuit protoboard pb; libzeth::tests::simple_circuit(pb); - const r1cs_constraint_system constraint_system = + const r1cs_constraint_system &constraint_system = pb.get_constraint_system(); const r1cs_gg_ppzksnark_keypair keypair = r1cs_gg_ppzksnark_generator(constraint_system, true); @@ -78,14 +156,19 @@ TEST(SimpleTests, SimpleCircuitProofPow2Domain) int main(int argc, char **argv) { - // /!\ WARNING: Do once for all tests. Do not - // forget to do this !!!! - pp::init_public_params(); + // WARNING: Do once for all tests. Do not forget to do this. + libff::alt_bn128_pp::init_public_params(); + libff::bls12_377_pp::init_public_params(); // Remove stdout noise from libff libff::inhibit_profiling_counters = true; libff::inhibit_profiling_info = true; - // Run ::testing::InitGoogleTest(&argc, argv); + + // Extract the test data destination dir, if passed on the command line. + if (argc > 1) { + g_output_dir = boost::filesystem::path(argv[1]); + } + return RUN_ALL_TESTS(); } diff --git a/libzeth/tests/circuits/simple_test.hpp b/libzeth/tests/circuits/simple_test.hpp index 915b08455..be0231d7d 100644 --- a/libzeth/tests/circuits/simple_test.hpp +++ b/libzeth/tests/circuits/simple_test.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -13,17 +13,22 @@ namespace tests { /// Generate a simple test circuit with 1 public input 'y' and auxiliary -/// -/// input 'x', for the expression: -/// +/// inputs 'x', 'g1, and 'g2' for the expression: /// x^3 + 4x^2 + 2x + 5 = y -/// -/// Internal auxiliary inputs are: -/// +/// where: /// g1 = x * x /// g2 = g1 * x template void simple_circuit(libsnark::protoboard &pb); +/// Append a set of valid simple circuit inputs [ 'y', 'x', 'g1', 'g2' ] to +/// out_inputs. Note that out_primary can be the same as out_auxiliary (primary +/// input is appended first). +template +void simple_circuit_assignment( + const FieldT &x, + std::vector &out_primary, + std::vector &out_auxiliary); + } // namespace tests } // namespace libzeth diff --git a/libzeth/tests/circuits/simple_test.tcc b/libzeth/tests/circuits/simple_test.tcc index 84f5a4728..138b0c991 100644 --- a/libzeth/tests/circuits/simple_test.tcc +++ b/libzeth/tests/circuits/simple_test.tcc @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -46,6 +46,21 @@ template void simple_circuit(libsnark::protoboard &pb) r1cs_constraint(g2 + (4 * g1) + (2 * x) + 5, 1, y), "y"); } +template +void simple_circuit_assignment( + const FieldT &x, + std::vector &out_primary, + std::vector &out_auxiliary) +{ + const FieldT g1 = x * x; + const FieldT g2 = g1 * x; + const FieldT y = g2 + (g1 * 4) + (x * 2) + 5; + out_primary.push_back(y); + out_auxiliary.push_back(x); + out_auxiliary.push_back(g1); + out_auxiliary.push_back(g2); +} + } // namespace tests } // namespace libzeth diff --git a/libzeth/tests/core/bits_test.cpp b/libzeth/tests/core/bits_test.cpp index f2f6e93dd..2c2832526 100644 --- a/libzeth/tests/core/bits_test.cpp +++ b/libzeth/tests/core/bits_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/tests/core/chacha_test.cpp b/libzeth/tests/core/chacha_test.cpp index 68a817eb9..856d9ee29 100644 --- a/libzeth/tests/core/chacha_test.cpp +++ b/libzeth/tests/core/chacha_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/tests/core/ec_operation_data_test.cpp b/libzeth/tests/core/ec_operation_data_test.cpp index c297e952a..a6d436eaa 100644 --- a/libzeth/tests/core/ec_operation_data_test.cpp +++ b/libzeth/tests/core/ec_operation_data_test.cpp @@ -1,10 +1,12 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ #include "libzeth/core/field_element_utils.hpp" #include "libzeth/core/group_element_utils.hpp" +#include "libzeth/serialization/proto_utils.hpp" +#include #include #include #include @@ -13,6 +15,8 @@ using namespace libzeth; +boost::filesystem::path g_output_dir = boost::filesystem::path(""); + namespace { @@ -47,12 +51,22 @@ template void operation_test_data() const libff::G2 g2_4 = g2_2 + g2_2; const libff::G2 g2_8 = g2_4 + g2_4; + // Simultaneously write all field and group data to a string stream. + std::stringstream binary_stream; + std::cout << " Fr:"; std::cout << "\n r: " << bigint_to_hex>(libff::Fr::mod); + std::cout << "\n 1: "; + field_element_write_json(fr_1, std::cout); + field_element_write_bytes(fr_1, binary_stream); + std::cout << "\n 2: "; + field_element_write_json(fr_2, std::cout); + field_element_write_bytes(fr_2, binary_stream); std::cout << "\n -2: "; field_element_write_json(fr_minus_2, std::cout); + field_element_write_bytes(fr_minus_2, binary_stream); std::cout << " Fq:"; std::cout << "\n q: " @@ -60,35 +74,60 @@ template void operation_test_data() std::cout << "\n G1:"; std::cout << "\n 1: "; - point_affine_write_json(g1_1, std::cout); + group_element_write_json(g1_1, std::cout); + group_element_write_bytes(g1_1, binary_stream); std::cout << "\n -1: "; - point_affine_write_json(-g1_1, std::cout); + group_element_write_json(-g1_1, std::cout); + group_element_write_bytes(-g1_1, binary_stream); std::cout << "\n 2: "; - point_affine_write_json(g1_2, std::cout); + group_element_write_json(g1_2, std::cout); + group_element_write_bytes(g1_2, binary_stream); std::cout << "\n 3: "; - point_affine_write_json(g1_3, std::cout); + group_element_write_json(g1_3, std::cout); + group_element_write_bytes(g1_3, binary_stream); std::cout << "\n 4: "; - point_affine_write_json(g1_4, std::cout); + group_element_write_json(g1_4, std::cout); + group_element_write_bytes(g1_4, binary_stream); std::cout << "\n 6: "; - point_affine_write_json(g1_6, std::cout); + group_element_write_json(g1_6, std::cout); + group_element_write_bytes(g1_6, binary_stream); std::cout << "\n 8: "; - point_affine_write_json(g1_8, std::cout); + group_element_write_json(g1_8, std::cout); + group_element_write_bytes(g1_8, binary_stream); std::cout << "\n -8: "; - point_affine_write_json(-g1_8, std::cout); + group_element_write_json(-g1_8, std::cout); + group_element_write_bytes(-g1_8, binary_stream); std::cout << "\n G2:"; std::cout << "\n 1: "; - point_affine_write_json(g2_1, std::cout); + group_element_write_json(g2_1, std::cout); + group_element_write_bytes(g2_1, binary_stream); std::cout << "\n -1: "; - point_affine_write_json(-g2_1, std::cout); + group_element_write_json(-g2_1, std::cout); + group_element_write_bytes(-g2_1, binary_stream); std::cout << "\n 4: "; - point_affine_write_json(g2_4, std::cout); + group_element_write_json(g2_4, std::cout); + group_element_write_bytes(g2_4, binary_stream); std::cout << "\n 8: "; - point_affine_write_json(g2_8, std::cout); + group_element_write_json(g2_8, std::cout); + group_element_write_bytes(g2_8, binary_stream); std::cout << "\n -8: "; - point_affine_write_json(-g2_8, std::cout); + group_element_write_json(-g2_8, std::cout); + group_element_write_bytes(-g2_8, binary_stream); std::cout << "\n"; + // Write the binary data if output_dir given + if (!g_output_dir.empty()) { + boost::filesystem::path outpath = + g_output_dir / ("ec_test_data_" + libzeth::pp_name() + ".bin"); + std::cout << "(writing to file " << outpath << ")\n"; + std::ofstream out_s( + outpath.c_str(), std::ios_base::out | std::ios_base::binary); + out_s << binary_stream.str(); + } else { + std::cout << "(skipping binary data write)\n"; + } + // Check the statements above ASSERT_EQ(g1_6, g1_2 + g1_4); ASSERT_EQ(-g1_8, fr_minus_2 * g1_4); @@ -134,13 +173,13 @@ TEST(ECOperationDataTest, BW6_761) ASSERT_FALSE(g2_not_in_subgroup.is_in_safe_subgroup()); std::cout << " g1_not_well_formed: "; - point_affine_write_json(g1_not_well_formed, std::cout); + group_element_write_json(g1_not_well_formed, std::cout); std::cout << "\n g1_not_in_subgroup: "; - point_affine_write_json(g1_not_in_subgroup, std::cout); + group_element_write_json(g1_not_in_subgroup, std::cout); std::cout << "\n g2_not_well_formed: "; - point_affine_write_json(g2_not_well_formed, std::cout); + group_element_write_json(g2_not_well_formed, std::cout); std::cout << "\n g2_not_in_subgroup: "; - point_affine_write_json(g2_not_in_subgroup, std::cout); + group_element_write_json(g2_not_in_subgroup, std::cout); std::cout << "\n"; } @@ -174,13 +213,13 @@ TEST(ECOperationDataTest, BLS12_377) ASSERT_FALSE(g2_not_in_subgroup.is_in_safe_subgroup()); std::cout << " g1_not_well_formed: "; - point_affine_write_json(g1_not_well_formed, std::cout); + group_element_write_json(g1_not_well_formed, std::cout); std::cout << "\n g1_not_in_subgroup: "; - point_affine_write_json(g1_not_in_subgroup, std::cout); + group_element_write_json(g1_not_in_subgroup, std::cout); std::cout << "\n g2_not_well_formed: "; - point_affine_write_json(g2_not_well_formed, std::cout); + group_element_write_json(g2_not_well_formed, std::cout); std::cout << "\n g2_not_in_subgroup: "; - point_affine_write_json(g2_not_in_subgroup, std::cout); + group_element_write_json(g2_not_in_subgroup, std::cout); std::cout << "\n"; } @@ -192,5 +231,11 @@ int main(int argc, char **argv) libff::bls12_377_pp::init_public_params(); libff::bw6_761_pp::init_public_params(); ::testing::InitGoogleTest(&argc, argv); + + // Extract the test data destination dir, if passed on the command line. + if (argc > 1) { + g_output_dir = boost::filesystem::path(argv[1]); + } + return RUN_ALL_TESTS(); } diff --git a/libzeth/tests/core/evaluator_from_lagrange_test.cpp b/libzeth/tests/core/evaluator_from_lagrange_test.cpp index 18b1caa10..b8986b8b3 100644 --- a/libzeth/tests/core/evaluator_from_lagrange_test.cpp +++ b/libzeth/tests/core/evaluator_from_lagrange_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/tests/core/field_element_utils_test.cpp b/libzeth/tests/core/field_element_utils_test.cpp index 4db8c25ee..e8c7d0f98 100644 --- a/libzeth/tests/core/field_element_utils_test.cpp +++ b/libzeth/tests/core/field_element_utils_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -113,6 +113,41 @@ template void field_element_encode_decode_json_badstring_test() do_field_element_encode_decode_json_badstring_test>(); } +template void do_field_element_read_write_bytes_test() +{ + static const size_t elements_to_write = 4; + FieldT elements[elements_to_write]; + for (FieldT &el : elements) { + el = FieldT::random_element(); + } + + std::string buffer; + { + std::stringstream out_s; + for (const FieldT &el : elements) { + libzeth::field_element_write_bytes(el, out_s); + } + buffer = out_s.str(); + } + + { + std::stringstream in_s(buffer); + for (const FieldT &el : elements) { + FieldT read; + libzeth::field_element_read_bytes(read, in_s); + ASSERT_EQ(el, read); + } + } +} + +template void field_element_read_write_bytes_test() +{ + do_field_element_read_write_bytes_test>(); + do_field_element_read_write_bytes_test>(); + do_field_element_read_write_bytes_test>(); + do_field_element_read_write_bytes_test>(); +} + TEST(FieldElementUtilsTest, BigIntEncodeDecodeHex) { bigint_encode_decode_hex_test(); @@ -158,6 +193,15 @@ TEST(FieldElementUtilsTest, FieldElementEncodeDecodeJsonBadString) field_element_encode_decode_json_badstring_test(); } +TEST(FieldElementUtilsTest, FieldElementReadWriteBytes) +{ + field_element_read_write_bytes_test(); + field_element_read_write_bytes_test(); + field_element_read_write_bytes_test(); + field_element_read_write_bytes_test(); + field_element_read_write_bytes_test(); +} + } // namespace int main(int argc, char **argv) diff --git a/libzeth/tests/core/group_element_utils_test.cpp b/libzeth/tests/core/group_element_utils_test.cpp index 5454e0f3e..829bd31ec 100644 --- a/libzeth/tests/core/group_element_utils_test.cpp +++ b/libzeth/tests/core/group_element_utils_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -27,10 +27,10 @@ TEST(GroupElementUtilsTest, G1EncodeDecodeJsonTestVectorAltBN128) "\"0x2857bd14bbc09767bed8e913d3ccb42b2bc8738f715417dd6f020725d22bcd90\"" "]"; G1 g1 = Fr(13) * G1::one(); - std::string g1_json = libzeth::point_affine_to_json(g1); + std::string g1_json = libzeth::group_element_to_json(g1); ASSERT_EQ(g1_json_expected, g1_json); - G1 g1_decoded = libzeth::point_affine_from_json(g1_json); + G1 g1_decoded = libzeth::group_element_from_json(g1_json); ASSERT_EQ(g1, g1_decoded); } @@ -50,45 +50,132 @@ TEST(GroupElementUtilsTest, G2EncodeJsonTestVectorAltBN128) "\"0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa\"" "]]"; G2 g2 = G2::one(); - const std::string g2_json = libzeth::point_affine_to_json(g2); - const G2 g2_decoded = libzeth::point_affine_from_json(g2_json); + const std::string g2_json = libzeth::group_element_to_json(g2); + const G2 g2_decoded = libzeth::group_element_from_json(g2_json); ASSERT_EQ(g2_json_expected, g2_json); ASSERT_EQ(g2, g2_decoded); } template -static void single_group_element_encode_decode_test(const GroupT &g) +static void single_group_element_encode_decode_json_test(const GroupT &g) { - const std::string g_json = libzeth::point_affine_to_json(g); - const GroupT g_decoded = libzeth::point_affine_from_json(g_json); + const std::string g_json = libzeth::group_element_to_json(g); + const GroupT g_decoded = libzeth::group_element_from_json(g_json); ASSERT_EQ(g, g_decoded); } -template static void group_element_encode_decode_test() +template static void group_element_encode_decode_json_test() { - single_group_element_encode_decode_test(GroupT::random_element()); - single_group_element_encode_decode_test(GroupT::zero()); - single_group_element_encode_decode_test(GroupT::one()); + single_group_element_encode_decode_json_test(GroupT::random_element()); + single_group_element_encode_decode_json_test(GroupT::zero()); + single_group_element_encode_decode_json_test(GroupT::one()); } -TEST(GroupElementUtilsTest, G1EncodeDecode) +TEST(GroupElementUtilsTest, G1EncodeDecodeJSON) { - group_element_encode_decode_test>(); - group_element_encode_decode_test>(); - group_element_encode_decode_test>(); - group_element_encode_decode_test>(); - group_element_encode_decode_test>(); + group_element_encode_decode_json_test>(); + group_element_encode_decode_json_test>(); + group_element_encode_decode_json_test>(); + group_element_encode_decode_json_test>(); + group_element_encode_decode_json_test>(); } -TEST(GroupElementUtilsTest, G2EncodeDecode) +TEST(GroupElementUtilsTest, G2EncodeDecodeJSON) { - group_element_encode_decode_test>(); - group_element_encode_decode_test>(); - group_element_encode_decode_test>(); - group_element_encode_decode_test>(); - group_element_encode_decode_test>(); + group_element_encode_decode_json_test>(); + group_element_encode_decode_json_test>(); + group_element_encode_decode_json_test>(); + group_element_encode_decode_json_test>(); + group_element_encode_decode_json_test>(); +} + +template +static void single_group_element_encode_decode_bytes_test(const GroupT &g) +{ + std::string buffer; + { + std::stringstream ss; + libzeth::group_element_write_bytes(g, ss); + buffer = ss.str(); + } + + GroupT g_decoded; + { + std::stringstream ss(buffer); + libzeth::group_element_read_bytes(g_decoded, ss); + } + + ASSERT_EQ(g, g_decoded); +} + +template static void group_element_encode_decode_bytes_test() +{ + single_group_element_encode_decode_bytes_test(GroupT::random_element()); + single_group_element_encode_decode_bytes_test(GroupT::zero()); + single_group_element_encode_decode_bytes_test(GroupT::one()); +} + +TEST(GroupElementUtilsTest, G1EncodeDecodeBytes) +{ + group_element_encode_decode_bytes_test>(); + group_element_encode_decode_bytes_test>(); + group_element_encode_decode_bytes_test>(); + group_element_encode_decode_bytes_test>(); + group_element_encode_decode_bytes_test>(); +} + +TEST(GroupElementUtilsTest, G2EncodeDecodeBytes) +{ + group_element_encode_decode_bytes_test>(); + group_element_encode_decode_bytes_test>(); + group_element_encode_decode_bytes_test>(); + group_element_encode_decode_bytes_test>(); + group_element_encode_decode_bytes_test>(); +} + +template static void group_elements_encode_decode_bytes_test() +{ + const size_t num_elements = 17; + std::vector elements; + elements.reserve(num_elements); + for (size_t i = 0; i < num_elements; ++i) { + elements.emplace_back(GroupT::random_element()); + } + + std::string buffer; + { + std::stringstream ss; + libzeth::group_elements_write_bytes(elements, ss); + buffer = ss.str(); + } + + std::vector elements_decoded; + { + std::stringstream ss(buffer); + libzeth::group_elements_read_bytes(elements_decoded, ss); + } + + ASSERT_EQ(elements, elements_decoded); +} + +TEST(GroupElementUtilsTest, G1CollectionEncodeDecodeBytes) +{ + group_elements_encode_decode_bytes_test>(); + group_elements_encode_decode_bytes_test>(); + group_elements_encode_decode_bytes_test>(); + group_elements_encode_decode_bytes_test>(); + group_elements_encode_decode_bytes_test>(); +} + +TEST(GroupElementUtilsTest, G2CollectionEncodeDecodeBytes) +{ + group_elements_encode_decode_bytes_test>(); + group_elements_encode_decode_bytes_test>(); + group_elements_encode_decode_bytes_test>(); + group_elements_encode_decode_bytes_test>(); + group_elements_encode_decode_bytes_test>(); } } // namespace diff --git a/libzeth/tests/core/utils_test.cpp b/libzeth/tests/core/utils_test.cpp index 381a9853b..f3de08eac 100644 --- a/libzeth/tests/core/utils_test.cpp +++ b/libzeth/tests/core/utils_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/tests/mpc/groth16/mpc_hash_test.cpp b/libzeth/tests/mpc/groth16/mpc_hash_test.cpp index 4b9725167..d51ed6e9a 100644 --- a/libzeth/tests/mpc/groth16/mpc_hash_test.cpp +++ b/libzeth/tests/mpc/groth16/mpc_hash_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/tests/mpc/groth16/mpc_test.cpp b/libzeth/tests/mpc/groth16/mpc_test.cpp index a37f28ea4..2dfc22943 100644 --- a/libzeth/tests/mpc/groth16/mpc_test.cpp +++ b/libzeth/tests/mpc/groth16/mpc_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -443,7 +443,9 @@ TEST(MPCTests, KeyPairReadWrite) in.exceptions( std::ios_base::eofbit | std::ios_base::badbit | std::ios_base::failbit); - return groth16_snark::keypair_read_bytes(in); + typename groth16_snark::keypair kp; + groth16_snark::keypair_read_bytes(kp, in); + return kp; }(); ASSERT_EQ(keypair.pk, keypair_deserialized.pk); diff --git a/libzeth/tests/mpc/groth16/powersoftau_test.cpp b/libzeth/tests/mpc/groth16/powersoftau_test.cpp index ad17032bb..5bac0feff 100644 --- a/libzeth/tests/mpc/groth16/powersoftau_test.cpp +++ b/libzeth/tests/mpc/groth16/powersoftau_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/tests/prover/prover_test.cpp b/libzeth/tests/prover/prover_test.cpp index 6f9991d74..0fa0f844e 100644 --- a/libzeth/tests/prover/prover_test.cpp +++ b/libzeth/tests/prover/prover_test.cpp @@ -1,10 +1,11 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ #include "libzeth/circuits/blake2s/blake2s.hpp" #include "libzeth/circuits/circuit_types.hpp" #include "libzeth/circuits/circuit_wrapper.hpp" +#include "libzeth/core/field_element_utils.hpp" #include "libzeth/core/utils.hpp" #include "libzeth/snarks/groth16/groth16_snark.hpp" #include "libzeth/snarks/pghr13/pghr13_snark.hpp" @@ -35,6 +36,9 @@ using prover = circuit_wrapper< 2, TreeDepth>; +template +using input_hasher_type = typename prover::input_hasher_type; + namespace { @@ -42,7 +46,7 @@ const bits256 zero_bits256 = bits256::from_hex( "0000000000000000000000000000000000000000000000000000000000000000"); template -bool TestValidJS2In2Case1( +void TestValidJS2In2Case1( const prover &prover, const typename snarkT::keypair &keypair) { // --- General setup for the tests --- // @@ -140,6 +144,7 @@ bool TestValidJS2In2Case1( libff::leave_block("Create JSOutput/zeth_note", true); libff::enter_block("Generate proof", true); + std::vector public_data; extended_proof ext_proof = prover.prove( updated_root_value, inputs, @@ -148,25 +153,40 @@ bool TestValidJS2In2Case1( value_pub_out_bits64, h_sig, phi, - keypair.pk); + keypair.pk, + public_data); libff::leave_block("Generate proof", true); + std::vector primary_inputs = ext_proof.get_primary_inputs(); + ASSERT_EQ(1, primary_inputs.size()); + ASSERT_NE(Field::zero(), primary_inputs[0]); + + // Check the hashed public data + ASSERT_GT(public_data.size(), 1); + ASSERT_EQ( + input_hasher_type::compute_hash(public_data), + primary_inputs[0]); + + // The full assignment should be strictly larger than the public data, and + // begin with the primary input. + const std::vector &full_assignment = prover.get_last_assignment(); + ASSERT_GT(full_assignment.size(), public_data.size()); + ASSERT_EQ(primary_inputs[0], full_assignment[0]); + libff::enter_block("Verify proof", true); // Get the verification key typename snarkT::verification_key vk = keypair.vk; - bool res = snarkT::verify( - ext_proof.get_primary_inputs(), ext_proof.get_proof(), vk); + bool res = snarkT::verify(primary_inputs, ext_proof.get_proof(), vk); std::cout << "Does the proof verify? " << res << std::endl; libff::leave_block("Verify proof", true); std::cout << "[DEBUG] Displaying the extended proof" << std::endl; ext_proof.write_json(std::cout); - - return res; + ASSERT_TRUE(res); } template -bool TestValidJS2In2Case2( +void TestValidJS2In2Case2( const prover &prover, const typename snarkT::keypair &keypair) { libff::print_header( @@ -261,6 +281,7 @@ bool TestValidJS2In2Case2( libff::enter_block("Generate proof", true); // RHS = 0x1A00000000000002 + 0x1500000000000002 + 0x000000000000000B = // 2F0000000000000F (LHS) + std::vector public_data; extended_proof ext_proof = prover.prove( updated_root_value, inputs, @@ -271,9 +292,15 @@ bool TestValidJS2In2Case2( bits64::from_hex("000000000000000B"), h_sig, phi, - keypair.pk); + keypair.pk, + public_data); libff::leave_block("Generate proof", true); + // Check the hashed public data + ASSERT_EQ( + ext_proof.get_primary_inputs()[0], + input_hasher_type::compute_hash(public_data)); + libff::enter_block("Verify proof", true); // Get the verification key typename snarkT::verification_key vk = keypair.vk; @@ -285,11 +312,11 @@ bool TestValidJS2In2Case2( std::cout << "[DEBUG] Displaying the extended proof" << std::endl; ext_proof.write_json(std::cout); - return res; + ASSERT_TRUE(res); } template -bool TestValidJS2In2Case3( +void TestValidJS2In2Case3( const prover &prover, const typename snarkT::keypair &keypair) { // --- General setup for the tests --- // @@ -386,6 +413,7 @@ bool TestValidJS2In2Case3( libff::enter_block("Generate proof", true); // (RHS) 0x1A00000000000012 + 0x1500000000000002 + 0x000000000000000B = // 2F0000000000000F + 0x0000000000000010 + 0x0 (LHS) + std::vector public_data; extended_proof ext_proof = prover.prove( updated_root_value, inputs, @@ -394,9 +422,15 @@ bool TestValidJS2In2Case3( bits64::from_hex("000000000000000B"), // v_pub_out = 0x000000000000000B h_sig, phi, - keypair.pk); + keypair.pk, + public_data); libff::leave_block("Generate proof", true); + // Check the hashed public data + ASSERT_EQ( + ext_proof.get_primary_inputs()[0], + input_hasher_type::compute_hash(public_data)); + libff::enter_block("Verify proof", true); // Get the verification key typename snarkT::verification_key vk = keypair.vk; @@ -408,11 +442,11 @@ bool TestValidJS2In2Case3( std::cout << "[DEBUG] Displaying the extended proof" << std::endl; ext_proof.write_json(std::cout); - return res; + ASSERT_TRUE(res); } template -bool TestValidJS2In2Deposit( +void TestValidJS2In2Deposit( const prover &prover, const typename snarkT::keypair &keypair) { // --- General setup for the tests --- // @@ -511,6 +545,7 @@ bool TestValidJS2In2Deposit( libff::enter_block("Generate proof", true); // RHS = 0x0 + 0x3782DACE9D900000 + 0x29A2241AF62C0000 = 0x6124FEE993BC0000 // (LHS) + std::vector public_data; extended_proof ext_proof = prover.prove( updated_root_value, inputs, @@ -521,9 +556,15 @@ bool TestValidJS2In2Deposit( bits64::from_hex("0000000000000000"), h_sig, phi, - keypair.pk); + keypair.pk, + public_data); libff::leave_block("Generate proof", true); + // Check the hashed public data + ASSERT_EQ( + ext_proof.get_primary_inputs()[0], + input_hasher_type::compute_hash(public_data)); + libff::enter_block("Verify proof", true); // Get the verification key typename snarkT::verification_key vk = keypair.vk; @@ -536,11 +577,11 @@ bool TestValidJS2In2Deposit( std::cout << "Does the proof verify? " << res << std::endl; libff::leave_block("Verify proof", true); - return res; + ASSERT_TRUE(res); } template -bool TestInvalidJS2In2( +void TestInvalidJS2In2( const prover &prover, const typename snarkT::keypair &keypair) { // --- General setup for the tests --- // @@ -644,6 +685,7 @@ bool TestInvalidJS2In2( // 0x8530000A00000001 (9.597170848876199937 ETH) + 0x7550000A00000000 // (8.453256543524093952 ETH) = RHS LHS = 18.050427392400293888 ETH RHS // = 18.050427392400293889 ETH (1 wei higher than LHS) + std::vector public_data; extended_proof ext_proof = prover.prove( updated_root_value, inputs, @@ -654,7 +696,8 @@ bool TestInvalidJS2In2( bits64::from_hex("0000000000000000"), h_sig, phi, - keypair.pk); + keypair.pk, + public_data); libff::leave_block("Generate proof", true); libff::enter_block("Verify proof", true); @@ -668,7 +711,7 @@ bool TestInvalidJS2In2( std::cout << "[DEBUG] Displaying the extended proof" << std::endl; ext_proof.write_json(std::cout); - return res; + ASSERT_FALSE(res); } template static void run_prover_tests() @@ -678,29 +721,23 @@ template static void run_prover_tests() prover proverJS2to2; typename snarkT::keypair keypair = proverJS2to2.generate_trusted_setup(); - bool res = false; - res = TestValidJS2In2Case1(proverJS2to2, keypair); - ASSERT_TRUE(res); + TestValidJS2In2Case1(proverJS2to2, keypair); - res = TestValidJS2In2Case2(proverJS2to2, keypair); - ASSERT_TRUE(res); + TestValidJS2In2Case2(proverJS2to2, keypair); - res = TestValidJS2In2Case3(proverJS2to2, keypair); - ASSERT_TRUE(res); + TestValidJS2In2Case3(proverJS2to2, keypair); - res = TestValidJS2In2Deposit(proverJS2to2, keypair); - ASSERT_TRUE(res); + TestValidJS2In2Deposit(proverJS2to2, keypair); // The following is expected to throw an exception because LHS =/= RHS. // Ensure that the exception is thrown. ASSERT_THROW( - (res = TestInvalidJS2In2(proverJS2to2, keypair)), - std::invalid_argument); + (TestInvalidJS2In2(proverJS2to2, keypair)), std::invalid_argument); + bool res = false; try { - res = false; - res = TestInvalidJS2In2(proverJS2to2, keypair); + TestInvalidJS2In2(proverJS2to2, keypair); res = true; } catch (const std::invalid_argument &e) { std::cerr << "Invalid argument exception: " << e.what() << '\n'; diff --git a/libzeth/tests/serialization/proto_utils_test.cpp b/libzeth/tests/serialization/proto_utils_test.cpp index 02fbebeac..d6564d82b 100644 --- a/libzeth/tests/serialization/proto_utils_test.cpp +++ b/libzeth/tests/serialization/proto_utils_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/libzeth/tests/serialization/r1cs_serialization_test.cpp b/libzeth/tests/serialization/r1cs_serialization_test.cpp index 54f03eab2..f77f910a4 100644 --- a/libzeth/tests/serialization/r1cs_serialization_test.cpp +++ b/libzeth/tests/serialization/r1cs_serialization_test.cpp @@ -1,8 +1,14 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ +#include "libzeth/circuits/blake2s/blake2s.hpp" +#include "libzeth/circuits/circuit_types.hpp" +#include "libzeth/circuits/circuit_wrapper.hpp" +#include "libzeth/serialization/proto_utils.hpp" #include "libzeth/serialization/r1cs_serialization.hpp" +#include "libzeth/snarks/groth16/groth16_snark.hpp" +#include "libzeth/tests/circuits/simple_test.hpp" #include #include @@ -52,6 +58,41 @@ template void accumulation_vector_json_encode_decode() ASSERT_EQ(acc_vect_string, acc_vect_decoded_string); } +template void r1cs_bytes_encode_decode() +{ + using Field = libff::Fr; + + // Create the joinsplit constraint system. + libzeth::circuit_wrapper< + libzeth::HashT, + libzeth::HashTreeT, + ppT, + libzeth::groth16_snark, + 2, + 2, + 32> + circuit; + const libsnark::r1cs_constraint_system &r1cs = + circuit.get_constraint_system(); + + std::string r1cs_bytes = ([&r1cs]() { + std::stringstream ss; + libzeth::r1cs_write_bytes(r1cs, ss); + return ss.str(); + })(); + + std::cout << "Joinsplit constraint system(" << libzeth::pp_name() + << "): " << std::to_string(r1cs_bytes.size()) << " bytes\n"; + + libsnark::r1cs_constraint_system r1cs2; + { + std::stringstream ss(r1cs_bytes); + libzeth::r1cs_read_bytes(r1cs2, ss); + } + + ASSERT_EQ(r1cs, r1cs2); +} + TEST(R1CSSerializationTest, PrimaryInputsJsonEncodeDecode) { primary_inputs_json_encode_decode(); @@ -70,6 +111,12 @@ TEST(R1CSSerializationTest, AccumulationVectorJsonEncodeDecode) accumulation_vector_json_encode_decode(); } +TEST(R1CSSerializationTest, R1CSBytesEncodeDecode) +{ + r1cs_bytes_encode_decode(); + r1cs_bytes_encode_decode(); +} + } // namespace int main(int argc, char **argv) diff --git a/libzeth/tests/serialization/r1cs_variable_assignment_serialization_test.cpp b/libzeth/tests/serialization/r1cs_variable_assignment_serialization_test.cpp new file mode 100644 index 000000000..715e0c661 --- /dev/null +++ b/libzeth/tests/serialization/r1cs_variable_assignment_serialization_test.cpp @@ -0,0 +1,80 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +#include "libzeth/serialization/r1cs_variable_assignment_serialization.hpp" + +#include +#include +#include + +namespace +{ + +template bool test_r1cs_variable_assignment_read_write_bytes() +{ + using FieldT = libff::Fr; + const size_t assignment_size = 37; + const size_t primary_size = 3; + + libsnark::r1cs_variable_assignment assignment; + assignment.reserve(assignment_size); + for (size_t i = 0; i < assignment_size; ++i) { + assignment.push_back(FieldT::random_element()); + } + + std::string buffer = ([&assignment]() { + std::stringstream ss; + libzeth::r1cs_variable_assignment_write_bytes(assignment, ss); + return ss.str(); + })(); + + libsnark::r1cs_variable_assignment assignment2; + { + std::stringstream ss(buffer); + libzeth::r1cs_variable_assignment_read_bytes(assignment2, ss); + } + + if (assignment != assignment2) { + return false; + } + + // Write as separate primary and auxiliary iputs + buffer = ([&assignment]() { + std::stringstream ss; + libzeth::r1cs_variable_assignment_write_bytes( + libsnark::r1cs_primary_input( + assignment.begin(), assignment.begin() + primary_size), + libsnark::r1cs_primary_input( + assignment.begin() + primary_size, assignment.end()), + ss); + return ss.str(); + })(); + + { + std::stringstream ss(buffer); + libzeth::r1cs_variable_assignment_read_bytes(assignment2, ss); + } + + return assignment == assignment2; +} + +TEST(R1CSVariableAssignementSerializationTest, AssignmentReadWriteBytes) +{ + const bool test_alt_bn128 = + test_r1cs_variable_assignment_read_write_bytes(); + ASSERT_TRUE(test_alt_bn128); + const bool test_bls12_377 = + test_r1cs_variable_assignment_read_write_bytes(); + ASSERT_TRUE(test_bls12_377); +} + +} // namespace + +int main(int argc, char **argv) +{ + libff::alt_bn128_pp::init_public_params(); + libff::bls12_377_pp::init_public_params(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/libzeth/tests/snarks/common_api_tests.tcc b/libzeth/tests/snarks/common_api_tests.tcc index 53be8dcb7..cbdc91aac 100644 --- a/libzeth/tests/snarks/common_api_tests.tcc +++ b/libzeth/tests/snarks/common_api_tests.tcc @@ -1,10 +1,10 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ #include -template void verification_key_encode_decode_test() +template void verification_key_encode_decode_json_test() { using snark = typename apiHandlerT::snark; @@ -20,7 +20,7 @@ template void verification_key_encode_decode_test() } template -void extended_proof_encode_decode_test( +void extended_proof_encode_decode_json_test( const libzeth::extended_proof &proof) { using snark = typename apiHandlerT::snark; diff --git a/libzeth/tests/snarks/common_snark_tests.tcc b/libzeth/tests/snarks/common_snark_tests.tcc new file mode 100644 index 000000000..cc86534e3 --- /dev/null +++ b/libzeth/tests/snarks/common_snark_tests.tcc @@ -0,0 +1,128 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +#ifndef __ZETH_TESTS_SNARKS_COMMON_SNARK_TESTS_TCC__ +#define __ZETH_TESTS_SNARKS_COMMON_SNARK_TESTS_TCC__ + +#include "libzeth/tests/circuits/simple_test.hpp" + +#include +#include +#include + +namespace libzeth +{ + +namespace tests +{ + +static const size_t DUMMY_NUM_PRIMARY_INPUTS = 37; + +template +typename snarkT::proving_key dummy_proving_key() +{ + using Field = libff::Fr; + libsnark::protoboard pb; + libzeth::tests::simple_circuit(pb); + libzeth::tests::simple_circuit(pb); + libzeth::tests::simple_circuit(pb); + libzeth::tests::simple_circuit(pb); + + typename snarkT::keypair keypair = snarkT::generate_setup(pb); + + return keypair.pk; +} + +template typename snarkT::proof dummy_proof() +{ + using Field = libff::Fr; + libsnark::protoboard pb; + libzeth::tests::simple_circuit(pb); + libzeth::tests::simple_circuit(pb); + libzeth::tests::simple_circuit(pb); + libzeth::tests::simple_circuit(pb); + + libsnark::r1cs_primary_input primary; + libsnark::r1cs_auxiliary_input auxiliary; + libzeth::tests::simple_circuit_assignment(Field("10"), primary, auxiliary); + libzeth::tests::simple_circuit_assignment( + Field("12"), auxiliary, auxiliary); + libzeth::tests::simple_circuit_assignment( + Field("14"), auxiliary, auxiliary); + libzeth::tests::simple_circuit_assignment( + Field("16"), auxiliary, auxiliary); + + if (!pb.get_constraint_system().is_satisfied(primary, auxiliary)) { + throw std::runtime_error("constraint system not satisfied"); + } + + const typename snarkT::keypair keypair = snarkT::generate_setup(pb); + return snarkT::generate_proof(keypair.pk, primary, auxiliary); +} + +template +bool verification_key_read_write_bytes_test() +{ + const typename snarkT::verification_key vk = + snarkT::verification_key::dummy_verification_key( + DUMMY_NUM_PRIMARY_INPUTS); + + std::string buffer = ([&vk]() { + std::stringstream ss; + snarkT::verification_key_write_bytes(vk, ss); + return ss.str(); + })(); + + typename snarkT::verification_key vk2; + { + std::stringstream ss(buffer); + snarkT::verification_key_read_bytes(vk2, ss); + } + + return vk == vk2; +} + +template bool proving_key_read_write_bytes_test() +{ + const typename snarkT::proving_key pk = dummy_proving_key(); + + std::string buffer = ([&pk]() { + std::stringstream ss; + snarkT::proving_key_write_bytes(pk, ss); + return ss.str(); + })(); + + typename snarkT::proving_key pk2; + { + std::stringstream ss(buffer); + snarkT::proving_key_read_bytes(pk2, ss); + } + + return pk == pk2; +} + +template bool proof_read_write_bytes_test() +{ + const typename snarkT::proof proof = dummy_proof(); + + std::string buffer = ([&proof]() { + std::stringstream ss; + snarkT::proof_write_bytes(proof, ss); + return ss.str(); + })(); + + typename snarkT::proof proof2; + { + std::stringstream ss(buffer); + snarkT::proof_read_bytes(proof2, ss); + } + + return proof == proof2; +} + +} // namespace tests + +} // namespace libzeth + +#endif // __ZETH_TESTS_SNARKS_COMMON_SNARK_TESTS_TCC__ diff --git a/libzeth/tests/snarks/groth16/groth16_api_handler_test.cpp b/libzeth/tests/snarks/groth16/groth16_api_handler_test.cpp index dbec8cfba..4a389477a 100644 --- a/libzeth/tests/snarks/groth16/groth16_api_handler_test.cpp +++ b/libzeth/tests/snarks/groth16/groth16_api_handler_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -20,7 +20,8 @@ namespace TEST(Groth16ApiHandlerTest, VerificationKeyEncodeDecode) { - verification_key_encode_decode_test>(); + verification_key_encode_decode_json_test< + libzeth::groth16_api_handler>(); } TEST(Groth16ApiHandlerTest, ProofEncodeDecode) @@ -29,7 +30,9 @@ TEST(Groth16ApiHandlerTest, ProofEncodeDecode) G1::random_element(), G2::random_element(), G1::random_element()}; libsnark::r1cs_primary_input dummy_inputs{ Fr::random_element(), Fr::random_element(), Fr::random_element()}; - extended_proof_encode_decode_test>( + extended_proof_encode_decode_json_test< + pp, + libzeth::groth16_api_handler>( {std::move(dummy_proof), std::move(dummy_inputs)}); } diff --git a/libzeth/tests/snarks/groth16/groth16_snark_test.cpp b/libzeth/tests/snarks/groth16/groth16_snark_test.cpp new file mode 100644 index 000000000..92db02c06 --- /dev/null +++ b/libzeth/tests/snarks/groth16/groth16_snark_test.cpp @@ -0,0 +1,234 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +#include "libzeth/serialization/proto_utils.hpp" +#include "libzeth/serialization/r1cs_variable_assignment_serialization.hpp" +#include "libzeth/snarks/groth16/groth16_snark.hpp" +#include "libzeth/tests/snarks/common_snark_tests.tcc" + +#include +#include +#include +#include + +boost::filesystem::path g_output_dir = boost::filesystem::path(""); + +namespace +{ + +/// Simple function to generate serializations of snark-related data types for +/// groth16. These may not be well-formed, but have known values for testing +/// serialization compatibility in other components. Data is written to a file +/// with the given path. +template void generate_test_data() +{ + if (g_output_dir.empty()) { + std::cout << "Skipping groth16 test data output (" + << libzeth::pp_name() << ")\n"; + return; + } + + using groth16 = libzeth::groth16_snark; + using Field = libff::Fr; + using G1 = libff::G1; + using G2 = libff::G2; + const std::string curve_name = libzeth::pp_name(); + + // dummy constraint_system = + // primary_input = { x1, x2 }, (indices 1, 2) + // auxiliary_input = { w1, w2, w3 }, (3, 4, 5) + // constraints = { + // (2*x1 + 3*w1) * (4*x2 + 5*w2) = 6*w3 + 7*x1, + // (7*x2 + 6*w3) * (5*x1 + 4*w1) = 3*w2 + 2*x2, + // } + // + // (Note that these are constructed "manually" (rather than with a big + // constructor call) to avoid the sorting in the linear_combination + // constructor, which makes the order of terms less predictable. + libsnark::r1cs_constraint_system cs; + + libsnark::linear_combination cs1_A; + cs1_A.add_term({1, 2}); + cs1_A.add_term({3, 3}); + libsnark::linear_combination cs1_B; + cs1_B.add_term({2, 4}); + cs1_B.add_term({4, 5}); + libsnark::linear_combination cs1_C; + cs1_C.add_term({5, 6}); + cs1_C.add_term({1, 7}); + cs.add_constraint({cs1_A, cs1_B, cs1_C}); + + libsnark::linear_combination cs2_A; + cs2_A.add_term({2, 7}); + cs2_A.add_term({5, 6}); + libsnark::linear_combination cs2_B; + cs2_B.add_term({1, 5}); + cs2_B.add_term({3, 4}); + libsnark::linear_combination cs2_C; + cs2_C.add_term({4, 3}); + cs2_C.add_term({2, 2}); + cs.add_constraint({cs2_A, cs2_B, cs2_C}); + + cs.primary_input_size = 2; + cs.auxiliary_input_size = 3; + { + std::ofstream out_s( + (g_output_dir / ("groth16_r1cs_" + curve_name)).c_str(), + std::ios_base::out | std::ios_base::binary); + libzeth::r1cs_write_bytes(cs, out_s); + } + + // Proving key + const typename groth16::proving_key pk( + G1::one(), // alpha_g1 = [1]_1 + -G1::one(), // beta_g1 = [-1]_1 + -G2::one(), // beta_g2 = [-1]_2 + -(G1::one() + G1::one()), // beta_g1 = [-2]_1 + -(G2::one() + G2::one()), // beta_g2 = [-2]_2 + libff::G1_vector{{ + // A_query = { [7]_1, [-3]_1, [8]_1, [-4]_1, [9]_1, [-5]_1 } + Field("7") * G1::one(), + -Field("3") * G1::one(), + Field("8") * G1::one(), + -Field("4") * G1::one(), + Field("9") * G1::one(), + -Field("5") * G1::one(), + }}, + libsnark::knowledge_commitment_vector( + std::vector>{{ + // B_query = { + // ([9]_1,[9]_2), ([-9]_1,[-9]_2), ([10]_1,[10}_2), + // ([11]_1,[11}_2), ([12]_1,[12}_2), ([13]_1,[13}_2) + // } + libsnark::knowledge_commitment( + Field("9") * G2::one(), Field("9") * G1::one()), + libsnark::knowledge_commitment( + -Field("9") * G2::one(), -Field("9") * G1::one()), + libsnark::knowledge_commitment( + Field("10") * G2::one(), Field("10") * G1::one()), + libsnark::knowledge_commitment( + -Field("11") * G2::one(), -Field("11") * G1::one()), + libsnark::knowledge_commitment( + Field("12") * G2::one(), Field("12") * G1::one()), + libsnark::knowledge_commitment( + -Field("13") * G2::one(), -Field("13") * G1::one()), + }}), + {{ + // H_query = { [11]_1, [-11]_1, [12]_1 } + Field("11") * G1::one(), + -Field("11") * G1::one(), + Field("12") * G1::one(), + }}, + {{ + // L_query = { [13]_1, [-13]_1, [14]_1 } + Field("13") * G1::one(), + -Field("13") * G1::one(), + Field("14") * G1::one(), + }}, + std::move(cs)); + { + std::ofstream out_s( + (g_output_dir / ("groth16_proving_key_" + curve_name)).c_str(), + std::ios_base::out | std::ios_base::binary); + groth16::proving_key_write_bytes(pk, out_s); + } + + // Verification Key + const typename groth16::verification_key vk( + Field("21") * G1::one(), // alpha_g1 = [21]_1 + -Field("21") * G2::one(), // beta_g2 = [-21]_2 + Field("22") * G2::one(), // delta_g2 = [22]_2 + libsnark::accumulation_vector( + // ABC_g1 = { [13]_1, { [-13]_1, [14]_1 } } + Field("13") * G1::one(), + libff::G1_vector{{ + -Field("13") * G1::one(), + Field("14") * G1::one(), + }})); + { + std::ofstream out_s( + (g_output_dir / ("groth16_verification_key_" + curve_name)).c_str(), + std::ios_base::out | std::ios_base::binary); + groth16::verification_key_write_bytes(vk, out_s); + } + + // Variable assignment + const libsnark::r1cs_variable_assignment assignment{{ + Field("15"), + -Field("15"), + Field("16"), + -Field("16"), + Field("17"), + -Field("17"), + }}; + { + std::ofstream out_s( + (g_output_dir / ("groth16_assignment_" + curve_name)).c_str(), + std::ios_base::out | std::ios_base::binary); + libzeth::r1cs_variable_assignment_write_bytes(assignment, out_s); + } +} + +TEST(Groth16SnarkTest, Groth16TestData) +{ + generate_test_data(); + generate_test_data(); +} + +TEST(Groth16SnarkTest, TestVerificationKeyReadWriteBytes) +{ + const bool test_alt_bn128 = + libzeth::tests::verification_key_read_write_bytes_test< + libff::alt_bn128_pp, + libzeth::groth16_snark>(); + ASSERT_TRUE(test_alt_bn128); + const bool test_bls12_377 = + libzeth::tests::verification_key_read_write_bytes_test< + libff::bls12_377_pp, + libzeth::groth16_snark>(); + ASSERT_TRUE(test_bls12_377); +} + +TEST(Groth16SnarkTest, TestProvingKeyReadWriteBytes) +{ + const bool test_alt_bn128 = + libzeth::tests::proving_key_read_write_bytes_test< + libff::alt_bn128_pp, + libzeth::groth16_snark>(); + ASSERT_TRUE(test_alt_bn128); + const bool test_bls12_377 = + libzeth::tests::proving_key_read_write_bytes_test< + libff::bls12_377_pp, + libzeth::groth16_snark>(); + ASSERT_TRUE(test_bls12_377); +} + +TEST(Groth16SnarkTest, TestProofReadWriteBytes) +{ + const bool test_alt_bn128 = libzeth::tests::proof_read_write_bytes_test< + libff::alt_bn128_pp, + libzeth::groth16_snark>(); + ASSERT_TRUE(test_alt_bn128); + const bool test_bls12_377 = + libzeth::tests::proving_key_read_write_bytes_test< + libff::bls12_377_pp, + libzeth::groth16_snark>(); + ASSERT_TRUE(test_bls12_377); +} + +} // namespace + +int main(int argc, char **argv) +{ + libff::alt_bn128_pp::init_public_params(); + libff::bls12_377_pp::init_public_params(); + ::testing::InitGoogleTest(&argc, argv); + + // Extract the test data destination dir, if passed on the command line. + if (argc > 1) { + g_output_dir = boost::filesystem::path(argv[1]); + } + + return RUN_ALL_TESTS(); +} diff --git a/libzeth/tests/snarks/pghr13/pghr13_api_handler_test.cpp b/libzeth/tests/snarks/pghr13/pghr13_api_handler_test.cpp index fbf9adfbc..e003eeede 100644 --- a/libzeth/tests/snarks/pghr13/pghr13_api_handler_test.cpp +++ b/libzeth/tests/snarks/pghr13/pghr13_api_handler_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -20,7 +20,7 @@ namespace TEST(PGHR13ApiHandlerTest, VerificationKeyEncodeDecode) { - verification_key_encode_decode_test>(); + verification_key_encode_decode_json_test>(); } TEST(PGHR13ApiHandlerTest, ProofEncodeDecode) @@ -33,7 +33,7 @@ TEST(PGHR13ApiHandlerTest, ProofEncodeDecode) G1::random_element()); libsnark::r1cs_primary_input dummy_inputs{ Fr::random_element(), Fr::random_element(), Fr::random_element()}; - extended_proof_encode_decode_test>( + extended_proof_encode_decode_json_test>( {std::move(dummy_proof), std::move(dummy_inputs)}); } diff --git a/libzeth/tests/snarks/pghr13/pghr13_snark_test.cpp b/libzeth/tests/snarks/pghr13/pghr13_snark_test.cpp new file mode 100644 index 000000000..1420d307f --- /dev/null +++ b/libzeth/tests/snarks/pghr13/pghr13_snark_test.cpp @@ -0,0 +1,65 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +#include "libzeth/snarks/pghr13/pghr13_snark.hpp" +#include "libzeth/tests/snarks/common_snark_tests.tcc" + +#include +#include +#include + +namespace +{ + +TEST(Pghr13SnarkTest, TestVerificationKeyReadWriteBytes) +{ + const bool test_alt_bn128 = + libzeth::tests::verification_key_read_write_bytes_test< + libff::alt_bn128_pp, + libzeth::pghr13_snark>(); + ASSERT_TRUE(test_alt_bn128); + + const bool test_bls12_377 = + libzeth::tests::verification_key_read_write_bytes_test< + libff::bls12_377_pp, + libzeth::pghr13_snark>(); + ASSERT_TRUE(test_bls12_377); +} + +TEST(Pghr13SnarkTest, TestProvingKeyReadWriteBytes) +{ + const bool test_alt_bn128 = + libzeth::tests::proving_key_read_write_bytes_test< + libff::alt_bn128_pp, + libzeth::pghr13_snark>(); + ASSERT_TRUE(test_alt_bn128); + const bool test_bls12_377 = + libzeth::tests::proving_key_read_write_bytes_test< + libff::bls12_377_pp, + libzeth::pghr13_snark>(); + ASSERT_TRUE(test_bls12_377); +} + +TEST(Pghr13SnarkTest, TestProofReadWriteBytes) +{ + const bool test_alt_bn128 = libzeth::tests::proof_read_write_bytes_test< + libff::alt_bn128_pp, + libzeth::pghr13_snark>(); + ASSERT_TRUE(test_alt_bn128); + const bool test_bls12_377 = + libzeth::tests::proving_key_read_write_bytes_test< + libff::bls12_377_pp, + libzeth::pghr13_snark>(); + ASSERT_TRUE(test_bls12_377); +} + +} // namespace + +int main(int argc, char **argv) +{ + libff::alt_bn128_pp::init_public_params(); + libff::bls12_377_pp::init_public_params(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/libzeth/zeth_constants.hpp b/libzeth/zeth_constants.hpp index 2490d5323..aca0c04f4 100644 --- a/libzeth/zeth_constants.hpp +++ b/libzeth/zeth_constants.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/commands/__init__.py b/mpc/commands/__init__.py index 6fb9229d2..f50145434 100644 --- a/mpc/commands/__init__.py +++ b/mpc/commands/__init__.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/commands/constants.py b/mpc/commands/constants.py index e173ecd55..198900695 100644 --- a/mpc/commands/constants.py +++ b/mpc/commands/constants.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/commands/contributors_from_csv b/mpc/commands/contributors_from_csv index abe1696e5..667e461de 100644 --- a/mpc/commands/contributors_from_csv +++ b/mpc/commands/contributors_from_csv @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/commands/create_keypair b/mpc/commands/create_keypair index 4cbfc850d..55293be15 100644 --- a/mpc/commands/create_keypair +++ b/mpc/commands/create_keypair @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/commands/generate_key b/mpc/commands/generate_key index c8a85ff32..ebbf3df6a 100644 --- a/mpc/commands/generate_key +++ b/mpc/commands/generate_key @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/commands/get_challenge b/mpc/commands/get_challenge index 285f5756a..fbde531ca 100644 --- a/mpc/commands/get_challenge +++ b/mpc/commands/get_challenge @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/commands/phase1_contribute b/mpc/commands/phase1_contribute index bb4482303..ae1f22d46 100644 --- a/mpc/commands/phase1_contribute +++ b/mpc/commands/phase1_contribute @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/commands/phase1_server b/mpc/commands/phase1_server index dd3d011fa..85476a785 100644 --- a/mpc/commands/phase1_server +++ b/mpc/commands/phase1_server @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/commands/phase2_contribute b/mpc/commands/phase2_contribute index b048c8e29..9a785ee3b 100644 --- a/mpc/commands/phase2_contribute +++ b/mpc/commands/phase2_contribute @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/commands/phase2_prepare b/mpc/commands/phase2_prepare index 77808de15..975d051c2 100644 --- a/mpc/commands/phase2_prepare +++ b/mpc/commands/phase2_prepare @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/commands/phase2_server b/mpc/commands/phase2_server index e40b8b402..dc6159b37 100644 --- a/mpc/commands/phase2_server +++ b/mpc/commands/phase2_server @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/commands/public_key b/mpc/commands/public_key index 7b1dfacac..26cbd50a6 100755 --- a/mpc/commands/public_key +++ b/mpc/commands/public_key @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/commands/public_key_operations.py b/mpc/commands/public_key_operations.py index ade721106..9f6c3f015 100644 --- a/mpc/commands/public_key_operations.py +++ b/mpc/commands/public_key_operations.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/commands/sign_contribution b/mpc/commands/sign_contribution index a402fc0e4..d91425cd2 100644 --- a/mpc/commands/sign_contribution +++ b/mpc/commands/sign_contribution @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/commands/upload_contribution b/mpc/commands/upload_contribution index 732ba7d10..87ece8d29 100644 --- a/mpc/commands/upload_contribution +++ b/mpc/commands/upload_contribution @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/__init__.py b/mpc/coordinator/__init__.py index 6fb9229d2..f50145434 100644 --- a/mpc/coordinator/__init__.py +++ b/mpc/coordinator/__init__.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/client.py b/mpc/coordinator/client.py index e2c264944..b7bedc4de 100644 --- a/mpc/coordinator/client.py +++ b/mpc/coordinator/client.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/contribute.py b/mpc/coordinator/contribute.py index 2e3b83315..3ccd35e45 100644 --- a/mpc/coordinator/contribute.py +++ b/mpc/coordinator/contribute.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/contributor_list.py b/mpc/coordinator/contributor_list.py index 6fdd75103..6a66ea008 100644 --- a/mpc/coordinator/contributor_list.py +++ b/mpc/coordinator/contributor_list.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/crypto.py b/mpc/coordinator/crypto.py index b5eeeb19c..74e6f0034 100644 --- a/mpc/coordinator/crypto.py +++ b/mpc/coordinator/crypto.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/icontributionhandler.py b/mpc/coordinator/icontributionhandler.py index 43656f7f2..8c5d18f17 100644 --- a/mpc/coordinator/icontributionhandler.py +++ b/mpc/coordinator/icontributionhandler.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/interval.py b/mpc/coordinator/interval.py index 57a85a650..1bdbd9ffa 100644 --- a/mpc/coordinator/interval.py +++ b/mpc/coordinator/interval.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/mpc_command.py b/mpc/coordinator/mpc_command.py index 86161d0ef..89f319da5 100644 --- a/mpc/coordinator/mpc_command.py +++ b/mpc/coordinator/mpc_command.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/null_contribution_handler.py b/mpc/coordinator/null_contribution_handler.py index 38303abf9..2e4312fb4 100644 --- a/mpc/coordinator/null_contribution_handler.py +++ b/mpc/coordinator/null_contribution_handler.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/phase1_contribution_handler.py b/mpc/coordinator/phase1_contribution_handler.py index e3e6ca56b..c61fcc405 100644 --- a/mpc/coordinator/phase1_contribution_handler.py +++ b/mpc/coordinator/phase1_contribution_handler.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/phase2_contribution_handler.py b/mpc/coordinator/phase2_contribution_handler.py index 1cc3c0e9d..9727800bb 100644 --- a/mpc/coordinator/phase2_contribution_handler.py +++ b/mpc/coordinator/phase2_contribution_handler.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/powersoftau_command.py b/mpc/coordinator/powersoftau_command.py index b9be08ad9..e9408a353 100644 --- a/mpc/coordinator/powersoftau_command.py +++ b/mpc/coordinator/powersoftau_command.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/powersoftau_process_command.py b/mpc/coordinator/powersoftau_process_command.py index 0d9a98f1f..fc7f514c1 100644 --- a/mpc/coordinator/powersoftau_process_command.py +++ b/mpc/coordinator/powersoftau_process_command.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/server.py b/mpc/coordinator/server.py index 42d56d93d..41d607bf3 100644 --- a/mpc/coordinator/server.py +++ b/mpc/coordinator/server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/server_configuration.py b/mpc/coordinator/server_configuration.py index e97c7b46b..735ddd237 100644 --- a/mpc/coordinator/server_configuration.py +++ b/mpc/coordinator/server_configuration.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/server_state.py b/mpc/coordinator/server_state.py index 67b404952..4474817a0 100644 --- a/mpc/coordinator/server_state.py +++ b/mpc/coordinator/server_state.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/coordinator/upload_utils.py b/mpc/coordinator/upload_utils.py index f2404f03d..59f5d17dc 100644 --- a/mpc/coordinator/upload_utils.py +++ b/mpc/coordinator/upload_utils.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/setup.py b/mpc/setup.py index c76072d7a..2965dc6dc 100644 --- a/mpc/setup.py +++ b/mpc/setup.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ @@ -20,7 +20,7 @@ setup( name='coordinator', - version='0.6', + version='0.7', description='MPC Coordinator for Zeth SRS', packages=find_packages(), install_requires=[ diff --git a/mpc/test/__init__.py b/mpc/test/__init__.py index 6fb9229d2..f50145434 100644 --- a/mpc/test/__init__.py +++ b/mpc/test/__init__.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/test/test_crypto.py b/mpc/test/test_crypto.py index 6842916d7..1c83a7383 100644 --- a/mpc/test/test_crypto.py +++ b/mpc/test/test_crypto.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc/test/test_server_state.py b/mpc/test/test_server_state.py index c77befe1e..69b2f10d1 100644 --- a/mpc/test/test_server_state.py +++ b/mpc/test/test_server_state.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc_tools/mpc_phase2/cli/mpc_common.cpp b/mpc_tools/mpc_phase2/cli/mpc_common.cpp index 2c2f6c5a4..bfbcf4709 100644 --- a/mpc_tools/mpc_phase2/cli/mpc_common.cpp +++ b/mpc_tools/mpc_phase2/cli/mpc_common.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc_tools/mpc_phase2/cli/mpc_common.hpp b/mpc_tools/mpc_phase2/cli/mpc_common.hpp index f02e5920f..0e2f4c427 100644 --- a/mpc_tools/mpc_phase2/cli/mpc_common.hpp +++ b/mpc_tools/mpc_phase2/cli/mpc_common.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc_tools/mpc_phase2/cli/mpc_create_keypair.cpp b/mpc_tools/mpc_phase2/cli/mpc_create_keypair.cpp index af39fee6d..bffdb648e 100644 --- a/mpc_tools/mpc_phase2/cli/mpc_create_keypair.cpp +++ b/mpc_tools/mpc_phase2/cli/mpc_create_keypair.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc_tools/mpc_phase2/cli/mpc_dummy_phase2.cpp b/mpc_tools/mpc_phase2/cli/mpc_dummy_phase2.cpp index 83b088c09..296f6bf81 100644 --- a/mpc_tools/mpc_phase2/cli/mpc_dummy_phase2.cpp +++ b/mpc_tools/mpc_phase2/cli/mpc_dummy_phase2.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc_tools/mpc_phase2/cli/mpc_linear_combination.cpp b/mpc_tools/mpc_phase2/cli/mpc_linear_combination.cpp index 37ec882cb..4dbc12d76 100644 --- a/mpc_tools/mpc_phase2/cli/mpc_linear_combination.cpp +++ b/mpc_tools/mpc_phase2/cli/mpc_linear_combination.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc_tools/mpc_phase2/cli/mpc_phase2_begin.cpp b/mpc_tools/mpc_phase2/cli/mpc_phase2_begin.cpp index 75e3b5ab2..69c2d84bb 100644 --- a/mpc_tools/mpc_phase2/cli/mpc_phase2_begin.cpp +++ b/mpc_tools/mpc_phase2/cli/mpc_phase2_begin.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc_tools/mpc_phase2/cli/mpc_phase2_contribute.cpp b/mpc_tools/mpc_phase2/cli/mpc_phase2_contribute.cpp index 4aae701ef..a802fda7a 100644 --- a/mpc_tools/mpc_phase2/cli/mpc_phase2_contribute.cpp +++ b/mpc_tools/mpc_phase2/cli/mpc_phase2_contribute.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc_tools/mpc_phase2/cli/mpc_phase2_verify_contribution.cpp b/mpc_tools/mpc_phase2/cli/mpc_phase2_verify_contribution.cpp index 9f112c26e..7e9739fc2 100644 --- a/mpc_tools/mpc_phase2/cli/mpc_phase2_verify_contribution.cpp +++ b/mpc_tools/mpc_phase2/cli/mpc_phase2_verify_contribution.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc_tools/mpc_phase2/cli/mpc_phase2_verify_transcript.cpp b/mpc_tools/mpc_phase2/cli/mpc_phase2_verify_transcript.cpp index 6ab8ba1f3..3bc7dd6fb 100644 --- a/mpc_tools/mpc_phase2/cli/mpc_phase2_verify_transcript.cpp +++ b/mpc_tools/mpc_phase2/cli/mpc_phase2_verify_transcript.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc_tools/mpc_phase2/mpc_client.cpp b/mpc_tools/mpc_phase2/mpc_client.cpp index b3e186965..4c6a3961c 100644 --- a/mpc_tools/mpc_phase2/mpc_client.cpp +++ b/mpc_tools/mpc_phase2/mpc_client.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc_tools/mpc_phase2/mpc_coord.cpp b/mpc_tools/mpc_phase2/mpc_coord.cpp index 34e2a1fd8..a78d17c8e 100644 --- a/mpc_tools/mpc_phase2/mpc_coord.cpp +++ b/mpc_tools/mpc_phase2/mpc_coord.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc_tools/mpc_phase2/test/mpc_test_cli.cpp b/mpc_tools/mpc_phase2/test/mpc_test_cli.cpp index 9cef02b72..fdb0caafd 100644 --- a/mpc_tools/mpc_phase2/test/mpc_test_cli.cpp +++ b/mpc_tools/mpc_phase2/test/mpc_test_cli.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/mpc_tools/pot_process/pot_process.cpp b/mpc_tools/pot_process/pot_process.cpp index a64ad5a3a..61b32c119 100644 --- a/mpc_tools/pot_process/pot_process.cpp +++ b/mpc_tools/pot_process/pot_process.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/proto/zeth/api/prover.proto b/proto/zeth/api/prover.proto index a614ba0eb..025ce9504 100644 --- a/proto/zeth/api/prover.proto +++ b/proto/zeth/api/prover.proto @@ -27,5 +27,5 @@ service Prover { rpc GetVerificationKey(google.protobuf.Empty) returns (VerificationKey) {} // Request a proof generation on the given inputs - rpc Prove(ProofInputs) returns (ExtendedProof) {} + rpc Prove(ProofInputs) returns (ExtendedProofAndPublicData) {} } diff --git a/proto/zeth/api/zeth_messages.proto b/proto/zeth/api/zeth_messages.proto index 0edd4248c..122c997ef 100644 --- a/proto/zeth/api/zeth_messages.proto +++ b/proto/zeth/api/zeth_messages.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package zeth_proto; +import "zeth/api/snark_messages.proto"; + message ZethNote { string apk = 1; // Hex string representing a int64 value @@ -34,3 +36,14 @@ message ProofInputs { string h_sig = 6; string phi = 7; } + +// The extended proof and related public data for the Zeth statement using ProofInputs data +message ExtendedProofAndPublicData { + // The extended proof (with single public input - the hash of public_data). + ExtendedProof extended_proof = 1; + + // The public data (public inputs to the Zeth statement). Each element in + // the array is a hex-encoded member of the scalar field for the pairing + // being used. + repeated string public_data = 2; +} diff --git a/prover_server/CMakeLists.txt b/prover_server/CMakeLists.txt index 548ccbd83..e0304b04d 100644 --- a/prover_server/CMakeLists.txt +++ b/prover_server/CMakeLists.txt @@ -44,30 +44,20 @@ add_executable( ${PROVER_SERVER_SOURCE} ${GRPC_SRCS} ) -if ("${STATIC_BUILD}") + +if (NOT APPLE) find_package(PkgConfig REQUIRED) pkg_check_modules(GRPC REQUIRED IMPORTED_TARGET grpc++) +endif() - target_link_libraries( - prover_server - - zeth - ${Boost_SYSTEM_LIBRARY} - ${Boost_FILESYSTEM_LIBRARY} - ${Boost_PROGRAM_OPTIONS_LIBRARY} - ${GRPC_LIBRARIES} - gRPC::grpc++_reflection - protobuf::libprotobuf - ) -else() - target_link_libraries( - prover_server +target_link_libraries( + prover_server - zeth - ${Boost_SYSTEM_LIBRARY} - ${Boost_FILESYSTEM_LIBRARY} - ${Boost_PROGRAM_OPTIONS_LIBRARY} - gRPC::grpc++_reflection - protobuf::libprotobuf + zeth + ${Boost_SYSTEM_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${GRPC_LIBRARIES} + gRPC::grpc++_reflection + protobuf::libprotobuf ) -endif() diff --git a/prover_server/prover_server.cpp b/prover_server/prover_server.cpp index e804e8e3d..42d4d7b75 100644 --- a/prover_server/prover_server.cpp +++ b/prover_server/prover_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ @@ -7,6 +7,7 @@ #include "libzeth/core/utils.hpp" #include "libzeth/serialization/proto_utils.hpp" #include "libzeth/serialization/r1cs_serialization.hpp" +#include "libzeth/serialization/r1cs_variable_assignment_serialization.hpp" #include "libzeth/zeth_constants.hpp" #include "zeth_config.h" @@ -55,7 +56,10 @@ static snark::keypair load_keypair(const boost::filesystem::path &keypair_file) keypair_file.c_str(), std::ios_base::in | std::ios_base::binary); in_s.exceptions( std::ios_base::eofbit | std::ios_base::badbit | std::ios_base::failbit); - return snark::keypair_read_bytes(in_s); + + snark::keypair keypair; + snark::keypair_read_bytes(keypair, in_s); + return keypair; } static void write_keypair( @@ -67,19 +71,55 @@ static void write_keypair( snark::keypair_write_bytes(keypair, out_s); } +static void write_proving_key( + const typename snark::proving_key &pk, + const boost::filesystem::path &pk_file) +{ + std::ofstream out_s( + pk_file.c_str(), std::ios_base::out | std::ios_base::binary); + snark::proving_key_write_bytes(pk, out_s); +} + +static void write_verification_key( + const typename snark::verification_key &vk, + const boost::filesystem::path &vk_file) +{ + std::ofstream out_s( + vk_file.c_str(), std::ios_base::out | std::ios_base::binary); + snark::verification_key_write_bytes(vk, out_s); +} + static void write_constraint_system( const circuit_wrapper &prover, const boost::filesystem::path &r1cs_file) { std::ofstream r1cs_stream(r1cs_file.c_str()); - libzeth::r1cs_write_json(prover.get_constraint_system(), r1cs_stream); + libzeth::r1cs_write_json(prover.get_constraint_system(), r1cs_stream); } -static void write_ext_proof_to_file( +static void write_extproof_to_json_file( const libzeth::extended_proof &ext_proof, - boost::filesystem::path proof_path) + const boost::filesystem::path &proof_path) { - std::ofstream os(proof_path.c_str()); - ext_proof.write_json(os); + std::ofstream out_s(proof_path.c_str()); + ext_proof.write_json(out_s); +} + +static void write_proof_to_file( + const typename snark::proof &proof, + const boost::filesystem::path &proof_path) +{ + std::ofstream out_s( + proof_path.c_str(), std::ios_base::out | std::ios_base::binary); + snark::proof_write_bytes(proof, out_s); +} + +static void write_assignment_to_file( + const std::vector &assignment, + const boost::filesystem::path &assignment_path) +{ + std::ofstream out_s( + assignment_path.c_str(), std::ios_base::out | std::ios_base::binary); + libzeth::r1cs_variable_assignment_write_bytes(assignment, out_s); } /// The prover_server class inherits from the Prover service @@ -88,20 +128,37 @@ static void write_ext_proof_to_file( class prover_server final : public zeth_proto::Prover::Service { private: - circuit_wrapper prover; + circuit_wrapper &prover; // The keypair is the result of the setup. Store a copy internally. snark::keypair keypair; + // Optional file to write proofs into (for debugging). + boost::filesystem::path extproof_json_output_file; + // Optional file to write proofs into (for debugging). boost::filesystem::path proof_output_file; + // Optional file to write primary input data into (for debugging). + boost::filesystem::path primary_output_file; + + // Optional file to write full assignments into (for debugging). + boost::filesystem::path assignment_output_file; + public: explicit prover_server( circuit_wrapper &prover, const snark::keypair &keypair, - const boost::filesystem::path &proof_output_file) - : prover(prover), keypair(keypair), proof_output_file(proof_output_file) + const boost::filesystem::path &extproof_json_output_file, + const boost::filesystem::path &proof_output_file, + const boost::filesystem::path &primary_output_file, + const boost::filesystem::path &assignment_output_file) + : prover(prover) + , keypair(keypair) + , extproof_json_output_file(extproof_json_output_file) + , proof_output_file(proof_output_file) + , primary_output_file(primary_output_file) + , assignment_output_file(assignment_output_file) { } @@ -141,7 +198,7 @@ class prover_server final : public zeth_proto::Prover::Service grpc::Status Prove( grpc::ServerContext *, const zeth_proto::ProofInputs *proof_inputs, - zeth_proto::ExtendedProof *proof) override + zeth_proto::ExtendedProofAndPublicData *proof_and_public_data) override { std::cout << "[ACK] Received the request to generate a proof" << std::endl; @@ -150,6 +207,8 @@ class prover_server final : public zeth_proto::Prover::Service // Parse received message to feed to the prover try { + // TODO: Factor this into more maintainable smaller functions + Field root = libzeth::base_field_element_from_hex( proof_inputs->mk_root()); libzeth::bits64 vpub_in = @@ -204,6 +263,8 @@ class prover_server final : public zeth_proto::Prover::Service std::cout << "[DEBUG] Data parsed successfully" << std::endl; std::cout << "[DEBUG] Generating the proof..." << std::endl; + + std::vector public_data; libzeth::extended_proof ext_proof = this->prover.prove( root, joinsplit_inputs, @@ -212,20 +273,47 @@ class prover_server final : public zeth_proto::Prover::Service vpub_out, h_sig_in, phi_in, - this->keypair.pk); + this->keypair.pk, + public_data); - std::cout << "[DEBUG] Displaying the extended proof" << std::endl; + std::cout << "[DEBUG] Displaying extended proof and public data\n"; ext_proof.write_json(std::cout); + for (const Field &f : public_data) { + std::cout << libzeth::base_field_element_to_hex(f) << "\n"; + } // Write a copy of the proof for debugging. + if (!extproof_json_output_file.empty()) { + std::cout << "[DEBUG] Writing extended proof (JSON) to " + << extproof_json_output_file << "\n"; + write_extproof_to_json_file( + ext_proof, extproof_json_output_file); + } if (!proof_output_file.empty()) { - std::cout << "[DEBUG] Writing extended proof to " - << proof_output_file << "\n"; - write_ext_proof_to_file(ext_proof, proof_output_file); + std::cout << "[DEBUG] Writing proof to " << proof_output_file + << "\n"; + write_proof_to_file(ext_proof.get_proof(), proof_output_file); + } + if (!primary_output_file.empty()) { + std::cout << "[DEBUG] Writing primary input to " + << primary_output_file << "\n"; + write_assignment_to_file( + ext_proof.get_primary_inputs(), primary_output_file); + } + if (!assignment_output_file.empty()) { + std::cout << "[DEBUG] WARNING! Writing assignment to " + << assignment_output_file << "\n"; + write_assignment_to_file( + prover.get_last_assignment(), assignment_output_file); } std::cout << "[DEBUG] Preparing response..." << std::endl; - api_handler::extended_proof_to_proto(ext_proof, proof); + api_handler::extended_proof_to_proto( + ext_proof, proof_and_public_data->mutable_extended_proof()); + for (size_t i = 0; i < public_data.size(); ++i) { + proof_and_public_data->add_public_data( + libzeth::base_field_element_to_hex(public_data[i])); + } } catch (const std::exception &e) { std::cout << "[ERROR] " << e.what() << std::endl; @@ -257,7 +345,7 @@ std::string get_server_version() void display_server_start_message() { std::string copyright = - "Copyright (c) 2015-2020 Clearmatics Technologies Ltd"; + "Copyright (c) 2015-2021 Clearmatics Technologies Ltd"; std::string license = "SPDX-License-Identifier: LGPL-3.0+"; std::string project = "R&D Department: PoC for Zerocash on Ethereum/Autonity"; @@ -278,12 +366,21 @@ void display_server_start_message() static void RunServer( circuit_wrapper &prover, const typename snark::keypair &keypair, - const boost::filesystem::path &proof_output_file) + const boost::filesystem::path &extproof_json_output_file, + const boost::filesystem::path &proof_output_file, + const boost::filesystem::path &primary_output_file, + const boost::filesystem::path &assignment_output_file) { // Listen for incoming connections on 0.0.0.0:50051 std::string server_address("0.0.0.0:50051"); - prover_server service(prover, keypair, proof_output_file); + prover_server service( + prover, + keypair, + extproof_json_output_file, + proof_output_file, + primary_output_file, + assignment_output_file); grpc::ServerBuilder builder; @@ -319,9 +416,29 @@ int main(int argc, char **argv) po::value(), "file in which to export the r1cs (in json format)"); options.add_options()( - "proof-output,p", + "proving-key-output", + po::value(), + "write proving key to file (if generated)"); + options.add_options()( + "verification-key-output", + po::value(), + "write verification key to file (if generated)"); + options.add_options()( + "extproof-json-output", + po::value(), + "(DEBUG) write generated extended proofs (JSON) to file"); + options.add_options()( + "proof-output", + po::value(), + "(DEBUG) write generated proofs to file"); + options.add_options()( + "primary-output", + po::value(), + "(DEBUG) write primary input to file"); + options.add_options()( + "assignment-output", po::value(), - "(DEBUG) file to write generated proofs into"); + "(DEBUG) write full assignment to file (INSECURE!)"); auto usage = [&]() { std::cout << "Usage:" @@ -334,7 +451,12 @@ int main(int argc, char **argv) boost::filesystem::path keypair_file; boost::filesystem::path r1cs_file; + boost::filesystem::path proving_key_output_file; + boost::filesystem::path verification_key_output_file; + boost::filesystem::path extproof_json_output_file; boost::filesystem::path proof_output_file; + boost::filesystem::path primary_output_file; + boost::filesystem::path assignment_output_file; try { po::variables_map vm; po::store( @@ -349,10 +471,30 @@ int main(int argc, char **argv) if (vm.count("r1cs")) { r1cs_file = vm["r1cs"].as(); } + if (vm.count("proving-key-output")) { + proving_key_output_file = + vm["proving-key-output"].as(); + } + if (vm.count("verification-key-output")) { + verification_key_output_file = + vm["verification-key-output"].as(); + } + if (vm.count("extproof-json-output")) { + extproof_json_output_file = + vm["extproof-json-output"].as(); + } if (vm.count("proof-output")) { proof_output_file = vm["proof-output"].as(); } + if (vm.count("primary-output")) { + primary_output_file = + vm["primary-output"].as(); + } + if (vm.count("assignment-output")) { + assignment_output_file = + vm["assignment-output"].as(); + } } catch (po::error &error) { std::cerr << " ERROR: " << error.what() << std::endl; usage(); @@ -370,13 +512,16 @@ int main(int argc, char **argv) } // Inititalize the curve parameters - std::cout << "[INFO] Init params" << std::endl; + std::cout << "[INFO] Init params (" << libzeth::pp_name() << ")\n"; pp::init_public_params(); // If the keypair file exists, load and use it, otherwise generate a new // keypair and write it to the file. circuit_wrapper prover; - snark::keypair keypair = [&keypair_file, &prover]() { + snark::keypair keypair = [&keypair_file, + &proving_key_output_file, + &verification_key_output_file, + &prover]() { if (boost::filesystem::exists(keypair_file)) { std::cout << "[INFO] Loading keypair: " << keypair_file << "\n"; return load_keypair(keypair_file); @@ -387,6 +532,18 @@ int main(int argc, char **argv) const snark::keypair keypair = prover.generate_trusted_setup(); std::cout << "[INFO] Writing new keypair to " << keypair_file << "\n"; write_keypair(keypair, keypair_file); + + if (!proving_key_output_file.empty()) { + std::cout << "[DEBUG] Writing separate proving key to " + << proving_key_output_file << "\n"; + write_proving_key(keypair.pk, proving_key_output_file); + } + if (!verification_key_output_file.empty()) { + std::cout << "[DEBUG] Writing separate verification key to " + << verification_key_output_file << "\n"; + write_verification_key(keypair.vk, verification_key_output_file); + } + return keypair; }(); @@ -398,6 +555,12 @@ int main(int argc, char **argv) } std::cout << "[INFO] Setup successful, starting the server..." << std::endl; - RunServer(prover, keypair, proof_output_file); + RunServer( + prover, + keypair, + extproof_json_output_file, + proof_output_file, + primary_output_file, + assignment_output_file); return 0; } diff --git a/scripts/ci b/scripts/ci index a7419a899..73a62fbd3 100755 --- a/scripts/ci +++ b/scripts/ci @@ -4,9 +4,24 @@ platform=`uname` echo platform=${platform} echo "running against commit: "`git log --oneline --no-decorate -n 1` +# Include the CI utils functions +. scripts/ci_utils.sh + set -x set -e +function _setup_client() { + pushd client + python3 -m venv env + . env/bin/activate + pip install --upgrade pip --progress-bar off + pip install --upgrade setuptools wheel + make setup + + deactivate + popd +} + function check_format() { scripts/format git diff --no-ext-diff | head -n 20 > format_errors @@ -22,20 +37,34 @@ function check_format() { } function check_contracts() { + # Setup the zeth_contracts dir + ganache_setup + + # Run checks in zeth_contracts pushd zeth_contracts - npm config set python python2.7 - npm install --unsafe-perm npm run check + popd # zeth_contracts + + # Run contract tests (in python) + _setup_client + + ganache_start + + pushd client + . env/bin/activate + make test_contracts + deactivate popd + + ganache_stop } function check_client() { + _setup_client + pushd client - python3 -m venv env . env/bin/activate - pip install --upgrade pip --progress-bar off - pip install --upgrade setuptools wheel - make setup + make check deactivate @@ -49,8 +78,6 @@ function check_cpp() { # Configure and run clang-tidy cppcheck mkdir -p build pushd build - # cmake -DUSE_CPP_CHECK=ON -DUSE_CLANG_TIDY=ON .. - # make VERBOSE=1 clang-tidy -j 5 cmake -DUSE_CPP_CHECK=ON .. make VERBOSE=1 cppcheck -j 5 popd @@ -83,7 +110,51 @@ function mpc_tests() { scripts/test_mpc_server_phase2 } +function prover_tests() { + + # Native code is built. Setup client and ganache. + ganache_setup + _setup_client + + # Start servers + ganache_start + prover_server_start + + # Enter client env and run prover test scripts + . client/env/bin/activate + python -m test_commands.test_ether_mixing GROTH16 + python -m test_commands.test_erc_token_mixing GROTH16 + deactivate + + # Stop servers + prover_server_stop + ganache_stop +} + +function integration_tests() { + + # Native code is built. Setup client and ganache. + ganache_setup + _setup_client + + # Start servers + ganache_start + prover_server_start + + # Enter client env and run client test script + . client/env/bin/activate + ./scripts/test_zeth_cli + deactivate + + # Stop servers + prover_server_stop + ganache_stop +} + function build() { + + cpp_build_setup + # Additional compilation flags cxx_flags="-Werror" @@ -101,9 +172,14 @@ function build() { cmake_flags="-DCMAKE_BUILD_TYPE=${CI_CONFIG} -DZETH_SNARK=${CI_ZKSNARK}" cmake_flags="${cmake_flags} -DZETH_CURVE=${CI_CURVE}" - if ! [ "${full_build}" == "1" ] ; then + # Switch off slow tests unless CI_FULL_TESTS == 1 + if ! [ "${CI_FULL_TESTS}" == "1" ] ; then cmake_flags="${cmake_flags} -DFAST_TESTS_ONLY=ON" fi + # Use ccache if available + if (which ccache) ; then + cmake_flags="${cmake_flags} -DCMAKE_C_COMPILER_LAUNCHER=ccache" + fi # Build and run unit tests . setup_env.sh @@ -118,37 +194,29 @@ function build() { CTEST_OUTPUT_ON_FAILURE=1 make -j 2 check cd .. - mpc_tests -} + if [ "${CI_MPC_TESTS}" == "1" ] ; then + mpc_tests + fi -function ci_setup() { + if [ "${CI_PROVER_TESTS}" == "1" ] ; then + prover_tests + fi - if [ "${platform}" == "Darwin" ] ; then - # Some of these commands can fail (if packages are already installed, - # etc), hence the `|| echo`. - brew update || echo - brew install \ - gmp \ - grpc \ - protobuf \ - boost \ - openssl \ - cmake \ - libtool \ - autoconf \ - automake \ - || echo + if [ "${CI_INTEGRATION_TESTS}" == "1" ] ; then + integration_tests fi +} + +function ci_setup() { # The base docker image we use is Alpine # See: https://www.alpinelinux.org/ if [ "${platform}" == "Linux" ] ; then if (which apk) ; then - apk add --update npm - # `py3-virtualenv` depends on `python3` - # which installs the latest version of python3 + # `py3-virtualenv` depends on `python3` which installs the latest + # version of python3. # See: https://pkgs.alpinelinux.org/package/edge/main/x86/python3 # https://build.alpinelinux.org/buildlogs/build-edge-x86/main/python3/python3-3.8.2-r6.log apk add \ @@ -166,22 +234,18 @@ function ci_setup() { fi } - ci_task=$1 -full_build=$2 -if [ "${full_build}" == "" ] ; then - if [ "${CI_EVENT_NAME}" == "pull_request" ] ; then - full_build=1 - fi -fi echo ci_task = ${ci_task} -echo full_build=${full_build} echo CI_CONFIG=${CI_CONFIG} echo CI_ZKSNARK=${CI_ZKSNARK} echo CI_CURVE=${CI_CURVE} echo CI_CHECK_FORMAT=${CI_CHECK_FORMAT} echo CI_EVENT_NAME=${CI_EVENT_NAME} +echo CI_FULL_TEST=${CI_FULL_TESTS} +echo CI_MPC_TESTS=${CI_MPC_TESTS} +echo CI_PROVER_TESTS=${CI_PROVER_TESTS} +echo CI_INTEGRATION_TESTS=${CI_INTEGRATION_TESTS} if [ "${CI_CHECK_FORMAT}" == "1" ] ; then check_format @@ -207,7 +271,10 @@ if [ "${CI_USE_DOCKER}" == "1" ] ; then --env CI_CONFIG=${CI_CONFIG} \ --env CI_ZKSNARK=${CI_ZKSNARK} \ --env CI_CURVE=${CI_CURVE} \ - zeth-dev:latest $0 ${ci_task} ${full_build} + --env CI_FULL_TESTS=${CI_FULL_TESTS} \ + --env CI_MPC_TESTS=${CI_MPC_TESTS} \ + --env CI_INTEGRATION_TESTS=${CI_INTEGRATION_TESTS} \ + zeth-dev:latest $0 ${ci_task} else ci_setup ${ci_task} diff --git a/scripts/ci_utils.sh b/scripts/ci_utils.sh new file mode 100644 index 000000000..751994531 --- /dev/null +++ b/scripts/ci_utils.sh @@ -0,0 +1,161 @@ +# Utility functions for CI tasks. +# +# All functions expect to be executed the root directory of the repository, and +# will exit with this as the current directory. + +# Launch a server in the background and wait for it to be ready, recording the +# pid in a file. +# +# 1 - server cmd +# 2 - server check cmd +# 3 - pid file +# 4 - stdout file +function server_start() { + $1 > $4 & + pid=$! + echo pid is ${pid} + echo ${pid} > $3 + + # Wait for prover_server to be active + while ! $2 ; do + echo "server_start: waiting for $1 ..." + sleep 1 + done + + echo "server_start: $1 is ACTIVE" +} + +# Stop a background server, given a name and pid file +# +# 1 - server name +# 2 - pid file +function server_stop() { + if ! [ -e $2 ] ; then + echo "server_stop: no PID file for $1" + return 1 + fi + + pid=`cat $2` + while (kill "${pid}") ; do + sleep 0.5 + done + rm $2 + echo "server_stop: $1 STOPPED" +} + +# +# GANACHE +# + +function ganache_setup() { + if [ "${platform}" == "Linux" ] ; then + if (which apk) ; then + apk add --update npm + fi + fi + + pushd zeth_contracts + npm config set python python2.7 + npm config set engine-strict true + npm config set unsafe-perm true + npm install --unsafe-perm + popd +} + +function ganache_is_active() { + curl -sf \ + -H "Content-Type: application/json" \ + -X POST \ + --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[]}' \ + http://localhost:8545 +} + +function ganache_start() { + pushd zeth_contracts + server_start \ + "npm run testrpc" \ + ganache_is_active \ + ganache.pid \ + ganache.stdout + popd +} + +function ganache_stop() { + pushd zeth_contracts + server_stop ganache ganache.pid + popd +} + +# +# PROVER SERVER +# +# These functions assume that the prover_server has been built in the build +# directory. + +function prover_server_is_active() { + # Assume the client env is active + zeth get-verification-key +} + +function prover_server_start() { + # Requires the client env (for prover_server_is_active) + . client/env/bin/activate + pushd build + + server_start \ + ./prover_server/prover_server \ + prover_server_is_active \ + prover_server.pid \ + prover_server.stdout + + popd # build + deactivate +} + +function prover_server_stop() { + pushd build + server_stop prover_server prover_server.pid + popd # build +} + +# +# DEPENDENCIES +# + +function cpp_build_setup() { + # Extra deps for native builds + + if [ "${platform}" == "Darwin" ] ; then + # Some of these commands can fail (if packages are already installed, + # etc), hence the `|| echo`. + brew update || echo + brew install \ + gmp \ + grpc \ + protobuf \ + boost \ + openssl \ + cmake \ + libtool \ + autoconf \ + automake \ + || echo + fi + + if [ "${platform}" == "Linux" ] ; then + if (which apk) ; then + # Packages already available in Docker build + echo -n # null op required for syntax + else + sudo apt install \ + libboost-dev \ + libboost-system-dev \ + libboost-filesystem-dev \ + libboost-program-options-dev \ + libgmp-dev \ + libprocps-dev \ + libxslt1-dev \ + pkg-config + fi + fi +} diff --git a/scripts/install_grpc b/scripts/install_grpc new file mode 100755 index 000000000..1dfbcffe8 --- /dev/null +++ b/scripts/install_grpc @@ -0,0 +1,77 @@ +#!/usr/bin/env bash + +# This script is only intended to run on the CI machines. Not for local +# development. + +# Expect 3 arguments, all non-empty. +if [ "$#" -ne 3 ] || [ "" == "$1" ] || [ "" == "$2" ] || [ "" == "$3" ] ; then + echo "error: invalid arguments" + echo "Usage: $0 " + echo "" + exit 1 +fi + +set -e +set -x + +INSTALL_DIR=$1 +VERSION=$2 +BUILD_DIR=$3 + +# This script executes in one of 2 modes on the CI: +# +# - No pre-existing build directory (DO_BUILD=1). +# +# - Pre-existing build directory, cached a previous successful build +# (DO_BUILD=0). In this case, installation can be performed directly. + +if [ -d "${BUILD_DIR}" ] ; then + DO_BUILD=0 +else + DO_BUILD=1 +fi + +mkdir -p ${BUILD_DIR} +pushd ${BUILD_DIR} + + # Clone repo and submodules (if DO_BUILD == 1) + if [ "${DO_BUILD}" == "1" ] ; then + git clone --depth 1 -b ${VERSION} https://github.com/grpc/grpc . + git submodule update --depth 1 --init --recursive + fi + + # Install protobuf + pushd third_party/protobuf + if [ "${DO_BUILD}" == "1" ] ; then + [ -e ./configure ] || ./autogen.sh + DIST_LANG=cpp ./configure --prefix ${INSTALL_DIR} + make -j $(($(nproc)+1)) + fi + sudo make install + popd # third_party/protobuf + + # Install grpc + mkdir -p build + pushd build + if [ "${DO_BUILD}" == "1" ] ; then + cmake \ + -DCMAKE_PREFIX_PATH=${INSTALL_DIR} \ + -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \ + -DCMAKE_BUILD_TYPE=Release \ + -DgRPC_INSTALL=ON \ + -DgRPC_BUILD_TESTS=OFF \ + -DBUILD_TESTING=OFF \ + -DgRPC_SSL_PROVIDER=package \ + -DgRPC_PROTOBUF_PROVIDER=package \ + -DgRPC_BUILD_GRPC_CSHARP_PLUGIN=OFF \ + -DgRPC_BUILD_GRPC_NODE_PLUGIN=OFF \ + -DgRPC_BUILD_GRPC_OBJECTIVE_C_PLUGIN=OFF \ + -DgRPC_BUILD_GRPC_PHP_PLUGIN=OFF \ + -DgRPC_BUILD_GRPC_RUBY_PLUGIN=OFF \ + .. + make -j"$(($(nproc)+1))" + fi + sudo make install + popd # build + +popd # ${INSTALL_DEST} diff --git a/scripts/mimc_constraints.sage b/scripts/mimc_constraints.sage index af5f8c8dc..8e3702af7 100644 --- a/scripts/mimc_constraints.sage +++ b/scripts/mimc_constraints.sage @@ -52,6 +52,8 @@ def output_valid_configs_and_constraints(r): for t in range(2, 22): e = (1 << t) - 1 output_valid_config_and_constraints(r, log_2_r, e) + e = (1 << t) + 1 + output_valid_config_and_constraints(r, log_2_r, e) # TODO: determine if these value are valid # output_valid_config_and_constraints(r, log_2_r, 11) @@ -61,6 +63,21 @@ def output_valid_configs_and_constraints(r): # output_valid_config_and_constraints(r, log_2_r, 23) +# BW6-761 +print("BW6-761:") +output_valid_configs_and_constraints( + r=258664426012969094010652733694893533536393512754914660539884262666720468348340822774968888139573360124440321458177) + +# MNT4 +print("MNT4:") +output_valid_configs_and_constraints( + r=475922286169261325753349249653048451545124878552823515553267735739164647307408490559963137) + +# MNT6 +print("MNT6:") +output_valid_configs_and_constraints( + r=475922286169261325753349249653048451545124879242694725395555128576210262817955800483758081) + # BLS12-377 print("BLS12-377:") output_valid_configs_and_constraints( diff --git a/scripts/mimc_round_constants_generation.py b/scripts/mimc_round_constants_generation.py index aacb70f5f..02ddbbe23 100644 --- a/scripts/mimc_round_constants_generation.py +++ b/scripts/mimc_round_constants_generation.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# Copyright (c) 2015-2021 Clearmatics Technologies Ltd # # SPDX-License-Identifier: LGPL-3.0+ diff --git a/scripts/test_mpc_common.sh b/scripts/test_mpc_common.sh index 4f66f6d49..686869723 100644 --- a/scripts/test_mpc_common.sh +++ b/scripts/test_mpc_common.sh @@ -64,7 +64,7 @@ function prepare_server_common() { # TLS server certs if ! [ -e ${SERVER_KEY} ] || ! [ -e ${SERVER_CERT} ] ; then echo TLS certificate ... - KEY_BITS=1024 + KEY_BITS=4096 cp /etc/ssl/openssl.cnf openssl.tmp.cnf echo "[v3_req]" >> openssl.tmp.cnf echo "subjectAltName=DNS:localhost" >> openssl.tmp.cnf @@ -93,9 +93,19 @@ function start_server_common() { $2 > server.stdout & echo $! > server.pid + x=1 while ! $3 ; do + if [ $x == 10 ] ; then + echo "FAILED TO LAUNCH" + exit 1 + fi + echo "TEST: waiting for server to start ..." sleep 1 + + x=$(( $x + 1 )) + echo "TEST: retrying ($x)" + done echo "TEST: server up (pid: "`cat server.pid`")" popd diff --git a/scripts/test_zeth_cli b/scripts/test_zeth_cli index 667d023eb..439e88d9f 100755 --- a/scripts/test_zeth_cli +++ b/scripts/test_zeth_cli @@ -38,8 +38,16 @@ copy_deployment_info deployer charlie # Alice deposits 200 and sends 100 to Bob pushd alice +# Test getting vk from prover server +[ -e vk.json ] || zeth get-verification-key --vk-out vk.json + alice_pk=`cat zeth-address.pub` if ! [ -e notes/state_zeth ] ; then + # Uncomment the following command to create dispatch call data for this transaction + # zeth mix --wait --vin 200 --out ${alice_pk},200 \ + # --for-dispatch-call \ + # --dump-signing-keypair deposit_otsig_keypair.json \ + # --dump-parameters deposit_mixparams.json zeth mix --wait --vin 200 --out ${alice_pk},200 fi note_id=`zeth ls-notes | tail -n 1 | grep -oe '^[A-Za-z0-9]\+'` diff --git a/verifier/CMakeLists.txt b/verifier/CMakeLists.txt new file mode 100644 index 000000000..58a39be0e --- /dev/null +++ b/verifier/CMakeLists.txt @@ -0,0 +1,14 @@ + +find_package(Boost REQUIRED COMPONENTS system filesystem program_options) + +file(GLOB_RECURSE VERIFIER_SOURCE **.?pp **.tcc) +add_executable(verifier ${VERIFIER_SOURCE}) +target_include_directories(verifier PRIVATE SYSTEM ${Boost_INCLUDE_DIR}) +target_link_libraries( + verifier + + zeth + ${Boost_SYSTEM_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ) diff --git a/verifier/verifier.cpp b/verifier/verifier.cpp new file mode 100644 index 000000000..e0889bf1f --- /dev/null +++ b/verifier/verifier.cpp @@ -0,0 +1,163 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +#include "libzeth/serialization/r1cs_variable_assignment_serialization.hpp" +#include "libzeth/snarks/groth16/groth16_snark.hpp" +#include "libzeth/snarks/pghr13/pghr13_snark.hpp" + +#include +#include +#include +#include +#include +#include + +namespace po = boost::program_options; + +static std::ifstream open_file(const std::string &filename) +{ + std::ifstream in_s( + filename.c_str(), std::ios_base::in | std::ios_base::binary); + in_s.exceptions( + std::ios_base::eofbit | std::ios_base::badbit | std::ios_base::failbit); + return in_s; +} + +template +int verifier_main( + const std::string &vk_file, + const std::string &primary_input_file, + const std::string &proof_file) +{ + ppT::init_public_params(); + libff::inhibit_profiling_info = true; + libff::inhibit_profiling_counters = true; + + typename snarkT::verification_key verification_key; + { + std::ifstream in_s = open_file(vk_file); + snarkT::verification_key_read_bytes(verification_key, in_s); + } + + libsnark::r1cs_primary_input> primary_input; + { + std::ifstream in_s = open_file(primary_input_file); + libzeth::r1cs_variable_assignment_read_bytes(primary_input, in_s); + } + + typename snarkT::proof proof; + { + std::ifstream in_s = open_file(proof_file); + snarkT::proof_read_bytes(proof, in_s); + } + + if (!snarkT::verify(primary_input, proof, verification_key)) { + std::cout << "verification failed.\n"; + return 1; + } + + return 0; +} + +template +int verifier_resolve_snark( + const std::string &vk_file, + const std::string &primary_input_file, + const std::string &proof_file, + const std::string &snark) +{ + if (snark == "groth16") { + return verifier_main>( + vk_file, primary_input_file, proof_file); + } else if (snark == "pghr13") { + return verifier_main>( + vk_file, primary_input_file, proof_file); + } + + throw po::error("unrecognized snark"); +} + +int verifier_resolve_curve( + const std::string &vk_file, + const std::string &primary_input_file, + const std::string &proof_file, + const std::string &curve, + const std::string &snark) +{ + if (curve == "alt-bn128") { + return verifier_resolve_snark( + vk_file, primary_input_file, proof_file, snark); + } else if (curve == "bls12-377") { + return verifier_resolve_snark( + vk_file, primary_input_file, proof_file, snark); + } else if (curve == "bw6-761") { + return verifier_resolve_snark( + vk_file, primary_input_file, proof_file, snark); + } + + throw po::error("unrecognized curve"); +} + +int main(int argc, char **argv) +{ + // Options + po::options_description options("Options"); + options.add_options()( + "curve,c", + po::value(), + "Curve: alt-bn128, bls12-377 or bw6-761"); + options.add_options()( + "snark,s", po::value(), "Snark: groth16 or pghr13"); + + po::options_description all_options(options); + all_options.add_options()( + "vk_file", po::value(), "Verification key file"); + all_options.add_options()( + "primary_input_file", po::value(), "Proof file"); + all_options.add_options()( + "proof_file", po::value(), "Proof file"); + + po::positional_options_description pos; + pos.add("vk_file", 1); + pos.add("primary_input_file", 1); + pos.add("proof_file", 1); + + try { + po::parsed_options parsed = po::command_line_parser{argc, argv} + .options(all_options) + .positional(pos) + .run(); + po::variables_map vm; + po::store(parsed, vm); + + if (vm.count("vk_file") == 0) { + throw po::error("vk_file not specified"); + } + if (vm.count("primary_input_file") == 0) { + throw po::error("primary_input_file not specified"); + } + if (vm.count("proof_file") == 0) { + throw po::error("proof_file not specified"); + } + std::string vk_file = vm["vk_file"].as(); + std::string proof_file = vm["proof_file"].as(); + std::string primary_input_file = + vm["primary_input_file"].as(); + std::string curve = + vm.count("curve") ? vm["curve"].as() : "alt-bn128"; + std::string snark = + vm.count("snark") ? vm["snark"].as() : "groth16"; + + verifier_resolve_curve( + vk_file, primary_input_file, proof_file, curve, snark); + } catch (po::error &error) { + std::cerr << " ERROR: " << error.what() << "\n"; + std::cout + << "Usage:\n" + << " " << argv[0] + << " [] \n\n" + << options << std::endl; + return 1; + } +} diff --git a/zeth_config.h.in b/zeth_config.h.in index d453ba895..7852b46af 100644 --- a/zeth_config.h.in +++ b/zeth_config.h.in @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ diff --git a/zeth_contracts/.solhint.json b/zeth_contracts/.solhint.json index 6e5d5d91a..54eee2f43 100644 --- a/zeth_contracts/.solhint.json +++ b/zeth_contracts/.solhint.json @@ -11,7 +11,7 @@ "avoid-suicide": "error", "no-inline-assembly": "off", "avoid-sha3": "warn", - "func-visibility": "error", + "func-visibility": ["error", {"ignoreConstructors": true}], "max-line-length": ["error", 79] } } diff --git a/zeth_contracts/.solhintignore b/zeth_contracts/.solhintignore index 470ae503a..c41e7a6f7 100644 --- a/zeth_contracts/.solhintignore +++ b/zeth_contracts/.solhintignore @@ -1,2 +1,3 @@ +ERC20Mintable.sol node_modules/ contracts/Migrations.sol diff --git a/zeth_contracts/contracts/BaseMerkleTree.sol b/zeth_contracts/contracts/AbstractMerkleTree.sol similarity index 52% rename from zeth_contracts/contracts/BaseMerkleTree.sol rename to zeth_contracts/contracts/AbstractMerkleTree.sol index 2d88a38bc..731e44aa9 100644 --- a/zeth_contracts/contracts/BaseMerkleTree.sol +++ b/zeth_contracts/contracts/AbstractMerkleTree.sol @@ -1,8 +1,8 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ -pragma solidity ^0.5.0; +pragma solidity ^0.8.0; /// Abstract Merkle tree implementation. Child classes should implement the /// hash function. @@ -13,39 +13,36 @@ pragma solidity ^0.5.0; /// This implementation stores all leaves and nodes, skipping those that have /// not been populated yet. The final entry in each layer stores that layer's /// default value. -contract BaseMerkleTree +abstract contract AbstractMerkleTree { // Depth of the merkle tree (should be set with the same depth set in the // cpp prover) - uint256 internal constant DEPTH = 32; + uint256 internal constant _DEPTH = 32; - // Number of leaves - uint256 internal constant MAX_NUM_LEAVES = 2**DEPTH; + // Maximum number of leaves in the tree + uint256 internal constant _MAX_NUM_LEAVES = 2**_DEPTH; - // Number of nodes - uint256 internal constant MAX_NUM_NODES = (MAX_NUM_LEAVES * 2) - 1; + // Maximum number of nodes in the tree + uint256 internal constant _MAX_NUM_NODES = (_MAX_NUM_LEAVES * 2) - 1; - uint256 internal constant MASK_LS_BIT = ~uint256(1); + uint256 internal constant _MASK_LS_BIT = ~uint256(1); - bytes32 internal constant DEFAULT_LEAF_VALUE = 0x0; + bytes32 internal constant _DEFAULT_LEAF_VALUE = 0x0; // Sparse array of populated leaves of the merkle tree. - // Unpopulated leaves have the DEFAULT_LEAF_VALUE. - bytes32[MAX_NUM_NODES] internal nodes; + // Unpopulated leaves have the _DEFAULT_LEAF_VALUE. + bytes32[_MAX_NUM_NODES] internal _nodes; - // Number of leaves populated in `nodes`. - uint256 internal num_leaves; - - // Debug only - event LogDebug(bytes32 message); + // Number of leaves populated in `_nodes`. + uint256 internal _numLeaves; /// Constructor - constructor(uint256 treeDepth) public { + constructor(uint256 treeDepth) { require ( - treeDepth == DEPTH, - "Invalid depth in BaseMerkleTree" + treeDepth == _DEPTH, + "Invalid depth in AbstractMerkleTree" ); - initializeTree(); + _initializeTree(); } /// Appends a commitment to the tree, and returns its address @@ -53,59 +50,62 @@ contract BaseMerkleTree // If this require fails => the merkle tree is full, we can't append // leaves anymore. require( - num_leaves < MAX_NUM_LEAVES, + _numLeaves < _MAX_NUM_LEAVES, "Merkle tree full: Cannot append anymore" ); // Address of the next leaf is the current number of leaves (before // insertion). Compute the next index in the full set of nodes, and // write. - uint256 next_address = num_leaves; - ++num_leaves; - uint256 next_entry_idx = (MAX_NUM_LEAVES - 1) + next_address; - nodes[next_entry_idx] = commitment; + uint256 next_address = _numLeaves; + ++_numLeaves; + uint256 next_entry_idx = (_MAX_NUM_LEAVES - 1) + next_address; + _nodes[next_entry_idx] = commitment; } /// Abstract hash function to be supplied by a concrete implementation of /// this class. - function hash(bytes32 left, bytes32 right) internal returns (bytes32); + function _hash(bytes32 left, bytes32 right) + internal + virtual + returns (bytes32); - function recomputeRoot(uint num_new_leaves) internal returns (bytes32) { - // Assume `num_new_leaves` have been written into the leaf slots. + function _recomputeRoot(uint numNewLeaves) internal returns (bytes32) { + // Assume `numNewLeaves` have been written into the leaf slots. // Update any affected nodes in the tree, up to the root, using the // default values for any missing nodes. - uint256 end_idx = num_leaves; - uint256 start_idx = num_leaves - num_new_leaves; - uint256 layer_size = MAX_NUM_LEAVES; + uint256 end_idx = _numLeaves; + uint256 start_idx = _numLeaves - numNewLeaves; + uint256 layer_size = _MAX_NUM_LEAVES; while (layer_size > 1) { (start_idx, end_idx) = - recomputeParentLayer(layer_size, start_idx, end_idx); + _recomputeParentLayer(layer_size, start_idx, end_idx); layer_size = layer_size / 2; } - return nodes[0]; + return _nodes[0]; } - function initializeTree() private { + function _initializeTree() private { // First layer - bytes32 default_value = DEFAULT_LEAF_VALUE; - nodes[2 * MAX_NUM_LEAVES - 2] = default_value; - uint256 layer_size = MAX_NUM_LEAVES / 2; + bytes32 default_value = _DEFAULT_LEAF_VALUE; + _nodes[2 * _MAX_NUM_LEAVES - 2] = default_value; + uint256 layer_size = _MAX_NUM_LEAVES / 2; // Subsequent layers while (layer_size > 0) { - default_value = hash(default_value, default_value); + default_value = _hash(default_value, default_value); uint256 layer_final_entry_idx = 2 * layer_size - 2; - nodes[layer_final_entry_idx] = default_value; + _nodes[layer_final_entry_idx] = default_value; layer_size = layer_size / 2; } } /// Recompute nodes in the parent layer that are affected by entries - /// [child_start_idx, child_end_idx[ in the child layer. If - /// `child_end_idx` is required in the calculation, the final entry of + /// [childStartIdx, childEndIdx[ in the child layer. If + /// `childEndIdx` is required in the calculation, the final entry of /// the child layer is used (since this contains the default entry for /// the layer if the tree is not full). /// @@ -114,19 +114,19 @@ contract BaseMerkleTree /// / \ / \ / \ / \ / \ / \ /// Child: ? ? ? ? A B C D E ? ? 0 /// ^ ^ - /// child_start_idx child_end_idx + /// childStartIdx childEndIdx /// /// Returns the start and end indices (within the parent layer) of touched /// parent nodes. - function recomputeParentLayer( - uint256 child_layer_size, - uint256 child_start_idx, - uint256 child_end_idx + function _recomputeParentLayer( + uint256 childLayerSize, + uint256 childStartIdx, + uint256 childEndIdx ) private returns (uint256, uint256) { - uint256 child_layer_start = child_layer_size - 1; + uint256 child_layer_start = childLayerSize - 1; // Start at the right and iterate left, so we only execute the // default_value logic once. child_left_idx_rend (reverse-end) is the @@ -134,20 +134,20 @@ contract BaseMerkleTree // parent node hash. uint256 child_left_idx_rend = - child_layer_start + (child_start_idx & MASK_LS_BIT); + child_layer_start + (childStartIdx & _MASK_LS_BIT); - // If child_end_idx is odd, it is the RIGHT of a computation we need + // If childEndIdx is odd, it is the RIGHT of a computation we need // to make. Do the computation using the default value, and move to // the next pair (on the left). // Otherwise, we have a fully populated pair. uint256 child_left_idx; - if ((child_end_idx & 1) != 0) { - child_left_idx = child_layer_start + child_end_idx - 1; - nodes[(child_left_idx - 1) / 2] = - hash(nodes[child_left_idx], nodes[2 * child_layer_start]); + if ((childEndIdx & 1) != 0) { + child_left_idx = child_layer_start + childEndIdx - 1; + _nodes[(child_left_idx - 1) / 2] = + _hash(_nodes[child_left_idx], _nodes[2 * child_layer_start]); } else { - child_left_idx = child_layer_start + child_end_idx; + child_left_idx = child_layer_start + childEndIdx; } // At this stage, pairs are all populated. Compute until we reach @@ -155,10 +155,10 @@ contract BaseMerkleTree while (child_left_idx > child_left_idx_rend) { child_left_idx = child_left_idx - 2; - nodes[(child_left_idx - 1) / 2] = - hash(nodes[child_left_idx], nodes[child_left_idx + 1]); + _nodes[(child_left_idx - 1) / 2] = + _hash(_nodes[child_left_idx], _nodes[child_left_idx + 1]); } - return (child_start_idx / 2, (child_end_idx + 1) / 2); + return (childStartIdx / 2, (childEndIdx + 1) / 2); } } diff --git a/zeth_contracts/contracts/AbstractMixer.sol b/zeth_contracts/contracts/AbstractMixer.sol new file mode 100644 index 000000000..7dcc73c80 --- /dev/null +++ b/zeth_contracts/contracts/AbstractMixer.sol @@ -0,0 +1,523 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +pragma solidity ^0.8.0; + +import "./Tokens.sol"; +import "./LibOTSchnorrVerifier.sol"; +import "./AbstractMerkleTree.sol"; + +/// AbstractMixer implements the functions shared across all Mixers (regardless +/// which zkSNARK is used) +abstract contract AbstractMixer is AbstractMerkleTree, ERC223ReceivingContract +{ + // The roots of the different updated trees + mapping(bytes32 => bool) private _roots; + + // The public list of nullifiers (prevents double spend) + mapping(bytes32 => bool) private _nullifiers; + + // Structure of the verification key and proofs is opaque, determined by + // zk-snark verification library. + uint256[] internal _vk; + + // Contract variable that indicates the address of the token contract + // If token = address(0) then the mixer works with ether + address private _token; + + // Contract that is allowed to call the `dispatch` method, passing in the + // correct _vkHash. (Disable the dispatch method by setting + // _permittedDispatcher = 0, in which case _vkHash is unused.) + address private _permittedDispatcher; + + // The acceptable value of _vkHash, passed in by a trusted dispatcher. + uint256[2] private _vkHash; + + // JoinSplit description, gives the number of inputs (nullifiers) and + // outputs (commitments/ciphertexts) to receive and process. + // + // IMPORTANT NOTE: We need to employ the same JS configuration than the one + // used in the cpp prover. Here we use 2 inputs and 2 outputs (it is a 2-2 + // JS). + uint256 internal constant _JSIN = 2; // Number of nullifiers + uint256 internal constant _JSOUT = 2; // Number of commitments/ciphertexts + + // Size of the public values in bits + uint256 internal constant _PUBLIC_VALUE_BITS = 64; + + // Public values mask + uint256 internal constant _PUBLIC_VALUE_MASK = + (1 << _PUBLIC_VALUE_BITS) - 1; + + // Total number of bits for public values. Digest residual bits appear + // after these. + uint256 internal constant _TOTAL_PUBLIC_VALUE_BITS = + 2 * _PUBLIC_VALUE_BITS; + + uint256 internal constant _DIGEST_LENGTH = 256; + + // Number of hash digests in the primary inputs: + // 1 (the root) + // 2 * _JSIN (nullifier and message auth tag per JS input) + // _JSOUT (commitment per JS output) + uint256 internal constant _NUM_HASH_DIGESTS = 1 + 2 * _JSIN; + + // All code assumes that public values and residual bits can be encoded in + // a single field element. + uint256 internal constant _NUM_FIELD_RESIDUAL = 1; + + // The number of public inputs are: + // - 1 (the root) + // - _JSIN (the nullifiers) + // - _JSOUT (the commitments) + // - 1 (hsig) + // - JsIn (the message auth. tags) + // - _NUM_FIELD_RESIDUAL (the residual bits not fitting in a single field + // element and the in and out public values) + uint256 internal constant _NUM_INPUTS = + 1 + _JSOUT + _NUM_HASH_DIGESTS + _NUM_FIELD_RESIDUAL; + + // The unit used for public values (ether in and out), in Wei. Must match + // the python wrappers. Use Szabos (10^12 Wei). + uint64 internal constant _PUBLIC_UNIT_VALUE_WEI = 1e12; + + event LogMix( + bytes32 root, + bytes32[_JSIN] nullifiers, + bytes32[_JSOUT] commitments, + bytes[_JSOUT] ciphertexts + ); + + /// Debug only + event LogDebug(string message, uint256 value); + + /// Constructor + constructor( + uint256 depth, + address tokenAddress, + uint256[] memory vk, + address permittedDispatcher, + uint256[2] memory vkHash + ) + AbstractMerkleTree(depth) + { + bytes32 initialRoot = _nodes[0]; + _roots[initialRoot] = true; + _vk = vk; + _token = tokenAddress; + _permittedDispatcher = permittedDispatcher; + _vkHash = vkHash; + } + + /// Function allowing external users of the contract to retrieve some of + /// the constants used in the mixer (since the solidity interfaces do not + /// export this information as-of the current version). The intention is + /// that external users and contraacts can query this function and ensure + /// that they are compatible with the mixer configurations. + /// + /// Returns the number of input notes, the number of output notes and the + /// total number of primary inputs. + function getConstants() + external + pure + returns ( + uint256 jsinOut, + uint256 jsoutOut, + uint256 numinputsOut + ) + { + jsinOut = _JSIN; + jsoutOut = _JSOUT; + numinputsOut = _NUM_INPUTS; + } + + /// Permitted dispatchers may call this entry point if they have verified + /// the associated proof. This is technically part of the + /// IZecaleApplication interface, see + /// https://github.com/clearmatics/zecale + function dispatch( + uint256[2] memory nestedVkHash, + uint256[] memory nestedInputs, + bytes memory nestedParameters + ) + external + payable + { + // Sanity / permission check + require( + msg.sender == _permittedDispatcher, "dispatcher not permitted"); + require( + nestedVkHash[0] == _vkHash[0] && + nestedVkHash[1] == _vkHash[1], + "invalid nestedVkHash"); + require(nestedInputs.length == 1, "invalid num nested inputs"); + + // Decode the nested parameters + // TODO: convert ciphertext array without copying + (uint256[4] memory vk, + uint256 sigma, + uint256[] memory public_data, + bytes[] memory decoded_ciphertexts) = abi.decode( + nestedParameters, (uint256[4], uint256, uint256[], bytes[])); + require( + public_data.length == _NUM_INPUTS, + "invalid number of public inputs in decoded data."); + require( + decoded_ciphertexts.length == _JSOUT, + "invalid number of ciphertexts in decoded data."); + + bytes[_JSOUT] memory ciphertexts; + for (uint256 i = 0 ; i < _JSOUT ; ++i) { + ciphertexts[i] = decoded_ciphertexts[i]; + } + + // Copy the public inputs into a fixed-size array. + // TODO: convert without copying. + uint256[_NUM_INPUTS] memory inputs; + for (uint256 i = 0 ; i < _NUM_INPUTS ; ++i) { + inputs[i] = public_data[i]; + } + + // Ensure that the primary input to the zk-proof (validated and passed + // in by the dispatcher), matches the hash of the public inputs. + require( + nestedInputs[0] == _hashPublicProofData(inputs), + "hash of public data does not match primary input"); + + // 1. Check the root and the nullifiers + bytes32[_JSIN] memory nullifiers; + _checkMkrootNullifiersHsigAppendNullifiersState( + vk, inputs, nullifiers); + + // 2.a Verify the signature on the hash of data_to_be_signed. + // hashToBeSigned is expected to have been created without the proof + // data. + bytes32 hashToBeSigned = sha256( + abi.encodePacked( + uint256(uint160(msg.sender)), + ciphertexts[0], + ciphertexts[1], + inputs + ) + ); + + require( + LibOTSchnorrVerifier._verify( + vk[0], vk[1], vk[2], vk[3], sigma, hashToBeSigned), + "Invalid signature in dispatch" + ); + + _mixAppendCommitmentsEmitAndHandlePublicValues( + inputs, ciphertexts, nullifiers); + } + + /// This function is used to execute payments in zero knowledge. + /// The format of `proof` is internal to the zk-snark library. + /// The `inputs` array is the set of scalar inputs to the proof. + /// We assume that each input occupies a single uint256. + function mix( + uint256[] memory proof, + uint256[4] memory vk, + uint256 sigma, + uint256[_NUM_INPUTS] memory publicInputs, + bytes[_JSOUT] memory ciphertexts + ) + external + payable + { + // 1. Check the root and the nullifiers + bytes32[_JSIN] memory nullifiers; + _checkMkrootNullifiersHsigAppendNullifiersState( + vk, publicInputs, nullifiers); + + // 2.a Verify the signature on the hash of data_to_be_signed + bytes32 hashToBeSigned = sha256( + abi.encodePacked( + uint256(uint160(msg.sender)), + // Unfortunately, we have to unroll this for now. We could + // replace encodePacked with a custom function but this would + // increase complexity and possibly gas usage. + ciphertexts[0], + ciphertexts[1], + proof, + publicInputs + ) + ); + require( + LibOTSchnorrVerifier._verify( + vk[0], vk[1], vk[2], vk[3], sigma, hashToBeSigned), + "Invalid signature: Unable to verify the signature correctly" + ); + + // 2.b Verify the proof + uint256 publicInputsHash = _hashPublicProofData(publicInputs); + require( + _verifyZkProof(proof, publicInputsHash), + "Invalid proof: Unable to verify the proof correctly" + ); + + _mixAppendCommitmentsEmitAndHandlePublicValues( + publicInputs, ciphertexts, nullifiers); + } + + function _mixAppendCommitmentsEmitAndHandlePublicValues( + uint256[_NUM_INPUTS] memory inputs, + bytes[_JSOUT] memory ciphertexts, + bytes32[_JSIN] memory nullifiers + ) + internal + { + // 3. Append the commitments to the tree + bytes32[_JSOUT] memory commitments; + _assembleCommitmentsAndAppendToState(inputs, commitments); + + // 4. Add the new root to the list of existing roots + bytes32 new_merkle_root = _recomputeRoot(_JSOUT); + _addMerkleRoot(new_merkle_root); + + // 5. Emit the all Mix data + emit LogMix( + new_merkle_root, + nullifiers, + commitments, + ciphertexts + ); + + // 6. Get the public values in Wei and modify the state depending on + // their values + _processPublicValues(inputs); + } + + function _hashPublicProofData(uint256[_NUM_INPUTS] memory publicData) + internal + returns (uint256) + { + // Initialize h with the IV and hash each public data value (see + // client/zeth/core/input_hasher.py for details) + bytes32 h; + h = bytes32(uint256( + // solhint-disable-next-line max-line-length + 13196537064117388418196223856311987714388543839552400408340921397545324034315)); + for (uint256 i = 0 ; i < _NUM_INPUTS; ++i) { + h = _hash(h, bytes32(publicData[i])); + } + h = _hash(h, bytes32(_NUM_INPUTS)); + return uint256(h); + } + + /// This function is used to extract the public values (vpub_in, vpub_out) + /// from the residual field element(S) + function _assemblePublicValues(uint256 residualBits) + internal + pure + returns ( + uint256 vpub_in, + uint256 vpub_out + ) + { + // vpub_out and vpub_in occupy the first and second _PUBLIC_VALUE_BITS + vpub_out = + (residualBits & _PUBLIC_VALUE_MASK) * _PUBLIC_UNIT_VALUE_WEI; + vpub_in = ((residualBits >> _PUBLIC_VALUE_BITS) & _PUBLIC_VALUE_MASK) + * _PUBLIC_UNIT_VALUE_WEI; + } + + /// This function is used to reassemble hsig given the primaryInputs. + /// To do so, we extract the remaining bits of hsig from the residual field + /// element(S) and combine them with the hsig field element + function _assembleHsig( + uint256[_NUM_INPUTS] memory primaryInputs + ) + internal + pure + returns(bytes32 hsig) + { + // The h_sig residual bits are after the _JSIN authentication tags and + // _JSIN nullifier bits. + return _extractBytes32( + primaryInputs[1 + _JSIN + _JSOUT], + primaryInputs[1 + _JSOUT + _NUM_HASH_DIGESTS], + 2 * _JSIN + ); + } + + /// This function is used to reassemble the nullifiers given the nullifier + /// index [0, _JSIN[ and the primaryInputs To do so, we extract the + /// remaining bits of the nullifier from the residual field element(S) and + /// combine them with the nullifier field element + function _assembleNullifier( + uint256 index, + uint256[_NUM_INPUTS] memory primaryInputs + ) + internal + pure + returns(bytes32 nf) + { + // We first check that the nullifier we want to retrieve exists + require(index < _JSIN, "nullifier index overflow"); + + // Nullifier residual bits follow the `_JSIN` message authentication + // tags + return _extractBytes32( + primaryInputs[1 + _JSOUT + index], + primaryInputs[1 + _JSOUT + _NUM_HASH_DIGESTS], + _JSIN + index + ); + } + + /// This function processes the primary inputs to append and check the root + /// and nullifiers in the primary inputs (instance) and modifies the state + /// of the mixer contract accordingly. (ie: Appends the commitments to the + /// tree, appends the nullifiers to the list and so on). + function _checkMkrootNullifiersHsigAppendNullifiersState( + uint256[4] memory vk, + uint256[_NUM_INPUTS] memory primaryInputs, + bytes32[_JSIN] memory nfs) + internal + { + // 1. We re-assemble the full root digest and check it is in the tree + require( + _roots[bytes32(primaryInputs[0])], + "Invalid root: This root doesn't exist" + ); + + // 2. We re-assemble the nullifiers (JSInputs) and check they were not + // already seen. + for (uint256 i = 0; i < _JSIN; i++) { + bytes32 nullifier = _assembleNullifier(i, primaryInputs); + require( + !_nullifiers[nullifier], + "Invalid nullifier: This nullifier has already been used" + ); + _nullifiers[nullifier] = true; + + nfs[i] = nullifier; + } + + // 3. We re-compute h_sig, re-assemble the expected h_sig and check + // they are equal (i.e. that h_sig re-assembled was correctly generated + // from vk). + bytes32 expected_hsig = sha256(abi.encodePacked(nfs, vk)); + bytes32 hsig = _assembleHsig(primaryInputs); + require( + expected_hsig == hsig, + "Invalid hsig: This hsig does not correspond to the hash of vk and" + " the nfs" + ); + } + + function _assembleCommitmentsAndAppendToState( + uint256[_NUM_INPUTS] memory primaryInputs, + bytes32[_JSOUT] memory comms + ) + internal + { + // We re-assemble the commitments (JSOutputs) + for (uint256 i = 0; i < _JSOUT; i++) { + bytes32 current_commitment = bytes32(primaryInputs[1 + i]); + comms[i] = current_commitment; + insert(current_commitment); + } + } + + function _processPublicValues(uint256[_NUM_INPUTS] memory primaryInputs) + internal + { + // We get vpub_in and vpub_out in wei + (uint256 vpub_in, uint256 vpub_out) = _assemblePublicValues( + primaryInputs[1 + _JSOUT + _NUM_HASH_DIGESTS]); + + // If the vpub_in is > 0, we need to make sure the right amount is paid + if (vpub_in > 0) { + if (_token != address(0)) { + IERC20 erc20Token = IERC20(_token); + erc20Token.transferFrom(msg.sender, address(this), vpub_in); + } else { + require( + msg.value == vpub_in, + "Wrong msg.value: Value paid is not correct" + ); + } + } else { + // If vpub_in = 0, return incoming Ether to the caller + if (msg.value > 0) { + // solhint-disable-next-line + (bool success, ) = msg.sender.call{value: msg.value}(""); + require(success, "vpub_in return transfer failed"); + } + } + + // If value_pub_out > 0 then we do a withdraw. We retrieve the + // msg.sender and send him the appropriate value IF proof is valid + if (vpub_out > 0) { + if (_token != address(0)) { + IERC20 erc20Token = IERC20(_token); + erc20Token.transfer(msg.sender, vpub_out); + } else { + // solhint-disable-next-line + (bool success, ) = msg.sender.call{value: vpub_out}(""); + require(success, "vpub_out transfer failed"); + } + } + } + + function _addMerkleRoot(bytes32 root) internal + { + _roots[root] = true; + } + + // ====================================================================== + // Reminder: Remember that the primary inputs are ordered as follows: + // + // [Root, CommitmentS, NullifierS, h_sig, h_iS, Residual Element(s)] + // + // ie, below is the index mapping of the primary input elements on the + // protoboard: + // + // 0 + // 1 + // ... + // _JSOUT + // _JSOUT + 1 + // ... + // _JSOUT + _JSIN + // _JSOUT + _JSIN + 1 + // _JSOUT + _JSIN + 2 + // ... + // _JSOUT + 2*_JSIN + 1 + // _JSOUT + 2*_JSIN + 2 + // + // The Residual field elements are structured as follows: + // + // 255 128 64 0 + // |||||)|| + // + // where each entry entry after public output and input holds the + // (curve-specific) number residual bits for the corresponding 256 bit + // value. + // ====================================================================== + + /// Utility function to extract a full uint256 from a field element and the + /// n-th set of residual bits from `residual`. This function is + /// curve-dependent. + function _extractBytes32( + uint256 fieldElement, + uint256 residual, + uint256 residualBitsSetIdx + ) + internal + pure + virtual + returns(bytes32); + + /// Implementations must implement the verification algorithm of the + /// selected SNARK. + function _verifyZkProof( + uint256[] memory proof, + uint256 publicInputsHash + ) + internal + virtual + returns (bool); +} diff --git a/zeth_contracts/contracts/AbstractMixerAltBN128.sol b/zeth_contracts/contracts/AbstractMixerAltBN128.sol new file mode 100644 index 000000000..1769b5830 --- /dev/null +++ b/zeth_contracts/contracts/AbstractMixerAltBN128.sol @@ -0,0 +1,80 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +pragma solidity ^0.8.0; + +import "./AbstractMixer.sol"; +import "./LibMiMC.sol"; + +/// Partial implementation of AbstractMixer which implements the +/// curve-specific methods to use the ALT-BN128 pairing. +abstract contract AbstractMixerAltBN128 is AbstractMixer +{ + // Constants regarding the hash digest length, the prime number used and + // its associated length in bits and the max values (v_in and v_out) + // FIELD_CAPACITY = floor( log_2(r) ) + uint256 internal constant _FIELD_CAPACITY = 253; + + // Number of residual bits per bytes32 + uint256 internal constant _NUM_RESIDUAL_BITS = + _DIGEST_LENGTH - _FIELD_CAPACITY; + + // Shift to move residual bits from lowest order to highest order + uint256 internal constant _RESIDUAL_BITS_SHIFT = 256 - _NUM_RESIDUAL_BITS; + + // Mask to extract the residual bits in the high-order position + uint256 internal constant _RESIDUAL_BITS_MASK = + ((1 << _NUM_RESIDUAL_BITS) - 1) << _RESIDUAL_BITS_SHIFT; + + /// Constructor of the contract + constructor( + uint256 mkDepth, + address token, + uint256[] memory vk, + address permittedDispatcher, + uint256[2] memory vkHash + ) + AbstractMixer(mkDepth, token, vk, permittedDispatcher, vkHash) + { + } + + function _hash(bytes32 left, bytes32 right) + internal + pure + override + returns(bytes32) + { + return LibMiMC._hashAltBN128(left, right); + } + + /// Utility function to extract a full uint256 from a field element and the + /// n-th set of residual bits from `residual`. + function _extractBytes32( + uint256 fieldElement, + uint256 residual, + uint256 residualBitsSetIdx + ) + internal + pure + override + returns(bytes32) + { + // The residual bits are located at: + // (2*public_value_bits) + (residualBitsSetIdx*NUM_RESIDUAL_BITS) + // + // Shift to occupy the highest order bits: + // 255 128 64 0 + // | bitsToShift | | residualBitsIdx | | | + // | <------------ | xxx | |)|| + // residual bits + + // Number of bits AFTER public values + uint256 residualBitsIdx = residualBitsSetIdx * _NUM_RESIDUAL_BITS; + uint256 bitsToShift = + _RESIDUAL_BITS_SHIFT - _TOTAL_PUBLIC_VALUE_BITS - residualBitsIdx; + uint256 residualBits = + (residual << bitsToShift) & _RESIDUAL_BITS_MASK; + return bytes32(fieldElement | residualBits); + } +} diff --git a/zeth_contracts/contracts/AbstractMixerBLS12_377.sol b/zeth_contracts/contracts/AbstractMixerBLS12_377.sol new file mode 100644 index 000000000..acf9da926 --- /dev/null +++ b/zeth_contracts/contracts/AbstractMixerBLS12_377.sol @@ -0,0 +1,87 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +pragma solidity ^0.8.0; + +import "./AbstractMixer.sol"; +import "./LibMiMC.sol"; + +/// Partial implementation of AbstractMixer which implements the +/// curve-specific methods to use the BLS12-377 pairing. +abstract contract AbstractMixerBLS12_377 is AbstractMixer +{ + // TODO: Code here is very similar to AbstractMixerAltBN128, with only the + // constants changed. Look into sharing more code (possibly by making some + // of these constants dynamic). + + // Constants regarding the hash digest length, the prime number used and + // its associated length in bits and the max values (v_in and v_out) + + // Number of bits that can be reliably represented by a single field + // element: + // _FIELD_CAPACITY = floor( log_2(r) ) + // Denoted FIELDCAP in Zeth specifications. + uint256 internal constant _FIELD_CAPACITY = 252; + + // Number of residual bits per bytes32 + uint256 internal constant _NUM_RESIDUAL_BITS = + _DIGEST_LENGTH - _FIELD_CAPACITY; + + // Shift to move residual bits from lowest order to highest order + uint256 internal constant _RESIDUAL_BITS_SHIFT = 256 - _NUM_RESIDUAL_BITS; + + // Mask to extract the residual bits in the high-order position + uint256 internal constant _RESIDUAL_BITS_MASK = + ((1 << _NUM_RESIDUAL_BITS) - 1) << _RESIDUAL_BITS_SHIFT; + + constructor( + uint256 mkDepth, + address token, + uint256[] memory vk, + address permittedDispatcher, + uint256[2] memory vkHash + ) + AbstractMixer(mkDepth, token, vk, permittedDispatcher, vkHash) + { + } + + function _hash(bytes32 left, bytes32 right) + internal + pure + override + returns(bytes32) + { + return LibMiMC._hashBLS12_377(left, right); + } + + /// Extract a full uint256 from a field element and the n-th set of + /// residual bits from `residual`. + function _extractBytes32( + uint256 fieldElement, + uint256 residual, + uint256 residualBitsSetIdx + ) + internal + pure + override + returns(bytes32) + { + // The residualBitsSetIdx-th set of residual bits (denoted r_i + // below) start at bit: + // (2*_PUBLIC_VALUE_BITS) + (residualBitsSetIdx*num_residual_bits) + // + // Shift r_i to occupy the highest order bits: + // 255 128 64 0 + // | bitsToShift | | residualBitsIdx | | | + // | <------------ || ||| + + // Number of bits AFTER public values + uint256 residualBitsIdx = residualBitsSetIdx * _NUM_RESIDUAL_BITS; + uint256 bitsToShift = + _RESIDUAL_BITS_SHIFT - _TOTAL_PUBLIC_VALUE_BITS - residualBitsIdx; + uint256 residualBits = + (residual << bitsToShift) & _RESIDUAL_BITS_MASK; + return bytes32(fieldElement | residualBits); + } +} diff --git a/zeth_contracts/contracts/AltBN128MixerBase.sol b/zeth_contracts/contracts/AltBN128MixerBase.sol deleted file mode 100644 index b8f80a49e..000000000 --- a/zeth_contracts/contracts/AltBN128MixerBase.sol +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd -// -// SPDX-License-Identifier: LGPL-3.0+ - -pragma solidity ^0.5.0; -pragma experimental ABIEncoderV2; - -import "./MixerBase.sol"; -import "./MiMC7.sol"; - -/// Partial implementation of abstract MixerBase which implements the -/// curve-specific methods to use the ALT-BN128 pairing. -contract AltBN128MixerBase is MixerBase -{ - // Constants regarding the hash digest length, the prime number used and - // its associated length in bits and the max values (v_in and v_out) - // FIELD_CAPACITY = floor( log_2(r) ) - uint256 internal constant FIELD_CAPACITY = 253; - - // Number of residual bits per bytes32 - uint256 internal constant NUM_RESIDUAL_BITS = - DIGEST_LENGTH - FIELD_CAPACITY; - - // Shift to move residual bits from lowest order to highest order - uint256 internal constant RESIDUAL_BITS_SHIFT = 256 - NUM_RESIDUAL_BITS; - - // Mask to extract the residual bits in the high-order position - uint256 internal constant RESIDUAL_BITS_MASK = - ((1 << NUM_RESIDUAL_BITS) - 1) << RESIDUAL_BITS_SHIFT; - - // Total number of residual bits from packing of 256-bit long string into - // 253-bit long field elements to which are added the public value of size - // 64 bits - uint256 internal constant TOTAL_NUM_RESIDUAL_BITS = - 2 * PUBLIC_VALUE_BITS + NUM_RESIDUAL_BITS * NUM_HASH_DIGESTS; - - /// Constructor of the contract - constructor( - uint256 mk_depth, - address token, - uint256[] memory vk - ) - public - MixerBase(mk_depth, token, vk) - { - } - - /// Use MiMC7 as the Merkle tree hash function. - function hash(bytes32 left, bytes32 right) internal returns(bytes32) { - return MiMC7.hash(left, right); - } - - /// Utility function to extract a full uint256 from a field element and the - /// n-th set of residual bits from `residual`. - function extract_bytes32( - uint256 field_element, - uint256 residual, - uint256 residual_bits_set_idx - ) - internal - pure - returns(bytes32) - { - // The residual bits are located at: - // (2*public_value_bits) + (residual_bits_set_idx*NUM_RESIDUAL_BITS) - // - // Shift to occupy the highest order bits: - // 255 128 64 0 - // | bits_to_shift | | residual_bits_idx | | | - // | <------------ | xxx | |)|| - // residual bits - - // Number of bits AFTER public values - uint256 residual_bits_idx = residual_bits_set_idx * NUM_RESIDUAL_BITS; - uint256 bits_to_shift = - RESIDUAL_BITS_SHIFT - TOTAL_PUBLIC_VALUE_BITS - residual_bits_idx; - uint256 residual_bits = - (residual << bits_to_shift) & RESIDUAL_BITS_MASK; - return bytes32(field_element | residual_bits); - } -} diff --git a/zeth_contracts/contracts/AltBN128MixerBase_test.sol b/zeth_contracts/contracts/AltBN128MixerBase_test.sol deleted file mode 100644 index 478ac18d0..000000000 --- a/zeth_contracts/contracts/AltBN128MixerBase_test.sol +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd -// -// SPDX-License-Identifier: LGPL-3.0+ - -pragma solidity ^0.5.0; -pragma experimental ABIEncoderV2; - -import "./AltBN128MixerBase.sol"; - - -// Implementation of abstract AltBN128MixerBase contract, to allow testing -// specific methods. -contract AltBN128MixerBase_test is AltBN128MixerBase -{ - constructor(uint256 mk_depth) - public - AltBN128MixerBase(mk_depth, address(0), new uint256[](0)) - { - } - - function assemble_public_values_test(uint256 residual_bits) - public - pure - returns (uint256 vpub_in, uint256 vpub_out) - { - return assemble_public_values(residual_bits); - } - - function assemble_hsig_test( - uint256[NUM_INPUTS] memory primary_inputs - ) - public - pure - returns(bytes32 hsig) - { - return assemble_hsig(primary_inputs); - } - - function assemble_nullifier_test( - uint256 index, - uint256[NUM_INPUTS] memory primary_inputs - ) - public - pure - returns(bytes32 nf) - { - return assemble_nullifier(index, primary_inputs); - } - - // Dummy implementation of abstract function - function verify_zk_proof( - uint256[] memory /* proof */, - uint256[NUM_INPUTS] memory /* inputs */ - ) - internal - returns (bool) - { - return false; - } -} diff --git a/zeth_contracts/contracts/BLS12_377MixerBase.sol b/zeth_contracts/contracts/BLS12_377MixerBase.sol deleted file mode 100644 index 195d0098e..000000000 --- a/zeth_contracts/contracts/BLS12_377MixerBase.sol +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd -// -// SPDX-License-Identifier: LGPL-3.0+ - -pragma solidity ^0.5.0; -pragma experimental ABIEncoderV2; - -import "./MixerBase.sol"; -import "./MiMC31.sol"; - -/// Partial implementation of abstract MixerBase which implements the -/// curve-specific methods to use the BLS12-377 pairing. -contract BLS12_377MixerBase is MixerBase -{ - // TODO: Code here is very similar to AltBN128MixerBase, with only the - // constants changed. Look into sharing more code (possibly by making some - // of these constants dynamic). - - // Constants regarding the hash digest length, the prime number used and - // its associated length in bits and the max values (v_in and v_out) - - // Number of bits that can be reliably represented by a single field - // element: - // FIELD_CAPACITY = floor( log_2(r) ) - // Denoted FIELDCAP in Zeth specifications. - uint256 internal constant FIELD_CAPACITY = 252; - - // Number of residual bits per bytes32 - uint256 internal constant NUM_RESIDUAL_BITS = - DIGEST_LENGTH - FIELD_CAPACITY; - - // Shift to move residual bits from lowest order to highest order - uint256 internal constant RESIDUAL_BITS_SHIFT = 256 - NUM_RESIDUAL_BITS; - - // Mask to extract the residual bits in the high-order position - uint256 internal constant RESIDUAL_BITS_MASK = - ((1 << NUM_RESIDUAL_BITS) - 1) << RESIDUAL_BITS_SHIFT; - - // Total number of bits required to hold all residual bits from, including - // the public vin and vout values (each 64 bits long). Denoted RSDBLEN in - // Zeth specifications. - uint256 internal constant RESIDUAL_BITS_LENGTH = - 2 * PUBLIC_VALUE_BITS + NUM_RESIDUAL_BITS * NUM_HASH_DIGESTS; - - constructor( - uint256 mk_depth, - address token, - uint256[] memory vk - ) - public - MixerBase(mk_depth, token, vk) - { - } - - function hash(bytes32 left, bytes32 right) internal returns(bytes32) { - return MiMC31.hash(left, right); - } - - /// Extract a full uint256 from a field element and the n-th set of - /// residual bits from `residual`. - function extract_bytes32( - uint256 field_element, - uint256 residual, - uint256 residual_bits_set_idx - ) - internal - pure - returns(bytes32) - { - // The residual_bits_set_idx-th set of residual bits (denoted r_i - // below) start at bit: - // (2*PUBLIC_VALUE_BITS) + (residual_bits_set_idx*num_residual_bits) - // - // Shift r_i to occupy the highest order bits: - // 255 128 64 0 - // | bits_to_shift | | residual_bits_idx | | | - // | <------------ || ||| - - // Number of bits AFTER public values - uint256 residual_bits_idx = residual_bits_set_idx * NUM_RESIDUAL_BITS; - uint256 bits_to_shift = - RESIDUAL_BITS_SHIFT - TOTAL_PUBLIC_VALUE_BITS - residual_bits_idx; - uint256 residual_bits = - (residual << bits_to_shift) & RESIDUAL_BITS_MASK; - return bytes32(field_element | residual_bits); - } -} diff --git a/zeth_contracts/contracts/BW6_761_test.sol b/zeth_contracts/contracts/BW6_761_test.sol deleted file mode 100644 index cad27fdd5..000000000 --- a/zeth_contracts/contracts/BW6_761_test.sol +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd -// -// SPDX-License-Identifier: LGPL-3.0+ - -pragma solidity ^0.5.0; - -contract BW6_761_test -{ - // In many cases, these numbers must be used as literals in the assembly - // code. - - uint256 private constant SCALAR_WORDS = 2; - uint256 private constant COORD_WORDS = 3; - uint256 private constant POINT_WORDS = 2 * COORD_WORDS; - - // `points` should be the concatenation of 2 encoded points - function testECAdd(bytes32[2 * POINT_WORDS] memory points) - public returns (bytes32[POINT_WORDS] memory) - { - bytes32[POINT_WORDS] memory output; - bool success = true; - assembly - { - success := call(gas, 0xc1, 0, points, 0x180, output, 0xc0) - } - - require(success, "precompiled contract call failed (ECAdd)"); - return output; - } - - // `inputs` is an encoded point, followed by an encoded scalar. - function testECMul(bytes32[POINT_WORDS + SCALAR_WORDS] memory input) - public returns (bytes32[POINT_WORDS] memory) - { - bytes32[POINT_WORDS] memory output; - bool success = true; - assembly - { - success := call(gas, 0xc2, 0, input, 0x100, output, 0xc0) - } - - require(success, "precompiled contract call failed (ECMul)"); - return output; - } - - // `points` is the concatenation of 4 pairs of encoded points. Each pair is - // a G1 point, followed by a G2 point. For BW6-761, both of these points - // are 6 words, so there should be 4 * 2 * 6 = 48 words ( - function testECPairing(bytes32[8 * POINT_WORDS] memory input) - public returns (uint256) - { - uint256[1] memory output; - bool success = true; - assembly - { - success := call(gas, 0xc3, 0, input, 0x600, output, 0x20) - } - - require(success, "precompiled contract call failed (ECMul)"); - return output[0]; - } -} diff --git a/zeth_contracts/contracts/ERC20Mintable.sol b/zeth_contracts/contracts/ERC20Mintable.sol new file mode 100644 index 000000000..387454061 --- /dev/null +++ b/zeth_contracts/contracts/ERC20Mintable.sol @@ -0,0 +1,330 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +pragma solidity ^0.8.0; + +import "./Tokens.sol"; + +// Adapted from: +// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v2.5.0/contracts/token/ERC20/ERC20Mintable.sol +// and modified to be compatible with solidity 0.8.0. Original code is covered +// by the following copyright notice: +// +// The MIT License (MIT) +// +// Copyright (c) 2016-2019 zOS Global Limited +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +abstract contract Context { + function _msgSender() internal view returns (address payable) { + return payable(msg.sender); + } + + function _msgData() internal view returns (bytes memory) { + this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 + return msg.data; + } +} + +contract ERC20 is Context, IERC20 { + mapping (address => uint256) private _balances; + + mapping (address => mapping (address => uint256)) private _allowances; + + uint256 private _totalSupply; + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `recipient` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + */ + function transfer(address recipient, uint256 amount) public override returns (bool) { + _transfer(_msgSender(), recipient, amount); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 amount) public returns (bool) { + _approve(_msgSender(), spender, amount); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Emits an {Approval} event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of {ERC20}; + * + * Requirements: + * - `sender` and `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + * - the caller must have allowance for `sender`'s tokens of at least + * `amount`. + */ + function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) { + _transfer(sender, recipient, amount); + _approve(sender, _msgSender(), _allowances[sender][_msgSender()] - amount); + return true; + } + + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue); + return true; + } + + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `spender` must have allowance for the caller of at least + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender] - subtractedValue); + return true; + } + + /** + * @dev Moves tokens `amount` from `sender` to `recipient`. + * + * This is internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * Requirements: + * + * - `sender` cannot be the zero address. + * - `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + */ + function _transfer(address sender, address recipient, uint256 amount) internal { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _balances[sender] = _balances[sender] - amount; + _balances[recipient] = _balances[recipient] + amount; + emit Transfer(sender, recipient, amount); + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * Requirements + * + * - `to` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal { + require(account != address(0), "ERC20: mint to the zero address"); + + _totalSupply = _totalSupply + amount; + _balances[account] = _balances[account] + amount; + emit Transfer(address(0), account, amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * Requirements + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 amount) internal { + require(account != address(0), "ERC20: burn from the zero address"); + + _balances[account] = _balances[account] - amount; + _totalSupply = _totalSupply - amount; + emit Transfer(account, address(0), amount); + } + + /** + * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. + * + * This is internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + */ + function _approve(address owner, address spender, uint256 amount) internal { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + /** + * @dev Destroys `amount` tokens from `account`.`amount` is then deducted + * from the caller's allowance. + * + * See {_burn} and {_approve}. + */ + function _burnFrom(address account, uint256 amount) internal { + _burn(account, amount); + _approve(account, _msgSender(), _allowances[account][_msgSender()] - amount); + } +} + +library Roles { + struct Role { + mapping (address => bool) bearer; + } + + /** + * @dev Give an account access to this role. + */ + function add(Role storage role, address account) internal { + require(!has(role, account), "Roles: account already has role"); + role.bearer[account] = true; + } + + /** + * @dev Remove an account's access to this role. + */ + function remove(Role storage role, address account) internal { + require(has(role, account), "Roles: account does not have role"); + role.bearer[account] = false; + } + + /** + * @dev Check if an account has this role. + * @return bool + */ + function has(Role storage role, address account) internal view returns (bool) { + require(account != address(0), "Roles: account is the zero address"); + return role.bearer[account]; + } +} + +abstract contract MinterRole is Context { + using Roles for Roles.Role; + + event MinterAdded(address indexed account); + event MinterRemoved(address indexed account); + + Roles.Role private _minters; + + constructor () { + _addMinter(_msgSender()); + } + + modifier onlyMinter() { + require(isMinter(_msgSender()), "MinterRole: caller does not have the Minter role"); + _; + } + + function isMinter(address account) public view returns (bool) { + return _minters.has(account); + } + + function addMinter(address account) public onlyMinter { + _addMinter(account); + } + + function renounceMinter() public { + _removeMinter(_msgSender()); + } + + function _addMinter(address account) internal { + _minters.add(account); + emit MinterAdded(account); + } + + function _removeMinter(address account) internal { + _minters.remove(account); + emit MinterRemoved(account); + } +} + +contract ERC20Mintable is ERC20, MinterRole { + /** + * @dev See {ERC20-_mint}. + * + * Requirements: + * + * - the caller must have the {MinterRole}. + */ + function mint(address account, uint256 amount) public onlyMinter returns (bool) { + _mint(account, amount); + return true; + } +} diff --git a/zeth_contracts/contracts/Groth16AltBN128Mixer.sol b/zeth_contracts/contracts/Groth16AltBN128Mixer.sol deleted file mode 100644 index 6912cbb83..000000000 --- a/zeth_contracts/contracts/Groth16AltBN128Mixer.sol +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd -// -// SPDX-License-Identifier: LGPL-3.0+ - -pragma solidity ^0.5.0; -pragma experimental ABIEncoderV2; - -import "./AltBN128MixerBase.sol"; -import "./Groth16AltBN128.sol"; - -/// Instance of AltBN128MixerBase implementing the Groth16 verifier for the -/// alt-bn128 pairing. -contract Groth16AltBN128Mixer is AltBN128MixerBase -{ - constructor( - uint256 mk_depth, - address token, - uint256[] memory vk - ) - public - AltBN128MixerBase(mk_depth, token, vk) - { - } - - function verify_zk_proof( - uint256[] memory proof, - uint256[NUM_INPUTS] memory inputs - ) - internal - returns (bool) - { - // Convert the statically sized primaryInputs to a dynamic array - // expected by the verifier. - - // TODO: mechanism to pass static-sized input arrays to generic - // verifier functions to avoid this copy. - - uint256[] memory input_values = new uint256[](NUM_INPUTS); - for (uint256 i = 0 ; i < NUM_INPUTS; i++) { - input_values[i] = inputs[i]; - } - return Groth16AltBN128.verify(_vk, proof, input_values); - } -} diff --git a/zeth_contracts/contracts/Groth16BLS12_377Mixer.sol b/zeth_contracts/contracts/Groth16BLS12_377Mixer.sol deleted file mode 100644 index cfa0aec2f..000000000 --- a/zeth_contracts/contracts/Groth16BLS12_377Mixer.sol +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd -// -// SPDX-License-Identifier: LGPL-3.0+ - -pragma solidity ^0.5.0; -pragma experimental ABIEncoderV2; - -import "./Groth16BLS12_377.sol"; -import "./BLS12_377MixerBase.sol"; - -// Instance of BLS12_377MixerBase implementing the Groth16 verifier for the -// bls12-377 pairing. -contract Groth16BLS12_377Mixer is BLS12_377MixerBase -{ - constructor( - uint256 mk_depth, - address token, - uint256[] memory vk - ) - public - BLS12_377MixerBase(mk_depth, token, vk) - { - } - - function verify_zk_proof( - uint256[] memory proof, - uint256[NUM_INPUTS] memory inputs - ) - internal - returns (bool) - { - // Convert the statically sized inputs to a dynamic array - // expected by the verifyer. - - // TODO: mechanism to pass static-sized input arrays to generic - // verifier functions to avoid this copy. - - uint256[] memory input_values = new uint256[](NUM_INPUTS); - for (uint256 i = 0 ; i < NUM_INPUTS; i++) { - input_values[i] = inputs[i]; - } - return Groth16BLS12_377.verify(_vk, proof, input_values); - } -} diff --git a/zeth_contracts/contracts/Groth16BLS12_377_test.sol b/zeth_contracts/contracts/Groth16BLS12_377_test.sol deleted file mode 100644 index f6888a853..000000000 --- a/zeth_contracts/contracts/Groth16BLS12_377_test.sol +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd -// -// SPDX-License-Identifier: LGPL-3.0+ - -pragma solidity ^0.5.0; - -import "./Groth16BLS12_377.sol"; - -contract Groth16BLS12_377_test -{ - uint256[] private _vk; - - function test_verify( - uint256[] memory vk, - uint256[] memory proof, - uint256[] memory inputs) - public - returns(bool) - { - _vk = vk; - return Groth16BLS12_377.verify(_vk, proof, inputs); - } -} diff --git a/zeth_contracts/contracts/Groth16AltBN128.sol b/zeth_contracts/contracts/LibGroth16AltBN128.sol similarity index 91% rename from zeth_contracts/contracts/Groth16AltBN128.sol rename to zeth_contracts/contracts/LibGroth16AltBN128.sol index 40224cd4f..ff0144422 100644 --- a/zeth_contracts/contracts/Groth16AltBN128.sol +++ b/zeth_contracts/contracts/LibGroth16AltBN128.sol @@ -1,10 +1,10 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ -pragma solidity ^0.5.0; +pragma solidity ^0.8.0; -library Groth16AltBN128 +library LibGroth16AltBN128 { // The structure of the verification key differs from the reference paper. // It doesn't contain any element of GT, but only elements of G1 and G2 @@ -14,15 +14,10 @@ library Groth16AltBN128 // contract code. // Used by client code to verify that inputs are in the correct field. - uint256 internal constant PRIME_R = + uint256 internal constant _PRIME_R = // solhint-disable-next-line max-line-length 21888242871839275222246405745257275088548364400416034343698204186575808495617; - // Return the value PRIME_R, the characteristic of the scalar field. - function scalar_r() internal pure returns (uint256) { - return PRIME_R; - } - // Fr elements and Fq elements can be held in a single uint256. Therefore // G1 elements require 2 uint256s. G2 elements have coordinates in Fp2, and // therefore occupy 4 uint256s. Based on this, the offsets and slot numbers @@ -41,7 +36,7 @@ library Groth16AltBN128 // uint256[2] c, (offset 06 - 0x06) // (offset 08 - 0x08) - function verify( + function _verify( uint256[] storage vk, uint256[] memory proof, uint256[] memory input @@ -61,17 +56,18 @@ library Groth16AltBN128 // Ensure that all inputs belong to the scalar field. for (uint256 i = 0 ; i < numInputs; i++) { - require(input[i] < PRIME_R, "Input is not in scalar field"); + require(input[i] < _PRIME_R, "Input is not in scalar field"); } // 1. Compute the linear combination // vk_x = \sum_{i=0}^{l} a_i * vk.ABC[i], vk_x in G1. // // ORIGINAL CODE: - // Pairing.G1Point memory vk_x = vk.ABC[0]; // a_0 = 1 + // LibPairing.G1Point memory vk_x = vk.ABC[0]; // a_0 = 1 // for (uint256 i = 0; i < input.length; i++) { // vk_x = - // Pairing.add(vk_x, Pairing.mul(vk.ABC[i + 1], input[i])); + // LibPairing._addG1(vk_x, + // LibPairing._scalarMulG1(vk.ABC[i + 1], input[i])); // } // // The linear combination loop was the biggest cost center of the mixer @@ -113,10 +109,10 @@ library Groth16AltBN128 uint256 vk_slot_num; assembly { - let g := sub(gas, 2000) + let g := sub(gas(), 2000) // Compute starting slot of vk data. - mstore(pad, vk_slot) + mstore(pad, vk.slot) vk_slot_num := keccak256(pad, 0x20) let abc_slot_num := add(vk_slot_num, 0x0a) @@ -173,8 +169,9 @@ library Groth16AltBN128 // e(vk_x, -g2) * e(vk.Alpha, vk.Minus_Beta) * // e(negate(Proof.A), Proof.B) * e(Proof.C, vk.Minus_Delta) == 1 // - // See Pairing.pairing(). Note terms have been re-ordered since vk_x is - // already at offset 0x00. Memory is laid out: + // See LibPairing.pairing(). + // Note terms have been re-ordered since vk_x is already at offset + // 0x00. Memory is laid out: // // 0x0300 // 0x0280 - verifyKey.Minus_Delta in G2 @@ -233,7 +230,7 @@ library Groth16AltBN128 mstore(add(pad, 0x2c0), sload(add(vk_slot_num, 8))) mstore(add(pad, 0x2e0), sload(add(vk_slot_num, 9))) - success := call(sub(gas, 2000), 8, 0, pad, 0x300, pad, 0x20) + success := call(sub(gas(), 2000), 8, 0, pad, 0x300, pad, 0x20) } require( diff --git a/zeth_contracts/contracts/Groth16BLS12_377.sol b/zeth_contracts/contracts/LibGroth16BLS12_377.sol similarity index 97% rename from zeth_contracts/contracts/Groth16BLS12_377.sol rename to zeth_contracts/contracts/LibGroth16BLS12_377.sol index 056977d34..746285eeb 100644 --- a/zeth_contracts/contracts/Groth16BLS12_377.sol +++ b/zeth_contracts/contracts/LibGroth16BLS12_377.sol @@ -1,10 +1,10 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ -pragma solidity ^0.5.0; +pragma solidity ^0.8.0; -library Groth16BLS12_377 +library LibGroth16BLS12_377 { // Fr elements occupy 1 uint256, and Fq elements occupy 2 uint256s. // Therefore G1 elements occupy 4 uint256s. G2 elements have coordinates in @@ -42,7 +42,7 @@ library Groth16BLS12_377 // uint256[4] c, // offset 0x0c (0x180 bytes) // // offset 0x10 (0x200 bytes) - function verify( + function _verify( uint256[] storage vk, uint256[] memory proof, uint256[] memory inputs @@ -91,10 +91,10 @@ library Groth16BLS12_377 assembly { // Copied from bn implemenation in zeth. - let g := sub(gas, 2000) + let g := sub(gas(), 2000) // Compute starting slot of the vk data and abc data. - mstore(pad, vk_slot) + mstore(pad, vk.slot) vk_slot_num := keccak256(pad, 0x20) let abc_slot_num := add(vk_slot_num, 0x14) @@ -255,7 +255,7 @@ library Groth16BLS12_377 mstore(add(pad, 0x5e0), sload(add(vk_slot_num, 0x13))) // Call ecpairing - result := call(gas, 0xc6, 0, pad, 0x600, pad, 0x20) + result := call(gas(), 0xc6, 0, pad, 0x600, pad, 0x20) } return 1 == pad[0]; diff --git a/zeth_contracts/contracts/MiMC7.sol b/zeth_contracts/contracts/LibMiMC.sol similarity index 56% rename from zeth_contracts/contracts/MiMC7.sol rename to zeth_contracts/contracts/LibMiMC.sol index 73bebdf0a..bd18ffb6e 100644 --- a/zeth_contracts/contracts/MiMC7.sol +++ b/zeth_contracts/contracts/LibMiMC.sol @@ -1,8 +1,8 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ -pragma solidity ^0.5.0; +pragma solidity ^0.8.0; /// Reference papers: /// @@ -16,12 +16,45 @@ pragma solidity ^0.5.0; /// Section: "Miyaguchi–Preneel" // solhint-disable-next-line max-line-length /// -library MiMC7 +/// +/// \[ZETHSPEC]: +/// "Zeth Protocol Specification", Clearmatics R&D, +/// +library LibMiMC { - function hash(bytes32 x, bytes32 y) internal pure returns (bytes32 out) { - // See [AGRRT16]: + function _hashAltBN128(bytes32 x, bytes32 y) + internal + pure + returns (bytes32 out) { + // Use exponent 17 over 65 rounds (see [ZETHSPEC]) + return _hash_e17( + x, + y, + // solhint-disable-next-line max-line-length + 21888242871839275222246405745257275088548364400416034343698204186575808495617, + 65); + } + + function _hashBLS12_377(bytes32 x, bytes32 y) + internal + pure + returns (bytes32 out) { + // Use exponent 17 over 62 rounds (see [ZETHSPEC]) + return _hash_e17( + x, + y, + // solhint-disable-next-line max-line-length + 8444461749428370424248824938781546531375899335154063827935233455917409239041, + 62); + } + + function _hash_e17(bytes32 x, bytes32 y, uint256 r, uint8 rounds) + private + pure + returns (bytes32 out) { + // See [AGRRT16] and [ZETHSPEC]: // The round function is: - // F_i(a, key, rc_i) -> a^7 + key + rc + // F_i(a, key, rc_i) -> a^17 + key + rc // // where: // rc_0 = 0 @@ -32,20 +65,20 @@ library MiMC7 assembly { - // solhint-disable-next-line max-line-length - let r := 21888242871839275222246405745257275088548364400416034343698204186575808495617 - // Perform round 0 with x + y (rc = 0 in first round) let a := addmod(x, y, r) let a2 := mulmod(a, a, r) - a := mulmod(mulmod(mulmod(a2, a2, r), a2, r), a, r) + let a4 := mulmod(a2, a2, r) + let a8 := mulmod(a4, a4, r) + a := mulmod(mulmod(a8, a8, r), a, r) // Write round constant seed to pad at 0x00, where keccak256 will // be applied iteratively // solhint-disable-next-line max-line-length mstore(0x0, 0xdec937b7fa8db3de380427a8cc947bfab68514522c3439cfa2e9965509836814) - for {let j := 0} slt(j, 90) {j := add(j,1)} { + rounds := sub(rounds, 1) + for {let j := 0} slt(j, rounds) {j := add(j,1)} { // roundConstant = H(roundConstant); // Derive the (round) constants by iterative hash on the seed @@ -55,7 +88,9 @@ library MiMC7 // a = (outPermutation + roundConstant + key) ^ 7 mod r a := addmod(addmod(a, roundConstant, r), y, r) a2 := mulmod(a, a, r) - a := mulmod(mulmod(mulmod(a2, a2, r), a2, r), a, r) + a4 := mulmod(a2, a2, r) + a8 := mulmod(a4, a4, r) + a := mulmod(mulmod(a8, a8, r), a, r) } // In MiMC, the final round output is summed with the round key diff --git a/zeth_contracts/contracts/OTSchnorrVerifier.sol b/zeth_contracts/contracts/LibOTSchnorrVerifier.sol similarity index 69% rename from zeth_contracts/contracts/OTSchnorrVerifier.sol rename to zeth_contracts/contracts/LibOTSchnorrVerifier.sol index 5a557345d..041dee97e 100644 --- a/zeth_contracts/contracts/OTSchnorrVerifier.sol +++ b/zeth_contracts/contracts/LibOTSchnorrVerifier.sol @@ -1,8 +1,8 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ -pragma solidity ^0.5.0; +pragma solidity ^0.8.0; /// Reference paper: /// \[Bel07]: @@ -11,15 +11,15 @@ pragma solidity ^0.5.0; /// Mihir Bellare, Sarah Shoup, /// International Workshop on Public Key Cryptography, 2007, /// -library OTSchnorrVerifier { +library LibOTSchnorrVerifier { - function verify( + function _verify( uint256 vk0, uint256 vk1, uint256 vk2, uint256 vk3, uint256 sigma, - bytes32 hash_to_be_signed + bytes32 hashToBeSigned ) internal returns (bool) @@ -27,18 +27,20 @@ library OTSchnorrVerifier { // Original code: // // bytes32 h_bytes = sha256( - // abi.encodePacked(vk[2], vk[3], hash_to_be_signed)); + // abi.encodePacked(vk[2], vk[3], hashToBeSigned)); // uint256 h = uint256(h_bytes); // - // // X = g^{x}, where g represents a generator of the cyclic group G - // Pairing.G1Point memory X = Pairing.G1Point(vk[0], vk[1]); + // // X = g^{x}, where g is a generator of the cyclic group G + // LibPairing.G1Point memory X = LibPairing.G1Point(vk[0], vk[1]); // // Y = g^{y} - // Pairing.G1Point memory Y = Pairing.G1Point(vk[2], vk[3]); + // LibPairing.G1Point memory Y = LibPairing.G1Point(vk[2], vk[3]); // // // S = g^{sigma} - // Pairing.G1Point memory S = Pairing.mul(Pairing.P1(), sigma); + // LibPairing.G1Point memory S = + // LibPairing._scalarMulG1(LibPairing._genG1(), sigma); // // S_comp = g^{y + xh} - // Pairing.G1Point memory S_comp = Pairing.add(Y, Pairing.mul(X, h)); + // LibPairing.G1Point memory S_comp = + // LibPairing._addG1(Y, LibPairing._scalarMulG1(X, h)); // // // Check that g^{sigma} == g^{y + xh} // return (S.X == S_comp.X && S.Y == S_comp.Y); @@ -48,21 +50,21 @@ library OTSchnorrVerifier { assembly { - let g := sub(gas, 2000) + let g := sub(gas(), 2000) // pad: - // 0x40 hash_to_be_signed + // 0x40 hashToBeSigned // 0x20 Y[1] // 0x00 Y[0] // Compute sha256 into 0x40 mstore(pad, vk2) mstore(add(pad, 0x20), vk3) - mstore(add(pad, 0x40), hash_to_be_signed) + mstore(add(pad, 0x40), hashToBeSigned) pop(call(g, 2, 0, pad, 0x60, add(pad, 0x80), 0x20)) // pad: - // 0x80 h = sha256(Y || hash_to_be_signed) + // 0x80 h = sha256(Y || hashToBeSigned) // 0x60 // 0x40 // 0x20 Y[1] @@ -88,7 +90,7 @@ library OTSchnorrVerifier { // 0x40 // 0x20 (Y + h.X)[1] // 0x00 (Y + h.X)[0] - // copy P1 and sigma (see Pairing.sol for values) + // copy _genG1 and sigma (see LibPairing.sol for values) mstore(add(pad, 0x40), 1) mstore(add(pad, 0x60), 2) @@ -96,8 +98,8 @@ library OTSchnorrVerifier { // pad: // 0x80 sigma - // 0x60 P1[1] - // 0x40 P1[0] + // 0x60 _genG1[1] + // 0x40 _genG1[0] // 0x20 (Y + h.X)[1] // 0x00 (Y + h.X)[0] // call bn256ScalarMul(in: 0x40, out: 0x40) @@ -105,8 +107,8 @@ library OTSchnorrVerifier { pop(call(g, 7, 0, x_location, 0x60, x_location, 0x40)) // pad: - // 0x60 sigma.P1[1] - // 0x40 sigma.P1[0] + // 0x60 sigma._genG1[1] + // 0x40 sigma._genG1[0] // 0x20 (Y + h.X)[1] // 0x00 (Y + h.X)[0] } diff --git a/zeth_contracts/contracts/Pairing.sol b/zeth_contracts/contracts/LibPairing.sol similarity index 88% rename from zeth_contracts/contracts/Pairing.sol rename to zeth_contracts/contracts/LibPairing.sol index 54fcbde94..f09064c26 100644 --- a/zeth_contracts/contracts/Pairing.sol +++ b/zeth_contracts/contracts/LibPairing.sol @@ -1,8 +1,8 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ -pragma solidity ^0.5.0; +pragma solidity ^0.8.0; /// Several pairing-related utility functions. /// @@ -13,7 +13,7 @@ pragma solidity ^0.5.0; /// gas and costs: // solhint-disable-next-line /// https://github.com/ethereum/go-ethereum/blob/master/params/protocol_params.go -library Pairing { +library LibPairing { struct G1Point { uint256 X; @@ -29,12 +29,12 @@ library Pairing { } // Return the generator of G1 - function P1() internal pure returns (G1Point memory) { + function _genG1() internal pure returns (G1Point memory) { return G1Point(1, 2); } // Return the generator of G2 - function P2() internal pure returns (G2Point memory) { + function _genG2() internal pure returns (G2Point memory) { return G2Point( // solhint-disable-next-line 11559732032986387107991004021392285783925812861821192530917403151452391805634, @@ -47,7 +47,11 @@ library Pairing { } // Return the negation of p, i.e. p.add(p.negate()) should be zero. - function negate(G1Point memory p) internal pure returns (G1Point memory) { + function _negateG2(G1Point memory p) + internal + pure + returns (G1Point memory) + { // The prime q in the base field F_q for G1 // solhint-disable-next-line uint256 q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; @@ -57,7 +61,7 @@ library Pairing { } // Return the sum of two points of G1 - function add(G1Point memory p1, G1Point memory p2) + function _addG1(G1Point memory p1, G1Point memory p2) internal returns (G1Point memory r) { @@ -69,7 +73,7 @@ library Pairing { bool success; assembly { // Call bn256Add([p1.X, p1.Y, p2.X, p2.Y]) - success := call(sub(gas, 2000), 6, 0, input, 0x80, r, 0x40) + success := call(sub(gas(), 2000), 6, 0, input, 0x80, r, 0x40) // Use "invalid" to make gas estimation work //switch success case 0 { invalid } } @@ -82,7 +86,7 @@ library Pairing { // Return the product of a point on G1 and a scalar, i.e. // p == p.mul(1) and p.add(p) == p.mul(2) for all points p. - function mul(G1Point memory p, uint256 s) + function _scalarMulG1(G1Point memory p, uint256 s) internal returns (G1Point memory r) { @@ -93,7 +97,7 @@ library Pairing { bool success; assembly { // Call bn256ScalarMul([p.X, p.Y, s]) - success := call(sub(gas, 2000), 7, 0, input, 0x60, r, 0x40) + success := call(sub(gas(), 2000), 7, 0, input, 0x60, r, 0x40) // Use "invalid" to make gas estimation work //switch success case 0 { invalid } } @@ -104,7 +108,7 @@ library Pairing { } // Return the result of computing the pairing check - function pairing(G1Point[] memory p1, G2Point[] memory p2) + function _pairing(G1Point[] memory p1, G2Point[] memory p2) internal returns (bool) { @@ -153,7 +157,7 @@ library Pairing { // in the form: e(g1, g2) = e(g'1, g'2), we need to call the // precompiled bn256Pairing on input [(g1, g2), (neg(g'1), g'2)] success := call( - sub(gas, 2000), + sub(gas(), 2000), 8, 0, add(input, 0x20), @@ -174,27 +178,29 @@ library Pairing { } // Convenience method for a pairing check for two pairs. - function pairingProd2( + function _pairingProd2( G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2) internal - returns (bool) { + returns (bool) + { G1Point[] memory p1 = new G1Point[](2); G2Point[] memory p2 = new G2Point[](2); p1[0] = a1; p1[1] = b1; p2[0] = a2; p2[1] = b2; - return pairing(p1, p2); + return _pairing(p1, p2); } // Convenience method for a pairing check for three pairs. - function pairingProd3( + function _pairingProd3( G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2, G1Point memory c1, G2Point memory c2) internal - returns (bool) { + returns (bool) + { G1Point[] memory p1 = new G1Point[](3); G2Point[] memory p2 = new G2Point[](3); p1[0] = a1; @@ -203,17 +209,18 @@ library Pairing { p2[0] = a2; p2[1] = b2; p2[2] = c2; - return pairing(p1, p2); + return _pairing(p1, p2); } // Convenience method for a pairing check for 4 pairs. - function pairingProd4( + function _pairingProd4( G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2, G1Point memory c1, G2Point memory c2, G1Point memory d1, G2Point memory d2) internal - returns (bool) { + returns (bool) + { G1Point[] memory p1 = new G1Point[](4); G2Point[] memory p2 = new G2Point[](4); p1[0] = a1; @@ -224,6 +231,6 @@ library Pairing { p2[1] = b2; p2[2] = c2; p2[3] = d2; - return pairing(p1, p2); + return _pairing(p1, p2); } } diff --git a/zeth_contracts/contracts/MerkleTreeMiMC7_test.sol b/zeth_contracts/contracts/MerkleTreeMiMC7_test.sol deleted file mode 100644 index 39713c7b2..000000000 --- a/zeth_contracts/contracts/MerkleTreeMiMC7_test.sol +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd -// -// SPDX-License-Identifier: LGPL-3.0+ - -pragma solidity ^0.5.0; - -import "./BaseMerkleTree.sol"; -import "./MiMC7.sol"; - -/// The Merkle tree implementation must trade-off complexity, storage, -/// initialization cost, and update & root computation cost. -/// -/// This implementation stores all leaves and nodes, skipping those that have -/// not been populated yet. The final entry in each layer stores that layer's -/// default value. -contract MerkleTreeMiMC7_test is BaseMerkleTree -{ - constructor(uint treeDepth) public BaseMerkleTree(treeDepth) { - } - - /// Add some leaves, computing the root, then adding more leaves and - /// recomputing the root. Returns the full set of nodes at the end. This - /// allows testing of the update code paths for any starting / finishing - /// state combination. - function testAddLeaves( - bytes32[] memory first, - bytes32[] memory second - ) - public - returns (bytes32) - { - for (uint i = 0 ; i < first.length ; ++i) { - insert(first[i]); - } - bytes32 root = recomputeRoot(first.length); - - for (uint i = 0 ; i < second.length ; ++i) { - insert(second[i]); - } - root = recomputeRoot(second.length); - return root; - } - - /// Use MiMC7 as the Merkle tree hash function. - function hash(bytes32 left, bytes32 right) internal returns(bytes32) { - return MiMC7.hash(left, right); - } -} diff --git a/zeth_contracts/contracts/MerkleTreeSha256.sol b/zeth_contracts/contracts/MerkleTreeSha256.sol deleted file mode 100644 index 5cf067e4b..000000000 --- a/zeth_contracts/contracts/MerkleTreeSha256.sol +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd -// -// SPDX-License-Identifier: LGPL-3.0+ - -pragma solidity ^0.5.0; - -import "./BaseMerkleTree.sol"; - -contract MerkleTreeSha256 is BaseMerkleTree { - - constructor(uint256 treeDepth) public BaseMerkleTree(treeDepth) { - } - - /// Returns the current merkle tree - function getTree() internal view returns (bytes32[] memory) { - uint256 nbNodes = 2**(depth + 1) - 1; - bytes32[] memory tmpTree = new bytes32[](nbNodes); - - // Dump the leaves in the right indexes in the tree - for (uint256 i = 0; i < nbLeaves; i++) { - tmpTree[(nbLeaves - 1) + i] = leaves[i]; - } - - // Compute the internal nodes of the merkle tree - for (uint256 i = nbLeaves - 2; i > 0; i--) { - tmpTree[i] = sha256( - abi.encodePacked(tmpTree[i*2+1], tmpTree[2*(i+1)])); - } - - // Compute the merkle root - tmpTree[0] = sha256(abi.encodePacked(tmpTree[1], tmpTree[2])); - - return tmpTree; - } - - /// Returns the root of the merkle tree - function getRoot() internal view returns(bytes32) { - return getTree()[0]; - } -} diff --git a/zeth_contracts/contracts/MiMC31.sol b/zeth_contracts/contracts/MiMC31.sol deleted file mode 100644 index 7f04f2dc7..000000000 --- a/zeth_contracts/contracts/MiMC31.sol +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd -// -// SPDX-License-Identifier: LGPL-3.0+ - -pragma solidity ^0.5.0; - -/// Reference papers: -/// -/// \[AGRRT16]: -/// "MiMC: Efficient Encryption and Cryptographic Hashing with Minimal -/// Multiplicative Complexity", Martin Albrecht, Lorenzo Grassi, Christian -/// Rechberger, Arnab Roy, and Tyge Tiessen, ASIACRYPT 2016, -/// -/// -/// "One-way compression function" -/// Section: "Miyaguchi–Preneel" -// solhint-disable-next-line max-line-length -/// -/// -/// Implementation of MiMC hash targeting BLS12-377 Fr. This means: -// solhint-disable-next-line max-line-length -/// r = 8444461749428370424248824938781546531375899335154063827935233455917409239041 -/// e (exponent) = 31, so that e=2^t-1 (t=5) and 1 == gcd(e, r-1) -/// rounds = 51 -/// -/// See MiMC7.sol for details. -library MiMC31 -{ - function hash(bytes32 x, bytes32 y) internal pure returns(bytes32 out) { - // Round function (see [AGRRT16]): - // F_i(a, key, rc_i) -> (a + key + rc)^31 - // - // where: - // rc_0 = 0 - // rc_1 = keccak(seed) - // rc_i = keccak(rc_{i-1}), for i = 2, ... - - assembly - { - // solhint-disable-next-line max-line-length - let r := 8444461749428370424248824938781546531375899335154063827935233455917409239041 - - // Perform round 0, in which rc = 0 - let a := addmod(x, y, r) - let a2 := mulmod(a, a, r) - let a4 := mulmod(a2, a2, r) - let a8 := mulmod(a4, a4, r) - let a16 := mulmod(a8, a8, r) - a := mulmod(mulmod(mulmod(mulmod(a16, a8, r), a4, r), a2, r), a, r) - - // Write round constant seed to pad at 0x00, where keccak256 will - // be applied iteratively - // solhint-disable-next-line max-line-length - mstore(0x0, 0xdec937b7fa8db3de380427a8cc947bfab68514522c3439cfa2e9965509836814) - - for {let i := 0} slt(i, 50) {i := add(i,1)} { - - // Compute rc_i, and store it back in the pad - let roundConstant := keccak256(0x0, 32) - mstore(0x0, roundConstant) - - // Apply F_i - a := addmod(addmod(a, roundConstant, r), y, r) - a2 := mulmod(a, a, r) - a4 := mulmod(a2, a2, r) - a8 := mulmod(a4, a4, r) - a16 := mulmod(a8, a8, r) - a := mulmod( - mulmod(mulmod(mulmod(a16, a8, r), a4, r), a2, r), a, r) - } - - // Sum output with key, as described in [AGRRT16]. - a := addmod(a, y, r) - - // Myjaguchi-Preneel OWCF is then applied, which adds the key and - // message to the output of MiMC. - out := addmod(addmod(a, x, r), y, r) - } - } -} diff --git a/zeth_contracts/contracts/MiMC_test.sol b/zeth_contracts/contracts/MiMC_test.sol deleted file mode 100644 index e536c0bad..000000000 --- a/zeth_contracts/contracts/MiMC_test.sol +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd -// -// SPDX-License-Identifier: LGPL-3.0+ - -pragma solidity ^0.5.0; - -import "./MiMC7.sol"; -import "./MiMC31.sol"; - -/// Contract to test the MiMC libraries -contract MiMC_test -{ - /// Test function for MiMC7 - function test_mimc7(bytes32 x, bytes32 y) public returns (bytes32) { - return MiMC7.hash(x, y); - } - - /// Test function for MiMC31 - function test_mimc31(bytes32 x, bytes32 y) public returns (bytes32) { - return MiMC31.hash(x, y); - } -} diff --git a/zeth_contracts/contracts/Migrations.sol b/zeth_contracts/contracts/Migrations.sol index fd29c98bc..e2d399a11 100644 --- a/zeth_contracts/contracts/Migrations.sol +++ b/zeth_contracts/contracts/Migrations.sol @@ -1,14 +1,14 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ -pragma solidity ^0.5.0; +pragma solidity ^0.8.0; contract Migrations { address public owner; uint public last_completed_migration; - constructor() public { + constructor() { owner = msg.sender; } diff --git a/zeth_contracts/contracts/MixerBase.sol b/zeth_contracts/contracts/MixerBase.sol deleted file mode 100644 index 5cbe29078..000000000 --- a/zeth_contracts/contracts/MixerBase.sol +++ /dev/null @@ -1,393 +0,0 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd -// -// SPDX-License-Identifier: LGPL-3.0+ - -pragma solidity ^0.5.0; -pragma experimental ABIEncoderV2; - -import "./Tokens.sol"; -import "./OTSchnorrVerifier.sol"; -import "./BaseMerkleTree.sol"; - -/// MixerBase implements the functions shared across all Mixers (regardless -/// which zkSNARK is used) -contract MixerBase is BaseMerkleTree, ERC223ReceivingContract -{ - // The roots of the different updated trees - mapping(bytes32 => bool) private _roots; - - // The public list of nullifiers (prevents double spend) - mapping(bytes32 => bool) private _nullifiers; - - // Structure of the verification key and proofs is opaque, determined by - // zk-snark verification library. - uint256[] internal _vk; - - // Contract variable that indicates the address of the token contract - // If token = address(0) then the mixer works with ether - address private _token; - - // JoinSplit description, gives the number of inputs (nullifiers) and - // outputs (commitments/ciphertexts) to receive and process. - // - // IMPORTANT NOTE: We need to employ the same JS configuration than the one - // used in the cpp prover. Here we use 2 inputs and 2 outputs (it is a 2-2 - // JS). - uint256 internal constant JSIN = 2; // Number of nullifiers - uint256 internal constant JSOUT = 2; // Number of commitments/ciphertexts - - // Size of the public values in bits - uint256 internal constant PUBLIC_VALUE_BITS = 64; - - // Public values mask - uint256 internal constant PUBLIC_VALUE_MASK = (1 << PUBLIC_VALUE_BITS) - 1; - - // Total number of bits for public values. Digest residual bits appear - // after these. - uint256 internal constant TOTAL_PUBLIC_VALUE_BITS = 2 * PUBLIC_VALUE_BITS; - - uint256 internal constant DIGEST_LENGTH = 256; - - // Number of hash digests in the primary inputs: - // 1 (the root) - // 2 * JSIN (nullifier and message auth tag per JS input) - // JSOUT (commitment per JS output) - uint256 internal constant NUM_HASH_DIGESTS = 1 + 2 * JSIN; - - // All code assumes that public values and residual bits can be encoded in - // a single field element. - uint256 internal constant NUM_FIELD_RESIDUAL = 1; - - // The number of public inputs are: - // - 1 (the root) - // - JSIN (the nullifiers) - // - JSOUT (the commitments) - // - 1 (hsig) - // - JsIn (the message auth. tags) - // - NUM_FIELD_RESIDUAL (the residual bits not fitting in a single field - // element and the in and out public values) - uint256 internal constant NUM_INPUTS = - 1 + JSOUT + NUM_HASH_DIGESTS + NUM_FIELD_RESIDUAL; - - // The unit used for public values (ether in and out), in Wei. Must match - // the python wrappers. Use Szabos (10^12 Wei). - uint64 internal constant PUBLIC_UNIT_VALUE_WEI = 1 szabo; - - event LogMix( - bytes32 root, - bytes32[JSIN] nullifiers, - bytes32[JSOUT] commitments, - bytes[JSOUT] ciphertexts - ); - - /// Debug only - event LogDebug(string message); - - /// Constructor - constructor(uint256 depth, address token_address, uint256[] memory vk) - public - BaseMerkleTree(depth) - { - bytes32 initialRoot = nodes[0]; - _roots[initialRoot] = true; - _vk = vk; - _token = token_address; - } - - /// Function allowing external users of the contract to retrieve some of - /// the constants used in the mixer (since the solidity interfaces do not - /// export this information as-of the current version). The intention is - /// that external users and contraacts can query this function and ensure - /// that they are compatible with the mixer configurations. - /// - /// Returns the number of input notes, the number of output notes and the - /// total number of primary inputs. - function get_constants() - external - pure - returns ( - uint256 js_in_out, - uint256 js_out_out, - uint256 num_inputs_out - ) - { - js_in_out = JSIN; - js_out_out = JSOUT; - num_inputs_out = NUM_INPUTS; - } - - /// This function is used to execute payments in zero knowledge. - /// The format of `proof` is internal to the zk-snark library. - /// The `inputs` array is the set of scalar inputs to the proof. - /// We assume that each input occupies a single uint256. - function mix( - uint256[] memory proof, - uint256[4] memory vk, - uint256 sigma, - uint256[NUM_INPUTS] memory inputs, - bytes[JSOUT] memory ciphertexts - ) - public - payable - { - // 1. Check the root and the nullifiers - bytes32[JSIN] memory nullifiers; - check_mkroot_nullifiers_hsig_append_nullifiers_state( - vk, inputs, nullifiers); - - // 2.a Verify the signature on the hash of data_to_be_signed - bytes32 hash_to_be_signed = sha256( - abi.encodePacked( - uint256(msg.sender), - // Unfortunately, we have to unroll this for now. We could - // replace encodePacked with a custom function but this would - // increase complexity and possibly gas usage. - ciphertexts[0], - ciphertexts[1], - proof, - inputs - ) - ); - require( - OTSchnorrVerifier.verify( - vk[0], vk[1], vk[2], vk[3], sigma, hash_to_be_signed), - "Invalid signature: Unable to verify the signature correctly" - ); - - // 2.b Verify the proof - require( - verify_zk_proof(proof, inputs), - "Invalid proof: Unable to verify the proof correctly" - ); - - // 3. Append the commitments to the tree - bytes32[JSOUT] memory commitments; - assemble_commitments_and_append_to_state(inputs, commitments); - - // 4. Add the new root to the list of existing roots - bytes32 new_merkle_root = recomputeRoot(JSOUT); - add_merkle_root(new_merkle_root); - - // 5. Emit the all Mix data - emit LogMix( - new_merkle_root, - nullifiers, - commitments, - ciphertexts - ); - - // 6. Get the public values in Wei and modify the state depending on - // their values - process_public_values(inputs); - } - - /// This function is used to extract the public values (vpub_in, vpub_out) - /// from the residual field element(S) - function assemble_public_values(uint256 residual_bits) - internal - pure - returns ( - uint256 vpub_in, - uint256 vpub_out - ) - { - // vpub_out and vpub_in occupy the first and second PUBLIC_VALUE_BITS - vpub_out = (residual_bits & PUBLIC_VALUE_MASK) * PUBLIC_UNIT_VALUE_WEI; - vpub_in = ((residual_bits >> PUBLIC_VALUE_BITS) & PUBLIC_VALUE_MASK) - * PUBLIC_UNIT_VALUE_WEI; - } - - /// This function is used to reassemble hsig given the primary_inputs. - /// To do so, we extract the remaining bits of hsig from the residual field - /// element(S) and combine them with the hsig field element - function assemble_hsig( - uint256[NUM_INPUTS] memory primary_inputs - ) - internal - pure - returns(bytes32 hsig) - { - // The h_sig residual bits are after the JSIN authentication tags and - // JSIN nullifier bits. - return extract_bytes32( - primary_inputs[1 + JSIN + JSOUT], - primary_inputs[1 + JSOUT + NUM_HASH_DIGESTS], - 2 * JSIN - ); - } - - /// This function is used to reassemble the nullifiers given the nullifier - /// index [0, JSIN[ and the primary_inputs To do so, we extract the - /// remaining bits of the nullifier from the residual field element(S) and - /// combine them with the nullifier field element - function assemble_nullifier( - uint256 index, - uint256[NUM_INPUTS] memory primary_inputs - ) - internal - pure - returns(bytes32 nf) - { - // We first check that the nullifier we want to retrieve exists - require(index < JSIN, "nullifier index overflow"); - - // Nullifier residual bits follow the JSIN message authentication tags. - return extract_bytes32( - primary_inputs[1 + JSOUT + index], - primary_inputs[1 + JSOUT + NUM_HASH_DIGESTS], - JSIN + index - ); - } - - // ====================================================================== - // Reminder: Remember that the primary inputs are ordered as follows: - // - // [Root, CommitmentS, NullifierS, h_sig, h_iS, Residual Element(s)] - // - // ie, below is the index mapping of the primary input elements on the - // protoboard: - // - // 0 - // 1 - // ... - // JSOUT - // JSOUT + 1 - // ... - // JSOUT + JSIN - // JSOUT + JSIN + 1 - // JSOUT + JSIN + 2 - // ... - // JSOUT + 2*JSIN + 1 - // JSOUT + 2*JSIN + 2 - // - // The Residual field elements are structured as follows: - // - // 255 128 64 0 - // |||||)|| - // - // where each entry entry after public output and input holds the - // (curve-specific) number residual bits for the corresponding 256 bit - // value. - // ====================================================================== - // - // Utility function to extract a full uint256 from a field element and the - // n-th set of residual bits from `residual`. This function is - // curve-dependent. - function extract_bytes32( - uint256 field_element, - uint256 residual, - uint256 residual_bits_set_idx - ) - internal - pure - returns(bytes32); - - // Implementations must implement the verification algorithm of the - // selected SNARK. - function verify_zk_proof( - uint256[] memory proof, - uint256[NUM_INPUTS] memory inputs - ) - internal - returns (bool); - - /// This function processes the primary inputs to append and check the root - /// and nullifiers in the primary inputs (instance) and modifies the state - /// of the mixer contract accordingly. (ie: Appends the commitments to the - /// tree, appends the nullifiers to the list and so on). - function check_mkroot_nullifiers_hsig_append_nullifiers_state( - uint256[4] memory vk, - uint256[NUM_INPUTS] memory primary_inputs, - bytes32[JSIN] memory nfs) - internal - { - // 1. We re-assemble the full root digest and check it is in the tree - require( - _roots[bytes32(primary_inputs[0])], - "Invalid root: This root doesn't exist" - ); - - // 2. We re-assemble the nullifiers (JSInputs) and check they were not - // already seen. - for (uint256 i = 0; i < JSIN; i++) { - bytes32 nullifier = assemble_nullifier(i, primary_inputs); - require( - !_nullifiers[nullifier], - "Invalid nullifier: This nullifier has already been used" - ); - _nullifiers[nullifier] = true; - - nfs[i] = nullifier; - } - - // 3. We re-compute h_sig, re-assemble the expected h_sig and check - // they are equal (i.e. that h_sig re-assembled was correctly generated - // from vk). - bytes32 expected_hsig = sha256(abi.encodePacked(nfs, vk)); - bytes32 hsig = assemble_hsig(primary_inputs); - require( - expected_hsig == hsig, - "Invalid hsig: This hsig does not correspond to the hash of vk and" - " the nfs" - ); - } - - function assemble_commitments_and_append_to_state( - uint256[NUM_INPUTS] memory primary_inputs, - bytes32[JSOUT] memory comms - ) - internal - { - // We re-assemble the commitments (JSOutputs) - for (uint256 i = 0; i < JSOUT; i++) { - bytes32 current_commitment = bytes32(primary_inputs[1 + i]); - comms[i] = current_commitment; - insert(current_commitment); - } - } - - function process_public_values(uint256[NUM_INPUTS] memory primary_inputs) - internal - { - // We get vpub_in and vpub_out in wei - (uint256 vpub_in, uint256 vpub_out) = assemble_public_values( - primary_inputs[1 + JSOUT + NUM_HASH_DIGESTS]); - - // If the vpub_in is > 0, we need to make sure the right amount is paid - if (vpub_in > 0) { - if (_token != address(0)) { - ERC20 erc20Token = ERC20(_token); - erc20Token.transferFrom(msg.sender, address(this), vpub_in); - } else { - require( - msg.value == vpub_in, - "Wrong msg.value: Value paid is not correct" - ); - } - } else { - // If vpub_in = 0, return incoming Ether to the caller - if (msg.value > 0) { - // solhint-disable-next-line - (bool success, ) = msg.sender.call.value(msg.value)(""); - require(success, "vpub_in return transfer failed"); - } - } - - // If value_pub_out > 0 then we do a withdraw. We retrieve the - // msg.sender and send him the appropriate value IF proof is valid - if (vpub_out > 0) { - if (_token != address(0)) { - ERC20 erc20Token = ERC20(_token); - erc20Token.transfer(msg.sender, vpub_out); - } else { - // solhint-disable-next-line - (bool success, ) = msg.sender.call.value(vpub_out)(""); - require(success, "vpub_out transfer failed"); - } - } - } - - function add_merkle_root(bytes32 root) internal { - _roots[root] = true; - } -} diff --git a/zeth_contracts/contracts/MixerGroth16AltBN128.sol b/zeth_contracts/contracts/MixerGroth16AltBN128.sol new file mode 100644 index 000000000..7fc4953d5 --- /dev/null +++ b/zeth_contracts/contracts/MixerGroth16AltBN128.sol @@ -0,0 +1,39 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +pragma solidity ^0.8.0; + +import "./AbstractMixerAltBN128.sol"; +import "./LibGroth16AltBN128.sol"; + +/// Instance of AbstractMixerAltBN128 implementing the Groth16 verifier for the +/// alt-bn128 pairing. +contract MixerGroth16AltBN128 is AbstractMixerAltBN128 +{ + constructor( + uint256 mkDepth, + address token, + uint256[] memory vk, + address permittedDispatcher, + uint256[2] memory vkHash + ) + AbstractMixerAltBN128(mkDepth, token, vk, permittedDispatcher, vkHash) + { + } + + function _verifyZkProof( + uint256[] memory proof, + uint256 publicInputsHash + ) + internal + override + returns (bool) + { + // Convert the single primary input to a dynamic array + // expected by the verifier. + uint256[] memory inputValues = new uint256[](1); + inputValues[0] = publicInputsHash; + return LibGroth16AltBN128._verify(_vk, proof, inputValues); + } +} diff --git a/zeth_contracts/contracts/MixerGroth16BLS12_377.sol b/zeth_contracts/contracts/MixerGroth16BLS12_377.sol new file mode 100644 index 000000000..9226e09ef --- /dev/null +++ b/zeth_contracts/contracts/MixerGroth16BLS12_377.sol @@ -0,0 +1,39 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +pragma solidity ^0.8.0; + +import "./LibGroth16BLS12_377.sol"; +import "./AbstractMixerBLS12_377.sol"; + +// Instance of AbstractMixerBLS12_377 implementing the Groth16 verifier for the +// bls12-377 pairing. +contract MixerGroth16BLS12_377 is AbstractMixerBLS12_377 +{ + constructor( + uint256 mkDepth, + address token, + uint256[] memory vk, + address permittedDispatcher, + uint256[2] memory vkHash + ) + AbstractMixerBLS12_377(mkDepth, token, vk, permittedDispatcher, vkHash) + { + } + + function _verifyZkProof( + uint256[] memory proof, + uint256 publicInputsHash + ) + internal + override + returns (bool) + { + // Convert the single primary input to a dynamic array + // expected by the verifier. + uint256[] memory input_values = new uint256[](1); + input_values[0] = publicInputsHash; + return LibGroth16BLS12_377._verify(_vk, proof, input_values); + } +} diff --git a/zeth_contracts/contracts/MixerPghr13AltBN128.sol b/zeth_contracts/contracts/MixerPghr13AltBN128.sol new file mode 100644 index 000000000..afecee2a9 --- /dev/null +++ b/zeth_contracts/contracts/MixerPghr13AltBN128.sol @@ -0,0 +1,189 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +pragma solidity ^0.8.0; + +import "./LibPairing.sol"; +import "./AbstractMixerAltBN128.sol"; + +contract MixerPghr13AltBN128 is AbstractMixerAltBN128 +{ + struct VerifyingKey { + LibPairing.G2Point A; + LibPairing.G1Point B; + LibPairing.G2Point C; + LibPairing.G2Point gamma; + LibPairing.G1Point gammaBeta1; + LibPairing.G2Point gammaBeta2; + LibPairing.G2Point Z; + LibPairing.G1Point[] IC; + } + + struct Proof { + LibPairing.G1Point A; + LibPairing.G1Point A_p; + LibPairing.G2Point B; + LibPairing.G1Point B_p; + LibPairing.G1Point C; + LibPairing.G1Point C_p; + LibPairing.G1Point K; + LibPairing.G1Point H; + } + + constructor( + uint256 mkDepth, + address token, + uint256[] memory vk, + address permittedDispatcher, + uint256[2] memory vkHash + ) + AbstractMixerAltBN128(mkDepth, token, vk, permittedDispatcher, vkHash) + { + uint256 vk_words = vk.length; + require(vk_words >= 26, "invalid vk length"); + } + + function _verify( + uint256[] memory inputs, + Proof memory proof + ) + internal + returns (uint) + { + + // Decode _vk into a VerifyKey struct + uint256 vk_words = _vk.length; + require(vk_words >= 26, "invalid vk length"); + uint256 ic_length = (vk_words - 24) / 2; + + VerifyingKey memory vk; + vk.IC = new LibPairing.G1Point[](ic_length); + vk.A = LibPairing.G2Point(_vk[0], _vk[1], _vk[2], _vk[3]); + vk.B = LibPairing.G1Point(_vk[4], _vk[5]); + vk.C = LibPairing.G2Point(_vk[6], _vk[7], _vk[8], _vk[9]); + vk.gamma = LibPairing.G2Point(_vk[10], _vk[11], _vk[12], _vk[13]); + vk.gammaBeta1 = LibPairing.G1Point(_vk[14], _vk[15]); + vk.gammaBeta2 = LibPairing.G2Point(_vk[16], _vk[17], _vk[18], _vk[19]); + vk.Z = LibPairing.G2Point(_vk[20], _vk[21], _vk[22], _vk[23]); + for (uint256 i = 24; i < vk_words ; i += 2) { + vk.IC[(i-24)/2] = LibPairing.G1Point(_vk[i], _vk[i+1]); + } + + // |I_{in}| == input.length, and vk.IC also contains A_0(s). Thus + // ||vk.IC| == input.length + 1 + require( + inputs.length + 1 == vk.IC.length, + "Using strong input consistency, and the input length differs from" + " expected" + ); + + // 1. Compute the linear combination + // vk_x := vk_{IC,0} + \sum_{i=1}^{n} x_i * vk_{IC,i}, vk_x ∈ G1 + // + // E(A_{in}(s)) if the encoding of + // A_{in}(s) = \sum_{k ∈ I_{in}} a_k · A_k(s), + // where I_{in} denotes the indices of the input wires. + // + // |I_{in}| = n here as we assume that we have a vector x of inputs of + // size n. + LibPairing.G1Point memory vk_x = LibPairing.G1Point(0, 0); + for (uint256 i = 0; i < inputs.length; i++) { + vk_x = LibPairing._addG1(vk_x, + LibPairing._scalarMulG1(vk.IC[i + 1], inputs[i])); + } + vk_x = LibPairing._addG1(vk_x, vk.IC[0]); + + // 2. Check the validity of knowledge commitments for A, B, C + // e(π_A, vk_A) = e(π′A, _genG2), e(vk_B, π_B) + // = e(π′_B, _genG2), e(vk_C, π_C) + // = e(π′_C, _genG2), + if (!LibPairing._pairingProd2( + proof.A, vk.A, + LibPairing._negateG2(proof.A_p), LibPairing._genG2()) + ) { + return 1; + } + if (!LibPairing._pairingProd2( + vk.B, proof.B, + LibPairing._negateG2(proof.B_p), LibPairing._genG2()) + ) { + return 2; + } + if (!LibPairing._pairingProd2( + proof.C, vk.C, + LibPairing._negateG2(proof.C_p), LibPairing._genG2()) + ) { + return 3; + } + + // 3. Check same coefficients were used + // e(π_K, vk_γ) = e(vk_x + π_A + π_C, vk_{γβ2}) · e(vk_{γβ1}, π_B) + + bool pairing_check = LibPairing._pairingProd3( + proof.K, + vk.gamma, + LibPairing._negateG2(LibPairing._addG1(vk_x, + LibPairing._addG1(proof.A, proof.C))), + vk.gammaBeta2, + LibPairing._negateG2(vk.gammaBeta1), + proof.B); + if (!pairing_check) { + return 4; + } + + // 4. Check QAP divisibility + // e(vk_x + π_A, π_B) = e(π_H, vk_Z) · e(π_C, _genG2) + pairing_check = LibPairing._pairingProd3( + LibPairing._addG1(vk_x, proof.A), + proof.B, + LibPairing._negateG2(proof.H), + vk.Z, + LibPairing._negateG2(proof.C), + LibPairing._genG2()); + if (!pairing_check) { + return 5; + } + + return 0; + } + + function _verifyZkProof( + uint256[] memory proofData, + uint256 publicInputsHash + ) + internal + override + returns (bool) + { + // Scalar field characteristic + // solhint-disable-next-line + uint256 r = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + + // Slightly redundant + Proof memory proof; + proof.A = LibPairing.G1Point(proofData[0], proofData[1]); + proof.A_p = LibPairing.G1Point(proofData[2], proofData[3]); + proof.B = LibPairing.G2Point( + proofData[4], proofData[5], proofData[6], proofData[7]); + proof.B_p = LibPairing.G1Point(proofData[8], proofData[9]); + proof.C = LibPairing.G1Point(proofData[10], proofData[11]); + proof.C_p = LibPairing.G1Point(proofData[12], proofData[13]); + proof.H = LibPairing.G1Point(proofData[14], proofData[15]); + proof.K = LibPairing.G1Point(proofData[16], proofData[17]); + + require( + publicInputsHash < r, + "Input is not is scalar field" + ); + + uint256[] memory inputs = new uint256[](1); + inputs[0] = publicInputsHash; + uint256 verification_result = _verify(inputs, proof); + if (verification_result != 0) { + return false; + } + + return true; + } +} diff --git a/zeth_contracts/contracts/Pghr13AltBN128Mixer.sol b/zeth_contracts/contracts/Pghr13AltBN128Mixer.sol deleted file mode 100644 index 69a3170e0..000000000 --- a/zeth_contracts/contracts/Pghr13AltBN128Mixer.sol +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd -// -// SPDX-License-Identifier: LGPL-3.0+ - -pragma solidity ^0.5.0; -pragma experimental ABIEncoderV2; - -import "./Pairing.sol"; -import "./AltBN128MixerBase.sol"; - -contract Pghr13AltBN128Mixer is AltBN128MixerBase -{ - struct VerifyingKey { - Pairing.G2Point A; - Pairing.G1Point B; - Pairing.G2Point C; - Pairing.G2Point gamma; - Pairing.G1Point gammaBeta1; - Pairing.G2Point gammaBeta2; - Pairing.G2Point Z; - Pairing.G1Point[] IC; - } - - struct Proof { - Pairing.G1Point A; - Pairing.G1Point A_p; - Pairing.G2Point B; - Pairing.G1Point B_p; - Pairing.G1Point C; - Pairing.G1Point C_p; - Pairing.G1Point K; - Pairing.G1Point H; - } - - constructor( - uint256 mk_depth, - address token, - uint256[] memory vk - ) - public - AltBN128MixerBase(mk_depth, token, vk) - { - uint256 vk_words = vk.length; - require(vk_words >= 26, "invalid vk length"); - } - - function verify( - uint256[num_inputs] memory input, - Proof memory proof - ) - internal - returns (uint) - { - - // Decode _vk into a VerifyKey struct - uint256 vk_words = _vk.length; - require(vk_words >= 26, "invalid vk length"); - uint256 ic_length = (vk_words - 24) / 2; - - VerifyingKey memory vk; - vk.IC = new Pairing.G1Point[](ic_length); - vk.A = Pairing.G2Point(_vk[0], _vk[1], _vk[2], _vk[3]); - vk.B = Pairing.G1Point(_vk[4], _vk[5]); - vk.C = Pairing.G2Point(_vk[6], _vk[7], _vk[8], _vk[9]); - vk.gamma = Pairing.G2Point(_vk[10], _vk[11], _vk[12], _vk[13]); - vk.gammaBeta1 = Pairing.G1Point(_vk[14], _vk[15]); - vk.gammaBeta2 = Pairing.G2Point(_vk[16], _vk[17], _vk[18], _vk[19]); - vk.Z = Pairing.G2Point(_vk[20], _vk[21], _vk[22], _vk[23]); - for (uint256 i = 24; i < vk_words ; i += 2) { - vk.IC[(i-24)/2] = Pairing.G1Point(_vk[i], _vk[i+1]); - } - - // |I_{in}| == input.length, and vk.IC also contains A_0(s). Thus - // ||vk.IC| == input.length + 1 - require( - input.length + 1 == vk.IC.length, - "Using strong input consistency, and the input length differs from" - " expected" - ); - - // 1. Compute the linear combination - // vk_x := vk_{IC,0} + \sum_{i=1}^{n} x_i * vk_{IC,i}, vk_x ∈ G1 - // - // E(A_{in}(s)) if the encoding of - // A_{in}(s) = \sum_{k ∈ I_{in}} a_k · A_k(s), - // where I_{in} denotes the indices of the input wires. - // - // |I_{in}| = n here as we assume that we have a vector x of inputs of - // size n. - Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); - for (uint256 i = 0; i < input.length; i++) { - vk_x = Pairing.add(vk_x, Pairing.mul(vk.IC[i + 1], input[i])); - } - vk_x = Pairing.add(vk_x, vk.IC[0]); - - // 2. Check the validity of knowledge commitments for A, B, C - // e(π_A, vk_A) = e(π′A, P2), e(vk_B, π_B) - // = e(π′_B, P2), e(vk_C, π_C) - // = e(π′_C, P2), - if (!Pairing.pairingProd2( - proof.A, vk.A, - Pairing.negate(proof.A_p), Pairing.P2()) - ) { - return 1; - } - if (!Pairing.pairingProd2( - vk.B, proof.B, - Pairing.negate(proof.B_p), Pairing.P2()) - ) { - return 2; - } - if (!Pairing.pairingProd2( - proof.C, vk.C, - Pairing.negate(proof.C_p), Pairing.P2()) - ) { - return 3; - } - - // 3. Check same coefficients were used - // e(π_K, vk_γ) = e(vk_x + π_A + π_C, vk_{γβ2}) · e(vk_{γβ1}, π_B) - - bool pairing_check = Pairing.pairingProd3( - proof.K, - vk.gamma, - Pairing.negate(Pairing.add(vk_x, Pairing.add(proof.A, proof.C))), - vk.gammaBeta2, - Pairing.negate(vk.gammaBeta1), - proof.B); - if (!pairing_check) { - return 4; - } - - // 4. Check QAP divisibility - // e(vk_x + π_A, π_B) = e(π_H, vk_Z) · e(π_C, P2) - pairing_check = Pairing.pairingProd3( - Pairing.add(vk_x, proof.A), - proof.B, - Pairing.negate(proof.H), - vk.Z, - Pairing.negate(proof.C), - Pairing.P2()); - if (!pairing_check) { - return 5; - } - - return 0; - } - - function verify_zk_proof( - uint256[] memory proof_data, - uint256[num_inputs] memory inputs - ) - internal - returns (bool) - { - // Scalar field characteristic - // solhint-disable-next-line - uint256 r = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - - // Slightly redundant - Proof memory proof; - proof.A = Pairing.G1Point(proof_data[0], proof_data[1]); - proof.A_p = Pairing.G1Point(proof_data[2], proof_data[3]); - proof.B = Pairing.G2Point( - proof_data[4], proof_data[5], proof_data[6], proof_data[7]); - proof.B_p = Pairing.G1Point(proof_data[8], proof_data[9]); - proof.C = Pairing.G1Point(proof_data[10], proof_data[11]); - proof.C_p = Pairing.G1Point(proof_data[12], proof_data[13]); - proof.H = Pairing.G1Point(proof_data[14], proof_data[15]); - proof.K = Pairing.G1Point(proof_data[16], proof_data[17]); - - for(uint256 i = 0; i < inputs.length; i++){ - // Make sure that all primary inputs lie in the scalar field - require( - inputs[i] < r, - "Input is not is scalar field" - ); - } - - uint256 verification_result = verify(inputs, proof); - if (verification_result != 0) { - return false; - } - - return true; - } -} diff --git a/zeth_contracts/contracts/BLS12_377_test.sol b/zeth_contracts/contracts/TestBLS12_377.sol similarity index 51% rename from zeth_contracts/contracts/BLS12_377_test.sol rename to zeth_contracts/contracts/TestBLS12_377.sol index dce118d47..9326d4fb9 100644 --- a/zeth_contracts/contracts/BLS12_377_test.sol +++ b/zeth_contracts/contracts/TestBLS12_377.sol @@ -1,30 +1,30 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ -pragma solidity ^0.5.0; +pragma solidity ^0.8.0; -contract BLS12_377_test +contract TestBLS12_377 { // In many cases, these numbers must be used as literals in the assembly // code. - uint256 private constant SCALAR_WORDS = 1; - uint256 private constant G1_COORD_WORDS = 2; - uint256 private constant G1_POINT_WORDS = 2 * G1_COORD_WORDS; - uint256 private constant G2_COORD_WORDS = 4; - uint256 private constant G2_POINT_WORDS = 2 * G2_COORD_WORDS; + uint256 private constant _SCALAR_WORDS = 1; + uint256 private constant _G1_COORD_WORDS = 2; + uint256 private constant _G1_POINT_WORDS = 2 * _G1_COORD_WORDS; + uint256 private constant _G2_COORD_WORDS = 4; + uint256 private constant _G2_POINT_WORDS = 2 * _G2_COORD_WORDS; /// `input` is the concatenation of 2 encoded points in G1 - function testECAdd(bytes32[2 * G1_POINT_WORDS] memory input) - public - returns(bytes32[G1_POINT_WORDS] memory) + function testECAdd(bytes32[2 * _G1_POINT_WORDS] memory input) + external + returns(bytes32[_G1_POINT_WORDS] memory) { - bytes32[G1_POINT_WORDS] memory output; + bytes32[_G1_POINT_WORDS] memory output; bool success = true; assembly { - success := call(gas, 0xc4, 0, input, 0x100, output, 0x80) + success := call(gas(), 0xc4, 0, input, 0x100, output, 0x80) } require(success, "precompiled contract call failed (ECAdd)"); @@ -32,15 +32,15 @@ contract BLS12_377_test } /// `input` is an encoded point in G1, followed by an encoded scalar. - function testECMul(bytes32[G1_POINT_WORDS + SCALAR_WORDS] memory input) - public - returns(bytes32[G1_POINT_WORDS] memory) + function testECMul(bytes32[_G1_POINT_WORDS + _SCALAR_WORDS] memory input) + external + returns(bytes32[_G1_POINT_WORDS] memory) { - bytes32[G1_POINT_WORDS] memory output; + bytes32[_G1_POINT_WORDS] memory output; bool success = true; assembly { - success := call(gas, 0xc5, 0, input, 0xa0, output, 0x80) + success := call(gas(), 0xc5, 0, input, 0xa0, output, 0x80) } require(success, "precompiled contract call failed (ECMul)"); @@ -51,16 +51,16 @@ contract BLS12_377_test /// a G1 point, followed by a G2 point. For BW6-761, both of these points /// are 6 words, so there should be 4 * 2 * 6 = 48 words. function testECPairing( - bytes32[4 * (G1_POINT_WORDS + G2_POINT_WORDS)] memory input + bytes32[4 * (_G1_POINT_WORDS + _G2_POINT_WORDS)] memory input ) - public + external returns(uint256) { uint256[1] memory output; bool success = true; assembly { - success := call(gas, 0xc6, 0, input, 0x600, output, 0x20) + success := call(gas(), 0xc6, 0, input, 0x600, output, 0x20) } require(success, "precompiled contract call failed (ECMul)"); diff --git a/zeth_contracts/contracts/TestBW6_761.sol b/zeth_contracts/contracts/TestBW6_761.sol new file mode 100644 index 000000000..6638b1497 --- /dev/null +++ b/zeth_contracts/contracts/TestBW6_761.sol @@ -0,0 +1,62 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +pragma solidity ^0.8.0; + +contract TestBW6_761 +{ + // In many cases, these numbers must be used as literals in the assembly + // code. + + uint256 private constant _SCALAR_WORDS = 2; + uint256 private constant _COORD_WORDS = 3; + uint256 private constant _POINT_WORDS = 2 * _COORD_WORDS; + + /// `input` is the concatenation of 2 encoded points in G1 + function testECAdd(bytes32[2 * _POINT_WORDS] memory input) + external returns (bytes32[_POINT_WORDS] memory) + { + bytes32[_POINT_WORDS] memory output; + bool success = true; + assembly + { + success := call(gas(), 0xc1, 0, input, 0x180, output, 0xc0) + } + + require(success, "precompiled contract call failed (ECAdd)"); + return output; + } + + // `input` is an encoded point, followed by an encoded scalar. + function testECMul(bytes32[_POINT_WORDS + _SCALAR_WORDS] memory input) + external returns (bytes32[_POINT_WORDS] memory) + { + bytes32[_POINT_WORDS] memory output; + bool success = true; + assembly + { + success := call(gas(), 0xc2, 0, input, 0x100, output, 0xc0) + } + + require(success, "precompiled contract call failed (ECMul)"); + return output; + } + + // `input` is the concatenation of 4 pairs of encoded points. Each pair is + // a G1 point, followed by a G2 point. For BW6-761, both of these points + // are 6 words, so there should be 4 * 2 * 6 = 48 words ( + function testECPairing(bytes32[8 * _POINT_WORDS] memory input) + external returns (uint256) + { + uint256[1] memory output; + bool success = true; + assembly + { + success := call(gas(), 0xc3, 0, input, 0x600, output, 0x20) + } + + require(success, "precompiled contract call failed (ECMul)"); + return output[0]; + } +} diff --git a/zeth_contracts/contracts/TestBaseMixerAltBN128.sol b/zeth_contracts/contracts/TestBaseMixerAltBN128.sol new file mode 100644 index 000000000..84634227d --- /dev/null +++ b/zeth_contracts/contracts/TestBaseMixerAltBN128.sol @@ -0,0 +1,78 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +pragma solidity ^0.8.0; + +import "./AbstractMixerAltBN128.sol"; + + +// Implementation of AbstractMixerAltBN128 contract, to allow testing +// specific methods. +contract TestAbstractMixerAltBN128 is AbstractMixerAltBN128 +{ + constructor( + uint256 mkDepth, + address permittedDispatcher, + uint256[2] memory vkHash + ) + AbstractMixerAltBN128( + mkDepth, + address(0), + new uint256[](0), + permittedDispatcher, + vkHash) + { + } + + function testHashPublicProofData( + uint256[_NUM_INPUTS] memory publicData + ) + external + returns (uint256) + { + return _hashPublicProofData(publicData); + } + + function testAssemblePublicValues(uint256 residualBits) + external + pure + returns (uint256 vpub_in, uint256 vpub_out) + { + return _assemblePublicValues(residualBits); + } + + function testAssembleHsig( + uint256[_NUM_INPUTS] memory primaryInputs + ) + external + pure + returns(bytes32 hsig) + { + return _assembleHsig(primaryInputs); + } + + function testAssembleNullifier( + uint256 index, + uint256[_NUM_INPUTS] memory primaryInputs + ) + external + pure + returns(bytes32 nf) + { + return _assembleNullifier(index, primaryInputs); + } + + // Dummy implementation of abstract function + function _verifyZkProof( + uint256[] memory /* proof */, + uint256 /* publicInputsHash */ + ) + internal + pure + override + returns (bool) + { + return false; + } +} diff --git a/zeth_contracts/contracts/TestGroth16BLS12_377.sol b/zeth_contracts/contracts/TestGroth16BLS12_377.sol new file mode 100644 index 000000000..adc0f2097 --- /dev/null +++ b/zeth_contracts/contracts/TestGroth16BLS12_377.sol @@ -0,0 +1,24 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +pragma solidity ^0.8.0; + +import "./LibGroth16BLS12_377.sol"; + +contract TestGroth16BLS12_377 +{ + uint256[] private _vk; + + function testVerify( + uint256[] memory vk, + uint256[] memory proof, + uint256[] memory inputs + ) + external + returns(bool) + { + _vk = vk; + return LibGroth16BLS12_377._verify(_vk, proof, inputs); + } +} diff --git a/zeth_contracts/contracts/TestMerkleTreeMiMCAltBN128.sol b/zeth_contracts/contracts/TestMerkleTreeMiMCAltBN128.sol new file mode 100644 index 000000000..0114bf36e --- /dev/null +++ b/zeth_contracts/contracts/TestMerkleTreeMiMCAltBN128.sol @@ -0,0 +1,48 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +pragma solidity ^0.8.0; + +import "./AbstractMerkleTree.sol"; +import "./LibMiMC.sol"; + +/// Implementation of AbstractMerkleTree for testing. +contract TestMerkleTreeMiMCAltBN128 is AbstractMerkleTree +{ + constructor(uint treeDepth) AbstractMerkleTree(treeDepth) + { + } + + /// Add some leaves, computing the root, then adding more leaves and + /// recomputing the root. Returns the full set of nodes at the end. This + /// allows testing of the update code paths for any starting / finishing + /// state combination. + function testAddLeaves( + bytes32[] memory first, + bytes32[] memory second + ) + external + returns (bytes32) + { + for (uint i = 0 ; i < first.length ; ++i) { + insert(first[i]); + } + bytes32 root = _recomputeRoot(first.length); + + for (uint i = 0 ; i < second.length ; ++i) { + insert(second[i]); + } + root = _recomputeRoot(second.length); + return root; + } + + function _hash(bytes32 left, bytes32 right) + internal + pure + override + returns(bytes32) + { + return LibMiMC._hashAltBN128(left, right); + } +} diff --git a/zeth_contracts/contracts/TestMiMC.sol b/zeth_contracts/contracts/TestMiMC.sol new file mode 100644 index 000000000..e02226f20 --- /dev/null +++ b/zeth_contracts/contracts/TestMiMC.sol @@ -0,0 +1,27 @@ +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd +// +// SPDX-License-Identifier: LGPL-3.0+ + +pragma solidity ^0.8.0; + +import "./LibMiMC.sol"; + +/// Contract to test the MiMC libraries +contract TestMiMC +{ + /// Test function for LibMiMC._hashAltBN128 + function testMimcAltBN128(bytes32 x, bytes32 y) + external + pure + returns (bytes32) { + return LibMiMC._hashAltBN128(x, y); + } + + /// Test function for LibMiMC._hashBLS12_377 + function testMimcBLS12_377(bytes32 x, bytes32 y) + external + pure + returns (bytes32) { + return LibMiMC._hashBLS12_377(x, y); + } +} diff --git a/zeth_contracts/contracts/Tokens.sol b/zeth_contracts/contracts/Tokens.sol index 8e5aec79a..eb8267c82 100644 --- a/zeth_contracts/contracts/Tokens.sol +++ b/zeth_contracts/contracts/Tokens.sol @@ -1,21 +1,33 @@ -// Copyright (c) 2015-2020 Clearmatics Technologies Ltd +// Copyright (c) 2015-2021 Clearmatics Technologies Ltd // // SPDX-License-Identifier: LGPL-3.0+ -pragma solidity ^0.5.0; -pragma experimental ABIEncoderV2; +pragma solidity ^0.8.0; -/// Declare the ERC20 interface in order to handle ERC20 tokens transfers to +/// Declare the IERC20 interface in order to handle ERC20 tokens transfers to /// and from the Mixer. Note that we only declare the functions we are /// interested in, namely, transferFrom() (used to do a Deposit), and /// transfer() (used to do a withdrawal) -contract ERC20 { - function transferFrom(address from, address to, uint256 value) public; - function transfer(address to, uint256 value) public; +interface IERC20 +{ + function transferFrom(address from, address to, uint256 value) + external + returns (bool); + + function transfer(address to, uint256 value) + external + returns (bool); + + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval( + address indexed owner, + address indexed spender, + uint256 value); } /// ERC223 token compatible contract -contract ERC223ReceivingContract { +contract ERC223ReceivingContract +{ // See: // solhint-disable-next-line max-line-length // https://github.com/Dexaran/ERC223-token-standard/blob/Recommended/Receiver_Interface.sol diff --git a/zeth_contracts/migrations/2_deploy_contracts.js b/zeth_contracts/migrations/2_deploy_contracts.js deleted file mode 100644 index 845eb8637..000000000 --- a/zeth_contracts/migrations/2_deploy_contracts.js +++ /dev/null @@ -1,47 +0,0 @@ -// Get verification key -var path = require('path'); -var setup_path = process.env.ZETH_SETUP_DIR; -var vk_json = path.join(setup_path, 'vk.json'); -var vk = require(vk_json); - -var tmp = [] -for (var i = 0; i < vk.IC.length; i++) { - tmp = [].concat(tmp, vk.IC[i]) -} -vk.IC = tmp; - -const MerkleTreeSha256 = artifacts.require("./MerkleTreeSha256.sol"); -const Mixer = artifacts.require("./Mixer.sol"); -const Verifier = artifacts.require("./Verifier.sol"); -const Bytes = artifacts.require("./Bytes.sol"); -const Bytes_tests = artifacts.require("./Bytes_tests.sol"); - -module.exports = (deployer) => { - console.log("[WARNING] Make sure you have the right verification key stored in the `ZETH_SETUP_DIR`!") - deployer.deploy( - Verifier, - vk.a[0], - vk.a[1], - vk.b, - vk.c[0], - vk.c[1], - vk.g[0], - vk.g[1], - vk.gb1, - vk.gb2[0], - vk.gb2[1], - vk.z[0], - vk.z[1], - vk.IC - ).then(function () { - // We deploy a mixer with a merkle tree of depth 4 - return deployer.deploy(Mixer, Verifier.address, 4); - }) - - deployer.deploy(Bytes); - deployer.link(Bytes, Bytes_tests); - deployer.deploy(Bytes_tests); - - // Deploy a merkle tree of depth 3 for the tests - deployer.deploy(MerkleTreeSha256, 3); -}; diff --git a/zeth_contracts/package.json b/zeth_contracts/package.json index 447f7d629..939d00d73 100644 --- a/zeth_contracts/package.json +++ b/zeth_contracts/package.json @@ -17,7 +17,6 @@ "dependencies": { "add": "^2.0.6", "ethereumjs-abi": "^0.6.8", - "openzeppelin-solidity": "^2.4.0", "shell-escape": "^0.2.0", "solhint": "^3.3.2", "strip-hex-prefix": "^1.0.0", diff --git a/zeth_contracts/test/bytes.js b/zeth_contracts/test/bytes.js deleted file mode 100644 index b95aa825d..000000000 --- a/zeth_contracts/test/bytes.js +++ /dev/null @@ -1,32 +0,0 @@ -const Bytes_tests = artifacts.require("./Bytes_tests.sol"); - -const testContracts = { - Bytes_tests: Bytes_tests -}; - -const allSimpleTests = { - Bytes_tests: [ - "testReverseByte", - "testGetLastByte", - "testFlipEndiannessBytes32", - "testBytesToBytes32", - "testSha256DigestFromFieldElements" - ] -}; - -Object.keys(allSimpleTests).forEach(function(k) { - var obj = testContracts[k]; - contract(k, (accounts) => { - allSimpleTests[k].forEach(function (name) { - it(name, (done) => { - obj.deployed().then((instance) => { - const txObj = {from: accounts[0]}; - instance[name].call(txObj).then(result => { - assert.ok(result, k + "." + name + " [Test Fail] Boolean value expected true"); - done(); - }); - }); - }); - }); - }); -}); diff --git a/zeth_contracts/test/merkleTree.js b/zeth_contracts/test/merkleTree.js deleted file mode 100644 index bcc41399f..000000000 --- a/zeth_contracts/test/merkleTree.js +++ /dev/null @@ -1,150 +0,0 @@ -const crypto = require('crypto'); - -const MerkleTreeSha256 = artifacts.require("./MerkleTreeSha256.sol"); - -function prefixHexadecimalString(hex_str) { - return "0x" + hex_str; -} - -contract('MerkleTreeSha256', (accounts) => { - it('Test default value of merkle tree', async () => { - // We have a merkle tree of depth 3 for the tests - let instance = await MerkleTreeSha256.deployed(); - - // --- Expected values --- // - // All the nodes at index [7, 8, 9, 10, 11, 12, 13, 14] have the zero value - let expectedValueNodeLayer3 = Buffer.from(new Uint8Array(32)).toString('hex'); - // All the nodes at index [3,4,5,6] have the same value (= sha256(initialValueLeftNodeLayer3 + initialValueRightNodeLayer3)) - let expectedValueNodeLayer2 = crypto.createHash('sha256'). - update(Buffer.from(expectedValueNodeLayer3 + expectedValueNodeLayer3, 'hex')). - digest('hex'); - // All the nodes at index [1,2] have the same value (= sha256(initialValueLeftNodeLayer2 + initialValueRightNodeLayer2)) - let expectedValueNodeLayer1 = crypto.createHash('sha256'). - update(Buffer.from(expectedValueNodeLayer2 + expectedValueNodeLayer2, 'hex')). - digest('hex'); - // The value of the root node at index [0] (= sha256(initialValueLeftNodeLayer1 + initialValueRightNodeLayer1)) - let expectedValueNodeLayer0 = crypto.createHash('sha256'). - update(Buffer.from(expectedValueNodeLayer1 + expectedValueNodeLayer1, 'hex')). - digest('hex'); - - // --- Actual values from the smart contract --- // - let actualInitialTree = await instance.getTree(); - - // --- Asserts --- // - for(var i = 7; i < actualInitialTree.length; i++) { - console.log("(Layer3-Leaves) Node" + i + " => " + actualInitialTree[i]); - assert.equal( - prefixHexadecimalString(expectedValueNodeLayer3), - actualInitialTree[i], - "Mismatch between actual and expected value for node (" + i + ") of Layer3" - ); - } - for(var i = 3; i < 7; i++) { - console.log("(Layer2) Node" + i + " => " + actualInitialTree[i]); - assert.equal( - prefixHexadecimalString(expectedValueNodeLayer2), - actualInitialTree[i], - "Mismatch between actual and expected value for node (" + i + ") of Layer2" - ); - } - for(var i = 1; i < 3; i++) { - console.log("(Layer1) Node" + i + " => " + actualInitialTree[i]); - assert.equal( - prefixHexadecimalString(expectedValueNodeLayer1), - actualInitialTree[i], - "Mismatch between actual and expected value for node (" + i + ") of Layer1" - ); - } - console.log("(Layer0-Root) Node 0 => " + actualInitialTree[0]); - assert.equal( - prefixHexadecimalString(expectedValueNodeLayer0), - actualInitialTree[0], - "Mismatch between actual and expected value for node (0) of Layer0-Root" - ); - }); - - it('Test insertion in the merkle tree', async () => { - // We have a merkle tree of depth 3 for the tests - let instance = await MerkleTreeSha256.deployed(); - - // --- Leaves layer (layer 3) --- // - // We insert at the first available leaf (leftmost leaf --> index 7 in the tree of depth 3) - var expectedValueNode7 = crypto.createHash('sha256').update("test-commitment").digest('hex') - // All the nodes at index [8, 9, 10, 11, 12, 13, 14] have the zero value - let expectedValueOtherNodesLayer3 = Buffer.from(new Uint8Array(32)).toString('hex'); - - // --- Layer 2 --- // - // The value of node3 - let expectedValueNode3 = crypto.createHash('sha256'). - update(Buffer.from(expectedValueNode7 + expectedValueOtherNodesLayer3, 'hex')). - digest('hex'); - // All the nodes at index [4,5,6] have the same value (= sha256(initialValueLeftNodeLayer3 || initialValueRightNodeLayer3)) - let expectedValueOtherNodesLayer2 = crypto.createHash('sha256'). - update(Buffer.from(expectedValueOtherNodesLayer3 + expectedValueOtherNodesLayer3, 'hex')). - digest('hex'); - - // --- Layer 1 --- // - // The value of node 1 - let expectedValueNode1 = crypto.createHash('sha256'). - update(Buffer.from(expectedValueNode3 + expectedValueOtherNodesLayer2, 'hex')). - digest('hex'); - // The value of node 2 - let expectedValueNode2 = crypto.createHash('sha256'). - update(Buffer.from(expectedValueOtherNodesLayer2 + expectedValueOtherNodesLayer2, 'hex')). - digest('hex'); - - // --- Layer 0 - Root --- // - // The value of the root node at index [0] (= sha256(value node 1(Left) || value node 2(Right))) - let expectedValueNodeLayer0 = crypto.createHash('sha256'). - update(Buffer.from(expectedValueNode1 + expectedValueNode2, 'hex')). - digest('hex'); - - // --- Insertion of the commitment at the left most free leaf (Leaf 7 here) --- // - let addressInsertion = await instance.insert("0x" + expectedValueNode7); - - // --- Assert to verify that the new merkle tree has the expected values --- // - let actualInitialTree = await instance.getTree(); - console.log("(Layer3-Location of insertion) Node7 => " + actualInitialTree[7]); - assert.equal( - prefixHexadecimalString(expectedValueNode7), actualInitialTree[7], - "Mismatch between actual and expected value for node (7) of Layer3" - ); - for(var i = 8; i < actualInitialTree.length; i++) { - console.log("(Layer3-Leaves) Node" + i + " => " + actualInitialTree[i]); - assert.equal( - prefixHexadecimalString(expectedValueOtherNodesLayer3), actualInitialTree[i], - "Mismatch between actual and expected value for node (" + i + ") of Layer3" - ); - } - - console.log("(Layer2-Parent of inserted commitment) Node3 => " + actualInitialTree[3]); - assert.equal( - prefixHexadecimalString(expectedValueNode3), actualInitialTree[3], - "Mismatch between actual and expected value for node (3) of Layer2" - ); - for(var i = 4; i < 7; i++) { - console.log("(Layer2) Node" + i + " => " + actualInitialTree[i]); - assert.equal( - prefixHexadecimalString(expectedValueOtherNodesLayer2), actualInitialTree[i], - "Mismatch between actual and expected value for node (" + i + ") of Layer2" - ); - } - - console.log("(Layer1-Grand Parent of inserted commitment) Node1 => " + actualInitialTree[1]); - assert.equal( - prefixHexadecimalString(expectedValueNode1), actualInitialTree[1], - "Mismatch between actual and expected value for node (1) of Layer1" - ); - console.log("(Layer1) Node 2 => " + actualInitialTree[2]); - assert.equal( - prefixHexadecimalString(expectedValueNode2), actualInitialTree[2], - "Mismatch between actual and expected value for node (2) of Layer1" - ); - - console.log("(Layer0-Root) Node 0 => " + actualInitialTree[0]); - assert.equal( - prefixHexadecimalString(expectedValueNodeLayer0), actualInitialTree[0], - "Mismatch between actual and expected value for node (0) of Layer0-Root" - ); - }); -}); diff --git a/zeth_contracts/truffle.js b/zeth_contracts/truffle.js index 4e8b68954..4ac6becab 100644 --- a/zeth_contracts/truffle.js +++ b/zeth_contracts/truffle.js @@ -24,10 +24,15 @@ module.exports = { useColors: true, enableTimeouts: false }, - solc: { - optimizer: { - enabled: true, - runs: 200 + compilers: { + solc: { + version: "^0.8.0", + settings: { + optimizer: { + enabled: true, + runs: 200 + } + } } } };