├── .flake8 ├── .github ├── dependabot.yml └── workflows │ ├── build-c-libraries.yml │ ├── build-riscv64.yml │ ├── build.yml │ ├── check-commit-signing.yml │ ├── codeql-analysis.yml │ ├── dependency-review.yml │ ├── hw-build.yml │ ├── rust.yml │ ├── stale-issue.yml │ └── test.yaml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── README_ASIC.md ├── README_AVX512.md ├── SECURITY.md ├── assets └── deb │ └── control.j2 ├── classgroups.pdf ├── comparenweso.py ├── lgtm.yml ├── mypi.ini ├── pyproject.toml ├── rust_bindings ├── Cargo.toml ├── build.rs ├── fuzz │ ├── .gitignore │ ├── Cargo.toml │ └── fuzz_targets │ │ ├── create_discriminant.rs │ │ ├── prove.rs │ │ ├── verify.rs │ │ └── verify_n_wesolowski.rs ├── src │ └── lib.rs └── wrapper.h ├── setup.py ├── src ├── 1weso_test.cpp ├── 2weso_test.cpp ├── CMakeLists.txt ├── ClassGroup.h ├── Makefile.vdf-client ├── Reducer.h ├── alloc.hpp ├── asm_avx512_ifma.h ├── asm_base.h ├── asm_gcd_128.h ├── asm_gcd_base_continued_fractions.h ├── asm_gcd_base_divide_table.h ├── asm_gcd_unsigned.h ├── asm_main.h ├── asm_types.h ├── asm_vm.h ├── avx512_integer.h ├── avx512_test.cpp ├── bit_manipulation.h ├── bqfc.c ├── bqfc.h ├── c_bindings │ ├── c_wrapper.cpp │ ├── c_wrapper.h │ └── readme.md ├── callback.h ├── cmake │ ├── FindGMP.cmake │ └── FindGMPXX.cmake ├── compile_asm.cpp ├── create_discriminant.h ├── double_utility.h ├── fast_storage.h ├── gcd_128.h ├── gcd_base_continued_fractions.h ├── gcd_base_divide_table.h ├── gcd_unsigned.h ├── generic.h ├── generic_macros.h ├── gpu_integer.h ├── gpu_integer_divide.h ├── gpu_integer_gcd.h ├── hw │ ├── chia_driver.cpp │ ├── chia_driver.hpp │ ├── chia_registers.hpp │ ├── clock.hpp │ ├── emu_funcs.cpp │ ├── emu_runner.cpp │ ├── emu_runner.hpp │ ├── ftdi_driver.cpp │ ├── ftdi_driver.hpp │ ├── hw_interface.cpp │ ├── hw_interface.hpp │ ├── hw_proof.cpp │ ├── hw_proof.hpp │ ├── hw_test.cpp │ ├── hw_util.cpp │ ├── hw_util.hpp │ ├── hw_vdf_client.cpp │ ├── pll_freqs.cpp │ ├── pll_freqs.hpp │ ├── pvt.hpp │ ├── real_hw.cpp │ ├── vdf_driver.cpp │ └── vdf_driver.hpp ├── include.h ├── integer.h ├── integer_common.h ├── lib │ └── gmp-patch-6.2.1 │ │ ├── compat.c │ │ ├── longlong.h │ │ └── mpz │ │ └── inp_raw.c ├── nucomp.h ├── parameters.h ├── picosha2.h ├── pprods.h ├── primetest.h ├── proof_common.h ├── prover_base.hpp ├── prover_parallel.hpp ├── prover_slow.h ├── prover_test.cpp ├── provers.h ├── python_bindings │ └── fastvdf.cpp ├── refcode │ ├── README.md │ └── lzcnt.c ├── stress_test.cpp ├── threading.h ├── tl_emu.py ├── uint128_t │ ├── LICENSE │ ├── README.md │ ├── uint128_t.build │ ├── uint128_t.cpp │ ├── uint128_t.h │ ├── uint128_t.include │ └── uint128_t_config.include ├── util.h ├── vdf.h ├── vdf.txt ├── vdf_base.cpp ├── vdf_base.hpp ├── vdf_bench.cpp ├── vdf_client.cpp ├── vdf_fast.h ├── vdf_new.h ├── vdf_original.h ├── vdf_test.cpp ├── vdf_test.h ├── verifier.h ├── verifier_test.cpp └── xgcd_partial.c ├── tests ├── test_n_weso_verifier.py └── test_verifier.py └── tools └── gen_pprods.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | exclude = ./typings/**/*, ./src/lib/pybind11 4 | ignore = E203,W503 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # This file is managed by the repo-content-updater project. Manual changes here will result in a PR to bring back 2 | # inline with the upstream template, unless you remove the dependabot managed file property from the repo 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: "gomod" 7 | directory: / 8 | schedule: 9 | interval: "weekly" 10 | day: "tuesday" 11 | open-pull-requests-limit: 10 12 | rebase-strategy: auto 13 | labels: 14 | - dependencies 15 | - go 16 | - "Changed" 17 | reviewers: ["cmmarslender", "Starttoaster"] 18 | groups: 19 | global: 20 | patterns: 21 | - "*" 22 | 23 | - package-ecosystem: "pip" 24 | directory: / 25 | schedule: 26 | interval: "weekly" 27 | day: "tuesday" 28 | open-pull-requests-limit: 10 29 | rebase-strategy: auto 30 | labels: 31 | - dependencies 32 | - python 33 | - "Changed" 34 | reviewers: ["emlowe", "altendky"] 35 | 36 | - package-ecosystem: "github-actions" 37 | directories: ["/", ".github/actions/*"] 38 | schedule: 39 | interval: "weekly" 40 | day: "tuesday" 41 | open-pull-requests-limit: 10 42 | rebase-strategy: auto 43 | labels: 44 | - dependencies 45 | - github_actions 46 | - "Changed" 47 | reviewers: ["cmmarslender", "Starttoaster", "pmaslana"] 48 | 49 | - package-ecosystem: "npm" 50 | directory: / 51 | schedule: 52 | interval: "weekly" 53 | day: "tuesday" 54 | open-pull-requests-limit: 10 55 | rebase-strategy: auto 56 | labels: 57 | - dependencies 58 | - javascript 59 | - "Changed" 60 | reviewers: ["cmmarslender", "ChiaMineJP"] 61 | 62 | - package-ecosystem: cargo 63 | directory: / 64 | schedule: 65 | interval: "weekly" 66 | day: "tuesday" 67 | open-pull-requests-limit: 10 68 | rebase-strategy: auto 69 | labels: 70 | - dependencies 71 | - rust 72 | - "Changed" 73 | 74 | - package-ecosystem: swift 75 | directory: / 76 | schedule: 77 | interval: "weekly" 78 | day: "tuesday" 79 | open-pull-requests-limit: 10 80 | rebase-strategy: auto 81 | -------------------------------------------------------------------------------- /.github/workflows/build-c-libraries.yml: -------------------------------------------------------------------------------- 1 | name: Build C Libraries 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | release: 8 | types: [published] 9 | pull_request: 10 | branches: 11 | - '**' 12 | 13 | concurrency: 14 | # SHA is added to the end if on `main` to let all main workflows run 15 | group: ${{ github.ref }}-${{ github.workflow }}-${{ github.event_name }}-${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') || startsWith(github.ref, 'refs/heads/long_lived/')) && github.sha || '' }} 16 | cancel-in-progress: true 17 | 18 | permissions: 19 | contents: write 20 | id-token: write 21 | 22 | jobs: 23 | build-c-libraries: 24 | name: C Libraries - ${{ matrix.os.name }} ${{ matrix.arch.name }} 25 | runs-on: ${{ matrix.os.runs-on[matrix.arch.matrix] }} 26 | strategy: 27 | fail-fast: false 28 | matrix: 29 | os: 30 | - name: macOS 31 | matrix: macos 32 | runs-on: 33 | arm: [macOS, ARM64] 34 | intel: [macos-13] 35 | - name: Ubuntu 36 | matrix: ubuntu 37 | runs-on: 38 | arm: [Linux, ARM64] 39 | intel: [ubuntu-latest] 40 | - name: Windows 41 | matrix: windows 42 | runs-on: 43 | intel: [windows-latest] 44 | arch: 45 | - name: ARM 46 | matrix: arm 47 | - name: Intel 48 | matrix: intel 49 | exclude: 50 | # Only partial entries are required here by GitHub Actions so generally I 51 | # only specify the `matrix:` entry. The super linter complains so for now 52 | # all entries are included to avoid that. Reported at 53 | # https://github.com/github/super-linter/issues/3016 54 | - os: 55 | name: Windows 56 | matrix: windows 57 | runs-on: 58 | intel: [windows-latest] 59 | arch: 60 | name: ARM 61 | matrix: arm 62 | 63 | steps: 64 | - name: Clean workspace 65 | uses: Chia-Network/actions/clean-workspace@main 66 | 67 | - name: Checkout code 68 | uses: actions/checkout@v4 69 | with: 70 | fetch-depth: 0 71 | 72 | - name: Set Env 73 | uses: Chia-Network/actions/setjobenv@main 74 | env: 75 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 76 | 77 | - name: Checkout mpir for windows 78 | if: matrix.os.matrix == 'windows' 79 | uses: actions/checkout@v4 80 | with: 81 | repository: Chia-Network/mpir_gc_x64 82 | fetch-depth: 1 83 | path: mpir_gc_x64 84 | 85 | - name: Build 86 | working-directory: src 87 | env: 88 | BUILD_VDF_CLIENT: "N" 89 | run: | 90 | cmake . -DBUILD_CHIAVDFC=ON -DBUILD_PYTHON=OFF 91 | cmake --build . 92 | 93 | - name: Upload artifacts 94 | uses: actions/upload-artifact@v4 95 | with: 96 | name: c-libraries-${{ matrix.os.matrix }}-${{ matrix.arch.matrix }} 97 | path: ./src/lib 98 | 99 | - name: Assemble directory of headers and libraries for distribution 100 | shell: bash 101 | run: | 102 | DIST_DIR="chiavdfc-${{ matrix.os.matrix }}-${{ matrix.arch.matrix }}" 103 | echo "DIST_DIR=$DIST_DIR" >> "$GITHUB_ENV" 104 | mkdir -p ${DIST_DIR}/static ${DIST_DIR}/shared 105 | 106 | cp src/c_bindings/c_wrapper.h ${DIST_DIR}/ 107 | find src/lib/static -type f -exec cp {} ${DIST_DIR}/static/ \; 108 | find src/lib/shared -type f -exec cp {} ${DIST_DIR}/shared/ \; 109 | 110 | - name: Zip (linux/mac) 111 | if: runner.os != 'Windows' 112 | run: | 113 | zip -r chiavdfc-${{ matrix.os.matrix }}-${{ matrix.arch.matrix }}.zip ${{ env.DIST_DIR }} 114 | 115 | - name: Zip (windows) 116 | if: runner.os == 'Windows' 117 | run: | 118 | Compress-Archive -Path ${{ env.DIST_DIR }}/* -Destination chiavdfc-${{ matrix.os.matrix }}-${{ matrix.arch.matrix }}.zip 119 | 120 | - name: Upload zip as artifact 121 | uses: actions/upload-artifact@v4 122 | with: 123 | name: chiavdfc-${{ matrix.os.matrix }}-${{ matrix.arch.matrix }} 124 | path: | 125 | ${{ github.workspace }}/chiavdfc-${{ matrix.os.matrix }}-${{ matrix.arch.matrix }}.zip 126 | 127 | - name: Upload release artifacts 128 | if: env.RELEASE == 'true' 129 | shell: bash 130 | env: 131 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 132 | run: | 133 | gh release upload \ 134 | $RELEASE_TAG \ 135 | chiavdfc-${{ matrix.os.matrix }}-${{ matrix.arch.matrix }}.zip 136 | 137 | - uses: Chia-Network/actions/github/jwt@main 138 | if: env.RELEASE == 'true' 139 | 140 | - name: Notify new build 141 | if: env.RELEASE == 'true' 142 | uses: Chia-Network/actions/github/glue@main 143 | with: 144 | json_data: '{"release_version":"${{ env.RELEASE_TAG }}"}' 145 | glue_url: ${{ secrets.GLUE_API_URL }} 146 | glue_project: "chiavdfc" 147 | glue_path: "trigger" 148 | -------------------------------------------------------------------------------- /.github/workflows/build-riscv64.yml: -------------------------------------------------------------------------------- 1 | name: Build and test riscv64 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | release: 8 | types: [published] 9 | pull_request: 10 | branches: 11 | - '**' 12 | 13 | concurrency: 14 | # SHA is added to the end if on `main` to let all main workflows run 15 | group: ${{ github.ref }}-${{ github.workflow }}-${{ github.event_name }}-${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') || startsWith(github.ref, 'refs/heads/long_lived/')) && github.sha || '' }} 16 | cancel-in-progress: true 17 | 18 | permissions: 19 | contents: read 20 | id-token: write 21 | 22 | jobs: 23 | build_wheels: 24 | name: ${{ matrix.os.emoji }} 📦 Build ${{ matrix.python.major-dot-minor }} 25 | runs-on: ${{ matrix.os.runs-on }} 26 | strategy: 27 | fail-fast: false 28 | matrix: 29 | os: 30 | - name: Linux 31 | matrix: linux 32 | emoji: 🐧 33 | runs-on: [ubuntu-latest] 34 | python: 35 | - major-dot-minor: '3.9' 36 | matrix: '3.9' 37 | - major-dot-minor: '3.10' 38 | matrix: '3.10' 39 | - major-dot-minor: '3.11' 40 | matrix: '3.11' 41 | - major-dot-minor: '3.12' 42 | matrix: '3.12' 43 | 44 | steps: 45 | - name: Checkout repository 46 | uses: actions/checkout@v4 47 | with: 48 | fetch-depth: 1 49 | 50 | - name: Set up QEMU on x86_64 51 | id: qemu 52 | uses: docker/setup-qemu-action@v3 53 | with: 54 | platforms: riscv64 55 | 56 | - name: Build and Test 57 | run: | 58 | docker run --rm --platform linux/riscv64 \ 59 | -v ${{ github.workspace }}:/ws --workdir=/ws \ 60 | chianetwork/ubuntu-22.04-risc-builder:latest \ 61 | bash -exc '\ 62 | pyenv global ${{ matrix.python.matrix }} && \ 63 | python3 -m venv venv && \ 64 | source ./venv/bin/activate && \ 65 | pip install --upgrade pip && \ 66 | cmake --version && \ 67 | uname -a && \ 68 | export BUILD_VDF_CLIENT=N && \ 69 | pip wheel -w dist . && \ 70 | ./venv/bin/python -m pip install dist/*.whl && \ 71 | ./venv/bin/python -m pip install pytest && \ 72 | ./venv/bin/python -m pytest -v tests/ 73 | ' 74 | 75 | - name: Upload artifacts 76 | uses: actions/upload-artifact@v4 77 | with: 78 | name: packages-${{ matrix.os.name }}-${{ matrix.python.major-dot-minor }} 79 | path: ./dist 80 | if-no-files-found: error 81 | upload: 82 | name: Upload to Chia PyPI 83 | runs-on: ubuntu-latest 84 | needs: 85 | - build_wheels 86 | steps: 87 | - name: Checkout code 88 | uses: actions/checkout@v4 89 | with: 90 | fetch-depth: 0 91 | 92 | - name: Set Env 93 | uses: Chia-Network/actions/setjobenv@main 94 | env: 95 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 96 | 97 | - name: Download artifacts 98 | if: env.RELEASE == 'true' 99 | uses: actions/download-artifact@v4 100 | with: 101 | merge-multiple: true 102 | pattern: packages-* 103 | path: ./dist 104 | 105 | - name: Configure AWS credentials 106 | if: env.RELEASE == 'true' 107 | uses: aws-actions/configure-aws-credentials@v4 108 | with: 109 | role-to-assume: arn:aws:iam::${{ secrets.CHIA_AWS_ACCOUNT_ID }}:role/installer-upload 110 | aws-region: us-west-2 111 | 112 | - name: List existing wheels 113 | if: env.RELEASE == 'true' 114 | shell: sh 115 | run: | 116 | aws s3 ls s3://download.chia.net/simple/chiavdf/ > existing_wheel_list_raw 117 | cat existing_wheel_list_raw 118 | cat existing_wheel_list_raw | tr -s ' ' | cut -d ' ' -f 4 > existing_wheel_list 119 | 120 | - name: List new wheels 121 | if: env.RELEASE == 'true' 122 | shell: sh 123 | run: | 124 | (cd dist/; ls chiavdf-*.whl) > new_wheel_list 125 | cat new_wheel_list | xargs -I % sh -c 'ls -l dist/%' 126 | 127 | - name: Choose wheels to upload 128 | if: env.RELEASE == 'true' 129 | shell: sh 130 | run: | 131 | grep -F -x -v -f existing_wheel_list new_wheel_list > upload_wheel_list 132 | cat upload_wheel_list 133 | 134 | - name: Upload wheels 135 | if: env.RELEASE == 'true' 136 | shell: sh 137 | run: | 138 | cat upload_wheel_list | xargs -I % sh -c 'aws s3 cp dist/% s3://download.chia.net/simple/chiavdf/' 139 | -------------------------------------------------------------------------------- /.github/workflows/check-commit-signing.yml: -------------------------------------------------------------------------------- 1 | name: 🚨 Check commit signing 2 | 3 | on: 4 | push: 5 | branches: 6 | - long_lived/** 7 | - main 8 | - release/** 9 | pull_request: 10 | branches: 11 | - "**" 12 | 13 | concurrency: 14 | group: ${{ github.event_name == 'pull_request' && format('{0}-{1}', github.workflow_ref, github.event.pull_request.number) || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | check-commit-signing: 19 | name: Check commit signing 20 | runs-on: [ubuntu-latest] 21 | timeout-minutes: 5 22 | 23 | steps: 24 | - name: Checkout Code 25 | uses: actions/checkout@v4 26 | with: 27 | fetch-depth: 0 28 | 29 | - uses: chia-network/actions/check-commit-signing@main 30 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "main" ] 20 | schedule: 21 | - cron: '43 9 * * 5' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'cpp', 'python' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v4 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v3 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 52 | # queries: security-extended,security-and-quality 53 | 54 | - name: Build 55 | run: | 56 | export BUILD_VDF_CLIENT=Y 57 | export BUILD_VDF_BENCH=Y 58 | sudo apt update && sudo apt-get install cmake libboost-all-dev libgmp-dev 59 | python3 -m venv venv 60 | source venv/bin/activate 61 | pip install wheel setuptools_scm pybind11 62 | pip wheel . 63 | 64 | - name: Perform CodeQL Analysis 65 | uses: github/codeql-action/analyze@v3 66 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Managed by repo-content-updater 2 | # Dependency Review Action 3 | # 4 | # This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. 5 | # 6 | # Source repository: https://github.com/actions/dependency-review-action 7 | # Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement 8 | name: "🚨 Dependency Review" 9 | on: [pull_request] 10 | 11 | permissions: 12 | contents: read 13 | 14 | jobs: 15 | dependency-review: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: "Checkout Repository" 19 | uses: actions/checkout@v4 20 | 21 | - name: "Dependency Review" 22 | uses: actions/dependency-review-action@v4 23 | with: 24 | allow-dependencies-licenses: pkg:pypi/pyinstaller 25 | deny-licenses: AGPL-1.0-only, AGPL-1.0-or-later, AGPL-1.0-or-later, AGPL-3.0-or-later, GPL-1.0-only, GPL-1.0-or-later, GPL-2.0-only, GPL-2.0-or-later, GPL-3.0-only, GPL-3.0-or-later 26 | -------------------------------------------------------------------------------- /.github/workflows/hw-build.yml: -------------------------------------------------------------------------------- 1 | name: HW Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | release: 8 | types: [published] 9 | pull_request: 10 | branches: 11 | - '**' 12 | workflow_dispatch: 13 | 14 | concurrency: 15 | # SHA is added to the end if on `main` to let all main workflows run 16 | group: ${{ github.ref }}-${{ github.workflow }}-${{ github.event_name }}-${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') || startsWith(github.ref, 'refs/heads/long_lived/')) && github.sha || '' }} 17 | cancel-in-progress: true 18 | 19 | permissions: 20 | id-token: write 21 | contents: write 22 | 23 | jobs: 24 | build-hw: 25 | name: Build HW VDF Client 26 | runs-on: [ubuntu-22.04] 27 | steps: 28 | - name: Checkout code 29 | uses: actions/checkout@v4 30 | with: 31 | fetch-depth: 0 32 | 33 | - uses: Chia-Network/actions/setup-python@main 34 | name: Install Python 35 | with: 36 | python-version: "3.10" 37 | 38 | - name: Set Env 39 | uses: Chia-Network/actions/setjobenv@main 40 | env: 41 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | 43 | - name: Install deps 44 | run: | 45 | sudo apt-get update 46 | sudo apt-get install -y build-essential cmake libgmp-dev libboost-system-dev 47 | 48 | - name: Download USB drivers 49 | run: | 50 | mkdir libft4222 51 | cd libft4222 52 | wget https://download.chia.net/vdf/libft4222-linux-1.4.4.170.tgz 53 | tar -xvzf libft4222-linux-1.4.4.170.tgz 54 | ln -s ${{ github.workspace }}/libft4222 ${{ github.workspace }}/src/hw/libft4222 55 | ln -s ${{ github.workspace }}/libft4222/build-x86_64/libft4222.so.1.4.4.170 ${{ github.workspace }}/libft4222/build-x86_64/libft4222.so 56 | 57 | - name: Compile 58 | working-directory: "${{ github.workspace }}/src" 59 | run: make -f Makefile.vdf-client emu_hw_test hw_test emu_hw_vdf_client hw_vdf_client 60 | 61 | - name: Upload Artifacts 62 | uses: actions/upload-artifact@v4 63 | with: 64 | name: hw-vdf 65 | path: | 66 | ${{ github.workspace }}/src/emu_hw_test 67 | ${{ github.workspace }}/src/hw_test 68 | ${{ github.workspace }}/src/hw_vdf_client 69 | ${{ github.workspace }}/src/emu_hw_vdf_client 70 | ${{ github.workspace }}/src/hw/libft4222/build-x86_64/libft4222.so 71 | 72 | - name: Assemble .deb 73 | env: 74 | INSTALLER_VERSION: "${{ env.RELEASE_TAG || format('0.0.1-{0}', github.run_id) }}" 75 | PLATFORM: "amd64" 76 | run: | 77 | pip install jinjanator 78 | CLI_DEB_BASE="chiavdf-hw_$INSTALLER_VERSION-1_$PLATFORM" 79 | mkdir -p "dist/$CLI_DEB_BASE/usr/bin" 80 | mkdir -p "dist/$CLI_DEB_BASE/usr/lib" 81 | mkdir -p "dist/$CLI_DEB_BASE/DEBIAN" 82 | mkdir -p "dist/$CLI_DEB_BASE/etc/udev/rules.d" 83 | j2 -o "dist/$CLI_DEB_BASE/DEBIAN/control" assets/deb/control.j2 84 | 85 | cp ${{ github.workspace }}/src/emu_hw_test dist/$CLI_DEB_BASE/usr/bin/ 86 | cp ${{ github.workspace }}/src/hw_test dist/$CLI_DEB_BASE/usr/bin/ 87 | cp ${{ github.workspace }}/src/hw_vdf_client dist/$CLI_DEB_BASE/usr/bin/ 88 | cp ${{ github.workspace }}/src/emu_hw_vdf_client dist/$CLI_DEB_BASE/usr/bin/ 89 | cp ${{ github.workspace }}/src/hw/libft4222/build-x86_64/libft4222.so dist/$CLI_DEB_BASE/usr/lib/ 90 | echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="601c", MODE="0666"' > dist/$CLI_DEB_BASE/etc/udev/rules.d/99-chiavdf.rules 91 | dpkg-deb --build --root-owner-group "dist/$CLI_DEB_BASE" 92 | 93 | echo "DEB_NAME=$CLI_DEB_BASE.deb" >> $GITHUB_ENV 94 | 95 | - name: Upload Installer 96 | uses: actions/upload-artifact@v4 97 | with: 98 | name: installer 99 | path: | 100 | ${{ github.workspace }}/dist/${{ env.DEB_NAME }} 101 | 102 | - name: Upload release artifacts 103 | if: env.RELEASE == 'true' 104 | env: 105 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 106 | run: | 107 | gh release upload \ 108 | $RELEASE_TAG \ 109 | dist/${{ env.DEB_NAME }} 110 | 111 | - uses: Chia-Network/actions/github/jwt@main 112 | if: env.RELEASE == 'true' 113 | 114 | - name: Trigger repo update 115 | if: env.RELEASE == 'true' 116 | uses: Chia-Network/actions/github/glue@main 117 | with: 118 | json_data: '{"release_version":"${{ env.RELEASE_TAG }}"}' 119 | glue_url: ${{ secrets.GLUE_API_URL }} 120 | glue_project: "chiavdf-hw" 121 | glue_path: "trigger" 122 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust bindings 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | release: 8 | types: [published] 9 | pull_request: 10 | branches: 11 | - "**" 12 | 13 | concurrency: 14 | group: ${{ github.event_name == 'pull_request' && format('{0}-{1}', github.workflow_ref, github.event.pull_request.number) || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | permissions: 18 | id-token: write 19 | contents: read 20 | 21 | jobs: 22 | fuzz_targets: 23 | name: Run fuzzers 24 | runs-on: ubuntu-latest 25 | env: 26 | CARGO_PROFILE_RELEASE_LTO: false 27 | steps: 28 | - uses: actions/checkout@v4 29 | - uses: dtolnay/rust-toolchain@nightly 30 | 31 | - name: Install cargo-fuzz 32 | run: cargo +nightly install cargo-fuzz 33 | 34 | - name: Cargo fuzz 35 | run: | 36 | cd rust_bindings 37 | cargo fuzz list | xargs -I "%" sh -c "cargo +nightly fuzz run % -- -max_total_time=600 || exit 255" 38 | 39 | lint: 40 | name: Lint 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@v4 44 | 45 | - name: Set up Rust 46 | uses: dtolnay/rust-toolchain@stable 47 | with: 48 | components: rustfmt, clippy 49 | 50 | - name: Rustfmt 51 | run: cargo fmt -- --files-with-diff --check 52 | 53 | - name: Clippy 54 | run: cargo clippy 55 | 56 | test: 57 | name: Test (${{ matrix.os.name }} ${{ matrix.arch.name }}) 58 | runs-on: ${{ matrix.os.runs-on[matrix.arch.matrix] }} 59 | 60 | strategy: 61 | fail-fast: false 62 | matrix: 63 | os: 64 | - name: macOS 65 | matrix: macos 66 | runs-on: 67 | arm: [macos-13-arm64] 68 | intel: [macos-13] 69 | cibw-archs-macos: 70 | arm: arm64 71 | intel: x86_64 72 | - name: Ubuntu 73 | matrix: ubuntu 74 | runs-on: 75 | arm: [Linux, ARM64] 76 | intel: [ubuntu-latest] 77 | - name: Windows 78 | matrix: windows 79 | runs-on: 80 | intel: [windows-latest] 81 | 82 | arch: 83 | - name: ARM 84 | matrix: arm 85 | - name: Intel 86 | matrix: intel 87 | 88 | exclude: 89 | - os: 90 | name: Windows 91 | matrix: windows 92 | runs-on: 93 | intel: [windows-latest] 94 | arch: 95 | name: ARM 96 | matrix: arm 97 | 98 | steps: 99 | - uses: actions/checkout@v4 100 | 101 | - name: Setup library path on MacOS 102 | if: matrix.os.name == 'macOS' 103 | run: echo "LIBRARY_PATH=/opt/homebrew/lib:$LIBRARY_PATH" >> $GITHUB_ENV 104 | 105 | - name: Install MPIR on Windows 106 | if: matrix.os.name == 'Windows' 107 | run: | 108 | git clone https://github.com/Chia-Network/mpir_gc_x64.git 109 | "$(Get-Location)/mpir_gc_x64" | Out-File -Append -FilePath $env:GITHUB_PATH 110 | 111 | - name: Install libclang-dev on Linux 112 | if: matrix.os.name == 'Ubuntu' 113 | run: sudo apt-get install libclang-dev -y 114 | 115 | - name: Set up Rust 116 | uses: dtolnay/rust-toolchain@stable 117 | 118 | - name: Tests 119 | run: cargo test && cargo test --release 120 | 121 | build_crate: 122 | name: Build crate 123 | needs: [lint, test] 124 | runs-on: ubuntu-latest 125 | strategy: 126 | fail-fast: false 127 | 128 | steps: 129 | - uses: actions/checkout@v4 130 | 131 | - name: Set up Rust 132 | uses: dtolnay/rust-toolchain@stable 133 | 134 | - name: Build 135 | run: cargo build --release 136 | 137 | - name: Prepare for publish 138 | run: | 139 | cd rust_bindings 140 | cp -r ../src cpp 141 | git clone https://github.com/Chia-Network/mpir_gc_x64.git 142 | 143 | - name: Publish to crates.io (dry run) 144 | # We use `--allow-dirty` because the `cpp` folder is copied into the working directory. 145 | # This is necessary because the `cpp` folder is not part of the crate otherwise. 146 | run: cargo publish --dry-run -p chiavdf --allow-dirty 147 | 148 | - name: Upload crate artifacts 149 | uses: actions/upload-artifact@v4 150 | with: 151 | name: crate 152 | path: ./target/package/*-*.crate 153 | 154 | - name: Set Env 155 | uses: Chia-Network/actions/setjobenv@main 156 | env: 157 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 158 | 159 | - name: Publish to crates.io 160 | if: env.RELEASE == 'true' 161 | env: 162 | CARGO_REGISTRY_TOKEN: ${{ secrets.cargo_registry_token }} 163 | # See comment above for why `--allow-dirty` is used. 164 | run: | 165 | cp -r src rust_bindings/cpp 166 | cargo publish -p chiavdf --allow-dirty 167 | -------------------------------------------------------------------------------- /.github/workflows/stale-issue.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 'Close stale issues' 3 | on: 4 | schedule: 5 | - cron: '0 11 * * *' 6 | 7 | jobs: 8 | stale: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: chia-network/stale@main 12 | with: 13 | operations-per-run: 10000 14 | ascending: true 15 | days-before-issue-stale: 14 16 | days-before-issue-close: 7 17 | days-before-pr-stale: 60 18 | days-before-pr-close: -1 19 | exempt-all-pr-milestones: true 20 | exempt-all-issue-milestones: true 21 | exempt-all-assignees: true 22 | stale-issue-label: stale-issue 23 | stale-pr-label: stale-pr 24 | remove-stale-when-updated: true 25 | stale-issue-message: > 26 | 'This issue has been flagged as stale as there has been no 27 | activity on it in 14 days. If this issue is still affecting you 28 | and in need of review, please update it to keep it open.' 29 | close-issue-message: > 30 | 'This issue was automatically closed because it has been flagged 31 | as stale and subsequently passed 7 days with no further activity.' 32 | stale-pr-message: > 33 | 'This PR has been flagged as stale due to no activity for over 60 34 | days. It will not be automatically closed, but it has been given 35 | a stale-pr label and should be manually reviewed.' 36 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test Prover and vdf_client 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | release: 8 | types: [published] 9 | pull_request: 10 | branches: 11 | - '**' 12 | 13 | jobs: 14 | test: 15 | name: Test ${{ matrix.config }} ${{ matrix.os }} 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | os: [macos-13, ubuntu-latest] 21 | config: [optimized=1, TSAN=1, ASAN=1] 22 | 23 | steps: 24 | - name: Checkout code 25 | uses: actions/checkout@v4 26 | 27 | # See: https://github.com/google/sanitizers/issues/1716 28 | # Fixes `FATAL: ThreadSanitizer: unexpected memory mapping 0x70498d8ae000-0x70498dd00000` type errors 29 | - name: Adjust mmap_rnd_bits on ubuntu 30 | if: startsWith(matrix.os, 'ubuntu') 31 | run: | 32 | sudo sysctl vm.mmap_rnd_bits=28 33 | 34 | - name: Build vdf-client on Ubuntu 35 | if: startsWith(matrix.os, 'ubuntu') 36 | run: | 37 | sudo apt-get install libgmp-dev libboost-python-dev libpython3-dev libboost-system-dev build-essential -y 38 | cd src 39 | make ${{ matrix.config }} -f Makefile.vdf-client 40 | 41 | - name: Build vdf-client on Mac 42 | if: startsWith(matrix.os, 'mac') 43 | run: | 44 | brew install boost 45 | cd src 46 | make ${{ matrix.config }} -f Makefile.vdf-client 47 | 48 | - name: Test vdf-client 49 | if: matrix.config == 'optimized=1' 50 | run: | 51 | cd src 52 | echo "Running 1weso_test" 53 | ./1weso_test 54 | echo "Running 2weso_test" 55 | ./2weso_test 56 | echo "Running prover_test" 57 | ./prover_test 58 | 59 | - name: Test vdf-client 60 | if: matrix.config != 'optimized=1' 61 | run: | 62 | cd src 63 | echo "Running 1weso_test" 64 | ./1weso_test 1000 65 | echo "Running 2weso_test" 66 | ./2weso_test 1000 67 | 68 | # macos thread sanitizer will give false positives on ./prover_test because 69 | # it prints to std::cout from multiple threads. Which is allowed by the 70 | # standard 71 | - name: test Prover 72 | if: matrix.config != 'optimized=1' && (matrix.config != 'TSAN=1' || startsWith(matrix.os, 'ubuntu')) 73 | run: | 74 | cd src 75 | echo "Running prover_test" 76 | ./prover_test 77 | 78 | - name: Benchmark vdf-client 79 | if: matrix.config == 'optimized=1' 80 | run: | 81 | cd src 82 | echo "Benchmarking vdf_client with 400,000 iterations of vdf_bench" 83 | ./vdf_bench square_asm 400000 84 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | *.so 6 | *.dylib 7 | *.dll 8 | *.a 9 | src/verifier_test 10 | 11 | # Generated assembly file 12 | /asm_compiled.s 13 | /avx2_asm_compiled.s 14 | 15 | # Makefiles 16 | CMakeFiles/ 17 | Makefile 18 | CMakeCache.txt 19 | cmake_install.cmake 20 | _deps/ 21 | dist 22 | 23 | # Binary files 24 | src/compile_asm 25 | /vdf 26 | /server 27 | src/vdf_bench 28 | /vdf_server 29 | src/vdf_client 30 | src/1weso_test 31 | src/2weso_test 32 | src/prover_test 33 | src/emu_hw_test 34 | src/hw_test 35 | src/emu_hw_vdf_client 36 | src/hw_vdf_client 37 | /verifier 38 | /verifier_test 39 | /build/* 40 | *.whl 41 | *.egg-info 42 | *.o 43 | *.s 44 | 45 | # external library 46 | /src/hw/libft4222 47 | 48 | # pyenv 49 | .python-version 50 | .eggs 51 | .venv 52 | venv 53 | activate 54 | 55 | # Tests cache 56 | .mypy_cache 57 | .pytest_cache 58 | 59 | src/cmake-build-debug/ 60 | .idea 61 | 62 | # Rust 63 | /target 64 | .vscode 65 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = ["./rust_bindings", './rust_bindings/fuzz'] 4 | -------------------------------------------------------------------------------- /README_ASIC.md: -------------------------------------------------------------------------------- 1 | # ASIC timelord user guide 2 | 3 | ## Initial setup 4 | 5 | Download and unpack LibFT4222 library: 6 | ```bash 7 | # in chiavdf directory 8 | wget https://ftdichip.com/wp-content/uploads/2022/06/libft4222-linux-1.4.4.170.tgz 9 | mkdir src/hw/libft4222 10 | tar -C src/hw/libft4222 -xf libft4222-linux-1.4.4.170.tgz 11 | ln -s libft4222.so.1.4.4.170 src/hw/libft4222/build-x86_64/libft4222.so 12 | ``` 13 | 14 | Build binaries: 15 | ```bash 16 | cd src 17 | make -f Makefile.vdf-client emu_hw_test hw_test emu_hw_vdf_client hw_vdf_client 18 | ``` 19 | 20 | Connect the Chia VDF ASIC device and verify that it is detected: 21 | ```bash 22 | # in chiavdf/src/ directory 23 | LD_LIBRARY_PATH=hw/libft4222/build-x86_64 ./hw_vdf_client --list 24 | ``` 25 | 26 | If the device is shown in the list, check if it's working: 27 | ```bash 28 | LD_LIBRARY_PATH=hw/libft4222/build-x86_64 ./hw_test 29 | ``` 30 | 31 | Output should contain lines similar to the following: 32 | ``` 33 | VDF 0: 1000000 HW iters done in 2s, HW speed: 714720 ips 34 | ``` 35 | 36 | ## Running 37 | 38 | You should have a Chia full node running and synced. 39 | 40 | Start timelord (but not timelord-launcher) in chia-blockchain: 41 | ```bash 42 | chia start timelord-only 43 | ``` 44 | 45 | Start hardware VDF client (`8000` specifies timelord's port number): 46 | ```bash 47 | # in chiavdf/src/ directory 48 | LD_LIBRARY_PATH=hw/libft4222/build-x86_64 ./hw_vdf_client 8000 49 | ``` 50 | 51 | The VDF client accepts a number of options: 52 | ``` 53 | Usage: ./hw_vdf_client [OPTIONS] PORT [N_VDFS] 54 | List of options [default, min - max]: 55 | --freq N - set ASIC frequency [1100, 200 - 2200] 56 | --voltage N - set board voltage [0.88, 0.7 - 1.0] 57 | --ip A.B.C.D - timelord IP address [localhost] 58 | Allows connecting to a timelord running on a remote host. Useful when running multiple machines with VDF hardware connecting to a single timelord. 59 | --vdfs-mask N - mask for enabling VDF engines [7, 1 - 7] 60 | The ASIC has 3 VDF engines numbered 0, 1, 2. If not running all 3 engines, the mask can be specified to enable specific engines. It must be the result of bitwise OR of the engine bits (1, 2, 4 for engines 0, 1, 2). 61 | --vdf-threads N - number of software threads per VDF engine [4, 2 - 64] 62 | Number of software threads computing intermediate values and proofs per VDF engine. 63 | --proof-threads N - number of proof threads per VDF engine 64 | Number of software threads only computing proofs per VDF engine. Must be less than --vdf-threads. 65 | --auto-freq-period N - auto-adjust frequency every N seconds [0, 10 - inf] 66 | --list - list available devices and exit 67 | ``` 68 | 69 | ## Shutting down 70 | 71 | Stop timelord: 72 | ```bash 73 | chia stop timelord-only 74 | ``` 75 | 76 | Stop the VDF client by pressing Control-C or killing the process with `SIGTERM`. 77 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | Please report security concerns to https://hackerone.com/chia_network. 6 | 7 | If your security issue is established to be valid, we will reach out immediately to establish 8 | communication channels and compensate the issue reporter for responsibly reporting security bugs via 9 | our bug bounty program. 10 | -------------------------------------------------------------------------------- /assets/deb/control.j2: -------------------------------------------------------------------------------- 1 | Package: chiavdf-hw 2 | Version: {{ INSTALLER_VERSION }} 3 | Architecture: {{ PLATFORM }} 4 | Maintainer: Chia Network Inc 5 | Description: Hardware VDF Client 6 | Depends: libgmp-dev, libboost-system-dev 7 | -------------------------------------------------------------------------------- /classgroups.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chia-Network/chiavdf/e2e30791d67c15390654b6bbbefff0f28526ab2e/classgroups.pdf -------------------------------------------------------------------------------- /lgtm.yml: -------------------------------------------------------------------------------- 1 | extraction: 2 | cpp: 3 | after_prepare: 4 | - "mkdir custom_cmake" 5 | - "wget --quiet -O - \"https://cmake.org/files/v3.16/cmake-3.16.3-Linux-x86_64.tar.gz\"\ 6 | \ | tar --strip-components=1 -xz -C custom_cmake" 7 | - "export PATH=$(pwd)/custom_cmake/bin:${PATH}" 8 | - "mkdir $LGTM_SRC/_lgtm_build_dir" 9 | - "cd $LGTM_SRC/_lgtm_build_dir" 10 | -------------------------------------------------------------------------------- /mypi.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | ignore_missing_imports = True 3 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.5.0", "pybind11>=2.10.0"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.setuptools_scm] 6 | fallback_version = "unknown-no-.git-directory" 7 | local_scheme = "no-local-version" 8 | 9 | [tool.cibuildwheel] 10 | test-requires = "pytest" 11 | test-command = "pytest -v {project}/tests" 12 | skip = "*-manylinux_i686 *-win32 *-musllinux_*" 13 | 14 | [tool.cibuildwheel.linux] 15 | environment = "BUILD_VDF_CLIENT=N" 16 | build-verbosity = 0 17 | before-all = """ 18 | yum -y install epel-release \ 19 | && echo "epel-release installed" \ 20 | && yum -y install boost-devel lzip \ 21 | && echo "boost-devel and lzip installed" \ 22 | && curl -L https://ftp.gnu.org/gnu/gmp/gmp-6.2.1.tar.lz | tar x --lzip \ 23 | && cp src/lib/gmp-patch-6.2.1/longlong.h gmp-6.2.1/ \ 24 | && cp src/lib/gmp-patch-6.2.1/compat.c gmp-6.2.1/ \ 25 | && cp src/lib/gmp-patch-6.2.1/mpz/inp_raw.c gmp-6.2.1/mpz \ 26 | && cd gmp-6.2.1 && ./configure --enable-fat --enable-cxx \ 27 | && make && make install && cd .. && rm -rf gmp-6.2.1 \ 28 | && cmake --version \ 29 | && uname -a \ 30 | """ 31 | before-build = "python -m pip install --upgrade pip" 32 | 33 | [tool.cibuildwheel.macos] 34 | build-verbosity = 0 35 | before-all = "brew install gmp boost cmake" 36 | before-build = "python -m pip install --upgrade pip" 37 | environment = {MACOSX_DEPLOYMENT_TARGET="13", SYSTEM_VERSION_COMPAT=0, BUILD_VDF_CLIENT="N"} 38 | 39 | [tool.cibuildwheel.windows] 40 | build-verbosity = 0 41 | before-all = "git clone https://github.com/Chia-Network/mpir_gc_x64.git" 42 | environment = {BUILD_VDF_CLIENT="N"} 43 | before-build = "pip install delvewheel" 44 | repair-wheel-command = """ 45 | delvewheel repair -v -w {dest_dir} {wheel} \ 46 | --no-mangle-all \ 47 | --add-path mpir_gc_x64 \ 48 | --add-dll mpir.dll;mpir_gc.dll;mpir_broadwell.dll;mpir_broadwell_avx.dll;mpir_bulldozer.dll;mpir_haswell.dll;mpir_piledriver.dll;mpir_sandybridge.dll;mpir_skylake_avx.dll \ 49 | """ 50 | -------------------------------------------------------------------------------- /rust_bindings/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chiavdf" 3 | version = "1.1.11" 4 | edition = "2021" 5 | license = "Apache-2.0" 6 | description = "Bindings to the chiavdf C++ library." 7 | authors = ["Brandon Haggstrom "] 8 | homepage = "https://github.com/Chia-Network/chiavdf" 9 | repository = "https://github.com/Chia-Network/chiavdf" 10 | 11 | [dependencies] 12 | link-cplusplus = "1.0.10" 13 | 14 | [build-dependencies] 15 | bindgen = "0.71.1" 16 | cmake = "0.1.52" 17 | 18 | [dev-dependencies] 19 | hex = "0.4.3" 20 | hex-literal = "1.0.0" 21 | -------------------------------------------------------------------------------- /rust_bindings/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::PathBuf; 3 | 4 | use cmake::Config; 5 | 6 | fn main() { 7 | println!("cargo:rerun-if-changed=wrapper.h"); 8 | println!("cargo:rerun-if-changed=../src/c_bindings/c_wrapper.h"); 9 | println!("cargo:rerun-if-changed=../src/c_bindings/c_wrapper.cpp"); 10 | 11 | let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); 12 | 13 | let mut src_dir = manifest_dir.join("cpp"); 14 | if !src_dir.exists() { 15 | src_dir = manifest_dir 16 | .parent() 17 | .expect("can't access ../") 18 | .join("src") 19 | .to_path_buf(); 20 | } 21 | 22 | let dst = Config::new(src_dir.as_path()) 23 | .build_target("chiavdfc_static") 24 | .define("BUILD_CHIAVDFC", "ON") 25 | .env("BUILD_VDF_CLIENT", "N") 26 | .define("BUILD_PYTHON", "OFF") 27 | .build(); 28 | 29 | println!("cargo:rustc-link-lib=static=chiavdfc"); 30 | 31 | println!( 32 | "cargo:rustc-link-search=native={}", 33 | dst.join("build") 34 | .join("lib") 35 | .join("static") 36 | .to_str() 37 | .unwrap() 38 | ); 39 | 40 | if cfg!(target_os = "windows") { 41 | println!("cargo:rustc-link-lib=static=mpir"); 42 | println!( 43 | "cargo:rustc-link-search=native={}", 44 | src_dir 45 | .parent() 46 | .unwrap() 47 | .join("mpir_gc_x64") 48 | .to_str() 49 | .unwrap() 50 | ); 51 | } else { 52 | println!("cargo:rustc-link-lib=gmp"); 53 | } 54 | 55 | let bindings = bindgen::Builder::default() 56 | .header(manifest_dir.join("wrapper.h").to_str().unwrap()) 57 | .clang_arg("-x") 58 | .clang_arg("c++") 59 | .clang_arg(format!( 60 | "-I{}", 61 | src_dir.join("c_bindings").to_str().unwrap() 62 | )) 63 | .clang_arg("-std=c++14") 64 | .allowlist_function("verify_n_wesolowski_wrapper") 65 | .allowlist_function("create_discriminant_wrapper") 66 | .allowlist_function("prove_wrapper") 67 | .allowlist_function("free") 68 | .allowlist_function("delete_byte_array") 69 | .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) 70 | .generate() 71 | .expect("Unable to generate bindings"); 72 | 73 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); 74 | bindings 75 | .write_to_file(out_path.join("bindings.rs")) 76 | .expect("Couldn't write bindings!"); 77 | } 78 | -------------------------------------------------------------------------------- /rust_bindings/fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | corpus 3 | artifacts 4 | coverage 5 | -------------------------------------------------------------------------------- /rust_bindings/fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chiavdf-fuzz" 3 | version = "0.0.0" 4 | publish = false 5 | edition = "2021" 6 | 7 | [package.metadata] 8 | cargo-fuzz = true 9 | 10 | [dependencies] 11 | libfuzzer-sys = "0.4" 12 | 13 | [dependencies.chiavdf] 14 | path = ".." 15 | 16 | [[bin]] 17 | name = "create_discriminant" 18 | path = "fuzz_targets/create_discriminant.rs" 19 | test = false 20 | doc = false 21 | bench = false 22 | 23 | [[bin]] 24 | name = "verify_n_wesolowski" 25 | path = "fuzz_targets/verify_n_wesolowski.rs" 26 | test = false 27 | doc = false 28 | bench = false 29 | 30 | [[bin]] 31 | name = "prove" 32 | path = "fuzz_targets/prove.rs" 33 | test = false 34 | doc = false 35 | bench = false 36 | 37 | [[bin]] 38 | name = "verify" 39 | path = "fuzz_targets/verify.rs" 40 | test = false 41 | doc = false 42 | bench = false 43 | -------------------------------------------------------------------------------- /rust_bindings/fuzz/fuzz_targets/create_discriminant.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use chiavdf::create_discriminant; 4 | use libfuzzer_sys::{arbitrary::Unstructured, fuzz_target}; 5 | 6 | fuzz_target!(|data: &[u8]| { 7 | let mut unstructured = Unstructured::new(data); 8 | let seed: [u8; 10] = unstructured.arbitrary().unwrap(); 9 | let mut disc = [0; 64]; 10 | assert!(create_discriminant(&seed, &mut disc)); 11 | }); 12 | -------------------------------------------------------------------------------- /rust_bindings/fuzz/fuzz_targets/prove.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use chiavdf::prove; 4 | use libfuzzer_sys::{arbitrary::Unstructured, fuzz_target}; 5 | 6 | fuzz_target!(|data: &[u8]| { 7 | let mut unstructured = Unstructured::new(data); 8 | let genesis_challenge: [u8; 32] = unstructured.arbitrary().unwrap(); 9 | let element: [u8; 100] = unstructured.arbitrary().unwrap(); 10 | let iters: u8 = unstructured.arbitrary().unwrap(); 11 | prove(&genesis_challenge, &element, 1024, iters as u64); 12 | }); 13 | -------------------------------------------------------------------------------- /rust_bindings/fuzz/fuzz_targets/verify.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use chiavdf::{create_discriminant, prove, verify_n_wesolowski}; 4 | use libfuzzer_sys::{arbitrary::Unstructured, fuzz_target}; 5 | 6 | fuzz_target!(|data: &[u8]| { 7 | let mut unstructured = Unstructured::new(data); 8 | let genesis_challenge: [u8; 32] = unstructured.arbitrary().unwrap(); 9 | let mut default_el = [0; 100]; 10 | default_el[0] = 0x08; 11 | let proof = prove(&genesis_challenge, &default_el, 1024, 231).unwrap(); 12 | let mut disc = [0; 128]; 13 | assert!(create_discriminant(&genesis_challenge, &mut disc)); 14 | let valid = verify_n_wesolowski(&disc, &default_el, &proof, 231, 0); 15 | assert!(valid); 16 | }); 17 | -------------------------------------------------------------------------------- /rust_bindings/fuzz/fuzz_targets/verify_n_wesolowski.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use chiavdf::{create_discriminant, verify_n_wesolowski}; 4 | use libfuzzer_sys::{arbitrary::Unstructured, fuzz_target}; 5 | 6 | fuzz_target!(|data: &[u8]| { 7 | let mut unstructured = Unstructured::new(data); 8 | let seed: [u8; 10] = unstructured.arbitrary().unwrap(); 9 | let mut disc = [0; 64]; 10 | if !create_discriminant(&seed, &mut disc) { 11 | return; 12 | }; 13 | let element: [u8; 100] = unstructured.arbitrary().unwrap(); 14 | let proof: Vec = unstructured.arbitrary().unwrap(); 15 | let iters: u8 = unstructured.arbitrary().unwrap(); 16 | verify_n_wesolowski(&disc, &element, &proof, iters as u64, 0); 17 | }); 18 | -------------------------------------------------------------------------------- /rust_bindings/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | #![allow(non_snake_case)] 3 | #![allow(dead_code)] 4 | #![allow(non_upper_case_globals)] 5 | 6 | extern crate link_cplusplus; 7 | 8 | mod bindings { 9 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 10 | } 11 | 12 | pub fn create_discriminant(seed: &[u8], result: &mut [u8]) -> bool { 13 | // SAFETY: The length of each individual array is passed in as to prevent buffer overflows. 14 | // Exceptions are handled on the C++ side and None is returned if so. 15 | unsafe { 16 | bindings::create_discriminant_wrapper( 17 | seed.as_ptr(), 18 | seed.len(), 19 | result.len() * 8, 20 | result.as_mut_ptr(), 21 | ) 22 | } 23 | } 24 | 25 | pub fn verify_n_wesolowski( 26 | discriminant: &[u8], 27 | x_s: &[u8], 28 | proof: &[u8], 29 | num_iterations: u64, 30 | recursion: u64, 31 | ) -> bool { 32 | // SAFETY: The length of each individual array is passed in as to prevent buffer overflows. 33 | // Exceptions are handled on the C++ side and false is returned if so. 34 | unsafe { 35 | bindings::verify_n_wesolowski_wrapper( 36 | discriminant.as_ptr(), 37 | discriminant.len(), 38 | x_s.as_ptr(), 39 | proof.as_ptr(), 40 | proof.len(), 41 | num_iterations, 42 | recursion, 43 | ) 44 | } 45 | } 46 | 47 | pub fn prove( 48 | challenge: &[u8], 49 | x_s: &[u8], 50 | discriminant_size_bits: usize, 51 | num_iterations: u64, 52 | ) -> Option> { 53 | // SAFETY: The length of each individual array is passed in as to prevent buffer overflows. 54 | // Exceptions are handled on the C++ side and a null pointer is returned for `data` if so. 55 | unsafe { 56 | let array = bindings::prove_wrapper( 57 | challenge.as_ptr(), 58 | challenge.len(), 59 | x_s.as_ptr(), 60 | x_s.len(), 61 | discriminant_size_bits, 62 | num_iterations, 63 | ); 64 | if array.data.is_null() { 65 | return None; 66 | } 67 | let result = std::slice::from_raw_parts(array.data, array.length).to_vec(); 68 | bindings::delete_byte_array(array); 69 | Some(result) 70 | } 71 | } 72 | 73 | #[cfg(test)] 74 | mod tests { 75 | use hex_literal::hex; 76 | 77 | use super::*; 78 | 79 | #[test] 80 | fn test_create_discriminant() { 81 | let seeds = [ 82 | hex!("6c3b9aa767f785b537c0"), 83 | hex!("b10da48cea4c09676b8e"), 84 | hex!("c51b8a31c98b9fe13065"), 85 | hex!("5de9bc1bb4cb7a9f9cf9"), 86 | hex!("22cfaefc92e4edb9b0ae"), 87 | ]; 88 | 89 | let mut discriminants = Vec::new(); 90 | 91 | for seed in seeds { 92 | let mut discriminant = [0; 64]; 93 | assert!(create_discriminant(&seed, &mut discriminant)); 94 | discriminants.push(discriminant); 95 | } 96 | 97 | // These came from running the Python `create_discriminant` with the same seeds. 98 | let expected = [ 99 | "9a8eaf9c52d9a5f1db648cdf7bcd04b35cb1ac4f421c978fa61fe1344b97d4199dbff700d24e7cfc0b785e4b8b8023dc49f0e90227f74f54234032ac3381879f", 100 | "b193cdb02f1c2615a257b98933ee0d24157ac5f8c46774d5d635022e6e6bd3f7372898066c2a40fa211d1df8c45cb95c02e36ef878bc67325473d9c0bb34b047", 101 | "bb5bd19ae50efe98b5ac56c69453a95e92dc16bb4b2824e73b39b9db0a077fa33fc2e775958af14f675a071bf53f1c22f90ccbd456e2291276951830dba9dcaf", 102 | "a1e93b8f2e9b0fd3b1325fbe40601f55e2afbdc6161409c0aff8737b7213d7d71cab21ffc83a0b6d5bdeee2fdcbbb34fbc8fc0b439915075afa9ffac8bb1b337", 103 | "f2a10f70148fb30e4a16c4eda44cc0f9917cb9c2d460926d59a408318472e2cfd597193aa58e1fdccc6ae6a4d85bc9b27f77567ebe94fcedbf530a60ff709fd7", 104 | ]; 105 | 106 | for i in 0..5 { 107 | assert_eq!( 108 | hex::encode(discriminants[i]), 109 | expected[i], 110 | "Discriminant {} does not match (seed is {})", 111 | i, 112 | hex::encode(seeds[i]) 113 | ); 114 | } 115 | } 116 | 117 | #[test] 118 | fn test_verify_n_wesolowski() { 119 | let genesis_challenge = 120 | hex!("ccd5bb71183532bff220ba46c268991a3ff07eb358e8255a65c30a2dce0e5fbb"); 121 | 122 | let mut default_el = [0; 100]; 123 | default_el[0] = 0x08; 124 | 125 | let mut disc = [0; 128]; 126 | assert!(create_discriminant(&genesis_challenge, &mut disc)); 127 | let proof = prove(&genesis_challenge, &default_el, 1024, 231).unwrap(); 128 | let valid = verify_n_wesolowski(&disc, &default_el, &proof, 231, 0); 129 | assert!(valid); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /rust_bindings/wrapper.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "c_wrapper.h" 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import shutil 4 | import subprocess 5 | import sys 6 | 7 | from setuptools import Command, Extension, setup 8 | from setuptools.command.build import build 9 | from setuptools.command.build_ext import build_ext 10 | from setuptools.command.install import install 11 | 12 | BUILD_HOOKS = [] 13 | INSTALL_HOOKS = [] 14 | 15 | 16 | def add_install_hook(hook): 17 | INSTALL_HOOKS.append(hook) 18 | 19 | 20 | def add_build_hook(hook): 21 | BUILD_HOOKS.append(hook) 22 | 23 | 24 | class HookCommand(Command): 25 | def __init__(self, dist): 26 | self.dist = dist 27 | Command.__init__(self, dist) 28 | 29 | def initialize_options(self, *args): 30 | self.install_dir = None 31 | self.build_dir = None 32 | 33 | def finalize_options(self): 34 | self.set_undefined_options("build", ("build_scripts", "build_dir")) 35 | self.set_undefined_options( 36 | "install", 37 | ("install_platlib", "install_dir"), 38 | ) 39 | 40 | def run(self): 41 | for _ in self.hooks: 42 | _(install_dir=self.install_dir, build_dir=self.build_dir) 43 | 44 | 45 | class build_hook(HookCommand): 46 | hooks = BUILD_HOOKS 47 | 48 | 49 | class install_hook(HookCommand): 50 | hooks = INSTALL_HOOKS 51 | 52 | 53 | ############################################ 54 | 55 | 56 | class CMakeExtension(Extension): 57 | def __init__(self, name, sourcedir=""): 58 | Extension.__init__(self, name, sources=["./"]) 59 | self.sourcedir = os.path.abspath(sourcedir) 60 | 61 | 62 | def copy_vdf_client(build_dir, install_dir): 63 | shutil.copy("src/vdf_client", install_dir) 64 | shutil.copy("src/prover_test", install_dir) 65 | shutil.copy("src/1weso_test", install_dir) 66 | shutil.copy("src/2weso_test", install_dir) 67 | 68 | 69 | def copy_vdf_bench(build_dir, install_dir): 70 | shutil.copy("src/vdf_bench", install_dir) 71 | 72 | 73 | def invoke_make(**kwargs): 74 | subprocess.check_output("make -C src -f Makefile.vdf-client", shell=True) 75 | 76 | 77 | BUILD_VDF_CLIENT = os.getenv("BUILD_VDF_CLIENT", "Y") == "Y" 78 | BUILD_VDF_BENCH = os.getenv("BUILD_VDF_BENCH", "N") == "Y" 79 | 80 | 81 | if BUILD_VDF_CLIENT or BUILD_VDF_BENCH: 82 | add_build_hook(invoke_make) 83 | 84 | if BUILD_VDF_CLIENT: 85 | add_install_hook(copy_vdf_client) 86 | 87 | if BUILD_VDF_BENCH: 88 | add_install_hook(copy_vdf_bench) 89 | 90 | 91 | class CMakeBuild(build_ext): 92 | def run(self): 93 | try: 94 | subprocess.check_output(["cmake", "--version"]) 95 | except OSError: 96 | raise RuntimeError( 97 | "CMake must be installed to build" 98 | + " the following extensions: " 99 | + ", ".join(e.name for e in self.extensions) 100 | ) 101 | 102 | for ext in self.extensions: 103 | self.build_extension(ext) 104 | 105 | def build_extension(self, ext): 106 | extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) 107 | cmake_args = [ 108 | "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=" + str(extdir), 109 | "-DPYTHON_EXECUTABLE=" + sys.executable, 110 | ] 111 | 112 | cfg = "Debug" if self.debug else "Release" 113 | build_args = ["--config", cfg] 114 | 115 | if platform.system() == "Windows": 116 | cmake_args += [ 117 | "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}".format(cfg.upper(), extdir) 118 | ] 119 | if sys.maxsize > 2**32: 120 | cmake_args += ["-A", "x64"] 121 | build_args += ["--", "/m"] 122 | else: 123 | cmake_args += ["-DCMAKE_BUILD_TYPE=" + cfg] 124 | build_args += ["--", "-j", "6"] 125 | 126 | env = os.environ.copy() 127 | env["CXXFLAGS"] = '{} -DVERSION_INFO=\\"{}\\"'.format( 128 | env.get("CXXFLAGS", ""), self.distribution.get_version() 129 | ) 130 | subprocess.check_call(["cmake", ext.sourcedir] + cmake_args, env=env) 131 | subprocess.check_call(["cmake", "--build", "."] + build_args) 132 | 133 | 134 | build.sub_commands.append(("build_hook", lambda x: True)) # type: ignore 135 | install.sub_commands.append(("install_hook", lambda x: True)) 136 | 137 | setup( 138 | name="chiavdf", 139 | author="Florin Chirica", 140 | author_email="florin@chia.net", 141 | description="Chia vdf verification (wraps C++)", 142 | license="Apache-2.0", 143 | python_requires=">=3.9", 144 | long_description=open("README.md").read(), 145 | long_description_content_type="text/markdown", 146 | url="https://github.com/Chia-Network/chiavdf", 147 | ext_modules=[CMakeExtension("chiavdf", "src")], 148 | cmdclass=dict( 149 | build_ext=CMakeBuild, install_hook=install_hook, build_hook=build_hook 150 | ), 151 | zip_safe=False, 152 | ) 153 | -------------------------------------------------------------------------------- /src/1weso_test.cpp: -------------------------------------------------------------------------------- 1 | #include "vdf.h" 2 | #include "create_discriminant.h" 3 | #include "verifier.h" 4 | 5 | #include 6 | #include 7 | 8 | int segments = 7; 9 | int thread_count = 3; 10 | 11 | Proof CreateProof(ProverManager& pm, uint64_t iteration) { 12 | return pm.Prove(iteration); 13 | } 14 | 15 | int gcd_base_bits=50; 16 | int gcd_128_max_iter=3; 17 | 18 | int main(int argc, char const* argv[]) try 19 | { 20 | // allow setting the multiplier for the number of iterations to test on the 21 | // command line. This can be used to run smaller and faster tests on CI, 22 | // specifically with instrumented binaries that aren't as fast 23 | std::uint64_t const iter_multiplier = (argc > 1) 24 | ? std::stoull(argv[1]) : 1000000; 25 | 26 | assert(is_vdf_test); //assertions should be disabled in VDF_MODE==0 27 | init_gmp(); 28 | debug_mode = true; 29 | if(hasAVX2()) 30 | { 31 | gcd_base_bits=63; 32 | gcd_128_max_iter=2; 33 | } 34 | std::vector challenge_hash({0, 0, 1, 2, 3, 3, 4, 4}); 35 | int d_bits = 1024; 36 | integer D = CreateDiscriminant(challenge_hash, d_bits); 37 | 38 | if (getenv( "warn_on_corruption_in_production" )!=nullptr) { 39 | warn_on_corruption_in_production=true; 40 | } 41 | set_rounding_mode(); 42 | 43 | integer L=root(-D, 4); 44 | form f=form::generator(D); 45 | 46 | std::atomic stopped = false; 47 | fast_algorithm = false; 48 | 49 | uint64_t iter = iter_multiplier; 50 | OneWesolowskiCallback weso(D, f, iter); 51 | FastStorage* fast_storage = nullptr; 52 | std::thread vdf_worker(repeated_square, iter, f, D, L, &weso, fast_storage, std::ref(stopped)); 53 | Proof const proof = ProveOneWesolowski(iter, D, f, &weso, stopped); 54 | stopped = true; 55 | vdf_worker.join(); 56 | 57 | bool is_valid; 58 | form x_init = form::generator(D); 59 | form y = DeserializeForm(D, proof.y.data(), proof.y.size()); 60 | form proof_form = DeserializeForm(D, proof.proof.data(), proof.proof.size()); 61 | VerifyWesolowskiProof(D, x_init, y, proof_form, iter, is_valid); 62 | std::cout << "Verify result: " << is_valid << "\n"; 63 | assert(is_valid); 64 | } 65 | catch (std::exception const& e) { 66 | std::cerr << "Exception " << e.what() << '\n'; 67 | return 1; 68 | } 69 | -------------------------------------------------------------------------------- /src/2weso_test.cpp: -------------------------------------------------------------------------------- 1 | #include "vdf.h" 2 | #include "create_discriminant.h" 3 | #include "verifier.h" 4 | 5 | #include 6 | 7 | int segments = 7; 8 | int thread_count = 3; 9 | 10 | int gcd_base_bits=50; 11 | int gcd_128_max_iter=3; 12 | 13 | void CheckProof(integer& D, Proof& proof, uint64_t iteration) { 14 | form x = form::generator(D); 15 | std::vector bytes; 16 | bytes.insert(bytes.end(), proof.y.begin(), proof.y.end()); 17 | bytes.insert(bytes.end(), proof.proof.begin(), proof.proof.end()); 18 | if (CheckProofOfTimeNWesolowski(D, DEFAULT_ELEMENT, bytes.data(), bytes.size(), iteration, 1024, proof.witness_type)) { 19 | std::cout << "Correct proof\n"; 20 | } else { 21 | std::cout << "Incorrect proof\n"; 22 | throw std::runtime_error("incorrect proof"); 23 | } 24 | } 25 | 26 | int main(int argc, char const* argv[]) try 27 | { 28 | // allow setting the multiplier for the number of iterations to test on the 29 | // command line. This can be used to run smaller and faster tests on CI, 30 | // specifically with instrumented binaries that aren't as fast 31 | std::uint64_t const iter_multiplier = (argc > 1) 32 | ? std::stoull(argv[1]) : 1000000; 33 | 34 | assert(is_vdf_test); //assertions should be disabled in VDF_MODE==0 35 | init_gmp(); 36 | debug_mode = true; 37 | if(hasAVX2()) 38 | { 39 | gcd_base_bits=63; 40 | gcd_128_max_iter=2; 41 | } 42 | std::vector challenge_hash({0, 0, 1, 2, 3, 3, 4, 4}); 43 | integer D = CreateDiscriminant(challenge_hash, 1024); 44 | 45 | if (getenv( "warn_on_corruption_in_production" )!=nullptr) { 46 | warn_on_corruption_in_production=true; 47 | } 48 | set_rounding_mode(); 49 | 50 | integer L=root(-D, 4); 51 | form f=form::generator(D); 52 | 53 | std::atomic stopped = false; 54 | fast_algorithm = false; 55 | two_weso = true; 56 | TwoWesolowskiCallback weso(D, f); 57 | FastStorage* fast_storage = NULL; 58 | std::thread vdf_worker(repeated_square, 0, f, D, L, &weso, fast_storage, std::ref(stopped)); 59 | // Test 1 - 1 million iters. 60 | uint64_t iteration = 1 * iter_multiplier; 61 | Proof proof = ProveTwoWeso(D, f, iteration, 0, &weso, 0, stopped); 62 | CheckProof(D, proof, iteration); 63 | // Test 2 - 15 million iters. 64 | iteration = 15 * iter_multiplier; 65 | proof = ProveTwoWeso(D, f, iteration, 0, &weso, 0, stopped); 66 | CheckProof(D, proof, iteration); 67 | // Test 3 - 100 million iters. 68 | iteration = 100 * iter_multiplier; 69 | proof = ProveTwoWeso(D, f, iteration, 0, &weso, 0, stopped); 70 | CheckProof(D, proof, iteration); 71 | // Test stopping gracefully. 72 | stopped = true; 73 | vdf_worker.join(); 74 | return 0; 75 | } 76 | catch (std::exception const& e) { 77 | std::cerr << "Exception " << e.what() << '\n'; 78 | return 1; 79 | } 80 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.14 FATAL_ERROR) 2 | option(BUILD_CHIAVDFC "Build the chiavdfc shared library" OFF) 3 | option(BUILD_PYTHON "Build the python bindings for chiavdf" ON) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 8 | 9 | IF(NOT CMAKE_BUILD_TYPE) 10 | SET(CMAKE_BUILD_TYPE "RELEASE") 11 | ENDIF() 12 | 13 | project(chiavdf) 14 | 15 | set(CMAKE_MODULE_PATH 16 | ${CMAKE_CURRENT_LIST_DIR}/cmake 17 | ${CMAKE_MODULE_PATH} 18 | ) 19 | 20 | if(MSVC) 21 | add_compile_options(/EHsc) 22 | else() 23 | add_compile_options("$<$:-Og>") 24 | endif() 25 | 26 | if(WIN32) 27 | set(MPIR_LIBRARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../mpir_gc_x64") 28 | set(MPIR_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../mpir_gc_x64") 29 | include_directories( 30 | ${INCLUDE_DIRECTORIES} 31 | ${CMAKE_CURRENT_SOURCE_DIR} 32 | ${MPIR_INCLUDE_DIR} 33 | ) 34 | find_library(MPIR_LIBRARY NAMES mpir PATHS ${MPIR_LIBRARY_DIR} NO_DEFAULT_PATH) 35 | if(MPIR_LIBRARY) 36 | message(STATUS "MPIR library found at ${MPIR_LIBRARY}") 37 | link_libraries(${MPIR_LIBRARY}) 38 | else() 39 | message(FATAL_ERROR "MPIR library not found") 40 | endif() 41 | 42 | list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../mpir_gc_x64") 43 | else() 44 | find_package(GMP REQUIRED) 45 | find_package(GMPXX REQUIRED) 46 | 47 | include_directories( 48 | ${INCLUDE_DIRECTORIES} 49 | ${CMAKE_CURRENT_SOURCE_DIR} 50 | ${GMP_INCLUDE_DIR} 51 | ${GMPXX_INCLUDE_DIR} 52 | ) 53 | endif() 54 | 55 | # CMake 3.14+ 56 | include(FetchContent) 57 | 58 | if(BUILD_PYTHON) 59 | FetchContent_Declare( 60 | pybind11-src 61 | GIT_REPOSITORY https://github.com/pybind/pybind11.git 62 | GIT_TAG v2.13.6 63 | ) 64 | FetchContent_MakeAvailable(pybind11-src) 65 | 66 | pybind11_add_module(chiavdf 67 | ${CMAKE_CURRENT_SOURCE_DIR}/python_bindings/fastvdf.cpp 68 | ${CMAKE_CURRENT_SOURCE_DIR}/refcode/lzcnt.c 69 | ) 70 | 71 | target_link_libraries(chiavdf PRIVATE ${GMP_LIBRARIES} ${GMPXX_LIBRARIES}) 72 | if(UNIX) 73 | target_link_libraries(chiavdf PRIVATE -pthread) 74 | endif() 75 | if (WIN32) 76 | # workaround for constexpr mutex constructor change in MSVC 2022 77 | # https://stackoverflow.com/questions/78598141/first-stdmutexlock-crashes-in-application-built-with-latest-visual-studio 78 | target_compile_definitions(chiavdf PUBLIC _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR) 79 | endif() 80 | endif() 81 | 82 | add_executable(verifier_test 83 | ${CMAKE_CURRENT_SOURCE_DIR}/verifier_test.cpp 84 | ${CMAKE_CURRENT_SOURCE_DIR}/refcode/lzcnt.c 85 | ) 86 | add_executable(stress_test 87 | ${CMAKE_CURRENT_SOURCE_DIR}/stress_test.cpp 88 | ${CMAKE_CURRENT_SOURCE_DIR}/refcode/lzcnt.c 89 | ) 90 | 91 | target_link_libraries(verifier_test PRIVATE ${GMP_LIBRARIES} ${GMPXX_LIBRARIES}) 92 | target_link_libraries(stress_test PRIVATE ${GMP_LIBRARIES} ${GMPXX_LIBRARIES}) 93 | 94 | if(UNIX) 95 | target_link_libraries(verifier_test PRIVATE -pthread) 96 | target_link_libraries(stress_test PRIVATE -pthread) 97 | endif() 98 | 99 | if(BUILD_CHIAVDFC) 100 | add_library(chiavdfc_shared SHARED 101 | ${CMAKE_CURRENT_SOURCE_DIR}/c_bindings/c_wrapper.cpp 102 | ${CMAKE_CURRENT_SOURCE_DIR}/refcode/lzcnt.c 103 | ) 104 | add_library(chiavdfc_static STATIC 105 | ${CMAKE_CURRENT_SOURCE_DIR}/c_bindings/c_wrapper.cpp 106 | ${CMAKE_CURRENT_SOURCE_DIR}/refcode/lzcnt.c 107 | ) 108 | target_link_libraries(chiavdfc_shared ${GMP_LIBRARIES} ${GMPXX_LIBRARIES}) 109 | target_link_libraries(chiavdfc_static ${GMP_LIBRARIES} ${GMPXX_LIBRARIES}) 110 | 111 | set_target_properties(chiavdfc_shared PROPERTIES 112 | OUTPUT_NAME chiavdfc 113 | LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/shared$<0:>" 114 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/shared$<0:>" 115 | ) 116 | 117 | set_target_properties(chiavdfc_static PROPERTIES 118 | OUTPUT_NAME chiavdfc 119 | ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/static$<0:>" 120 | ) 121 | endif() 122 | -------------------------------------------------------------------------------- /src/ClassGroup.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (C) 2018 Markku Pulkkinen 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | **/ 16 | 17 | #ifndef CLASSGROUP_H 18 | #define CLASSGROUP_H 19 | 20 | #include 21 | #include "gmp.h" 22 | 23 | /** 24 | * @brief The ClassGroup data struct for VDF variables a, b, c and discriminant. 25 | * Optimal size because it fits into single entry of 64 byte wide cache line. 26 | */ 27 | struct alignas(64) ClassGroup { 28 | mpz_t a; 29 | mpz_t b; 30 | mpz_t c; 31 | mpz_t d; 32 | }; 33 | 34 | /** 35 | * @brief ClassGroupContext struct - placeholder for variables 36 | * in classgroup arithmetic operations. Uses four cache 37 | * line entries, 256 bytes. 38 | */ 39 | struct alignas(64) ClassGroupContext { 40 | mpz_t a; 41 | mpz_t b; 42 | mpz_t c; 43 | mpz_t mu; 44 | 45 | mpz_t m; 46 | mpz_t r; 47 | mpz_t s; 48 | mpz_t faa; 49 | 50 | mpz_t fab; 51 | mpz_t fac; 52 | mpz_t fba; 53 | mpz_t fbb; 54 | 55 | mpz_t fbc; 56 | mpz_t fca; 57 | mpz_t fcb; 58 | mpz_t fcc; 59 | 60 | ClassGroupContext(uint32_t numBits = 4096) { 61 | mpz_init2(a, numBits); 62 | mpz_init2(b, numBits); 63 | mpz_init2(c, numBits); 64 | mpz_init2(mu, numBits); 65 | mpz_init2(m, numBits); 66 | mpz_init2(r, numBits); 67 | mpz_init2(s, numBits); 68 | mpz_init2(faa, numBits); 69 | mpz_init2(fab, numBits); 70 | mpz_init2(fac, numBits); 71 | mpz_init2(fba, numBits); 72 | mpz_init2(fbb, numBits); 73 | mpz_init2(fbc, numBits); 74 | mpz_init2(fca, numBits); 75 | mpz_init2(fcb, numBits); 76 | mpz_init2(fcc, numBits); 77 | } 78 | 79 | ~ClassGroupContext() { 80 | mpz_clears(a, b, c, mu, m, r, s, faa, fab, fac, fba, fbb, fbc, fca, fcb, 81 | fcc, NULL); 82 | } 83 | }; 84 | 85 | #endif // CLASSGROUP_H 86 | -------------------------------------------------------------------------------- /src/Makefile.vdf-client: -------------------------------------------------------------------------------- 1 | UNAME := $(shell uname) 2 | 3 | ifneq (,$(findstring clang, $(shell $(CXX) --version))) 4 | NOPIE = -fno-PIE 5 | else 6 | NOPIE = -no-pie 7 | endif 8 | 9 | LDFLAGS += -flto $(NOPIE) -g 10 | LDLIBS += -lgmpxx -lgmp -lboost_system -pthread 11 | CXXFLAGS += -flto -std=c++1z -D VDF_MODE=0 -D FAST_MACHINE=1 -pthread $(NOPIE) -fvisibility=hidden 12 | ifeq ($(UNAME),Darwin) 13 | CXXFLAGS += -D CHIAOSX=1 14 | endif 15 | 16 | OPT_CFLAGS = -O3 -g 17 | 18 | ifneq ($(ASAN),) 19 | LDFLAGS += -fsanitize=address -fsanitize=undefined 20 | CXXFLAGS += -g -fsanitize=address -fsanitize=undefined -fsanitize-undefined-trap-on-error 21 | endif 22 | 23 | ifneq ($(TSAN),) 24 | LDFLAGS += -fsanitize=thread 25 | CXXFLAGS += -g -fsanitize=thread 26 | endif 27 | 28 | .PHONY: all clean 29 | 30 | BINS = vdf_client prover_test 1weso_test 2weso_test vdf_bench 31 | all: $(BINS) 32 | 33 | clean: 34 | rm -f *.o hw/*.o $(BINS) compile_asm emu_hw_test hw_test hw_vdf_client emu_hw_vdf_client 35 | 36 | $(BINS) avx512_test: %: %.o lzcnt.o asm_compiled.o avx2_asm_compiled.o avx512_asm_compiled.o 37 | $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) 38 | 39 | $(addsuffix .o,$(BINS)) avx512_test.o: CXXFLAGS += $(OPT_CFLAGS) 40 | 41 | lzcnt.o: refcode/lzcnt.c 42 | $(CC) -c refcode/lzcnt.c 43 | 44 | asm_compiled.s: compile_asm 45 | ./compile_asm 46 | 47 | avx2_asm_compiled.s: compile_asm 48 | ./compile_asm avx2 49 | 50 | avx512_asm_compiled.s: compile_asm 51 | ./compile_asm avx512 52 | 53 | compile_asm: compile_asm.o 54 | $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) 55 | 56 | HW_OBJS = $(addprefix hw/,hw_util.o hw_proof.o hw_interface.o chia_driver.o ftdi_driver.o vdf_driver.o pll_freqs.o) vdf_base.o lzcnt.o 57 | EMU_OBJS = hw/emu_funcs.o hw/emu_runner.o 58 | HW_LIB = hw/libft4222/build-x86_64/libft4222.so 59 | 60 | hw_test: hw/hw_test.o $(HW_OBJS) $(HW_LIB) hw/real_hw.o 61 | $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) 62 | 63 | emu_hw_test: hw/hw_test.o $(HW_OBJS) $(EMU_OBJS) 64 | $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) 65 | 66 | hw_vdf_client: hw/hw_vdf_client.o $(HW_OBJS) $(HW_LIB) hw/real_hw.o 67 | $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) 68 | 69 | emu_hw_vdf_client: hw/hw_vdf_client.o $(HW_OBJS) $(EMU_OBJS) 70 | $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) 71 | 72 | hw/hw_test.o hw/hw_vdf_client.o $(HW_OBJS) $(EMU_OBJS): CXXFLAGS += -I. -Ihw -Ihw/libft4222 $(OPT_CFLAGS) -Wall 73 | -------------------------------------------------------------------------------- /src/alloc.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ALLOC_H 2 | #define ALLOC_H 3 | 4 | #include // for posix_memalign 5 | 6 | inline void* mp_alloc_func(size_t new_bytes) 7 | { 8 | new_bytes = ((new_bytes + 8) + 15) & ~15; 9 | #if defined _MSC_VER 10 | uint8_t* ret = static_cast(_aligned_malloc(new_bytes, 16)); 11 | #else 12 | void* ptr = nullptr; 13 | if (::posix_memalign(&ptr, 16, new_bytes) != 0) return nullptr; 14 | uint8_t* ret = static_cast(ptr); 15 | #endif 16 | return ret + 8; 17 | } 18 | 19 | inline void mp_free_func(void* old_ptr, size_t) { 20 | // if the old_ptr alignment is not to 16 bytes + 8 bytes offset, we did not 21 | // allocate it. It's an in-place buffer and should not be freed 22 | if ((std::uintptr_t(old_ptr) & 15) == 8) { 23 | #if defined _MSC_VER 24 | _aligned_free(static_cast(old_ptr) - 8); 25 | #else 26 | std::free(static_cast(old_ptr) - 8); 27 | #endif 28 | } 29 | else if ((std::uintptr_t(old_ptr) & 63) != 0) { 30 | // this is a bit mysterious. Our allocator only allocates buffers 31 | // aligned to 16 bytes + 8 (i.e. the address always ends with 8) 32 | // The only other kind of buffer there should be, are the in-place 33 | // buffers that are members of the mpz class. Those are specifically 34 | // made to be 64 byte aligned (the assumed cache line size). If we're 35 | // asked to free such buffer, we just ignore it, since it wasn't 36 | // heap allocated. however, this isn't aligned to 64 bytes, so it may 37 | // be a default allocated buffer. It's not supposed to happen, but if it 38 | // does, we better free it 39 | std::free(old_ptr); 40 | } 41 | } 42 | 43 | inline void* mp_realloc_func(void* old_ptr, size_t old_size, size_t new_bytes) { 44 | 45 | void* ret = mp_alloc_func(new_bytes); 46 | ::memcpy(ret, old_ptr, std::min(old_size, new_bytes)); 47 | mp_free_func(old_ptr, old_size); 48 | return ret; 49 | } 50 | 51 | //must call this before calling any gmp functions 52 | //(the mpz class constructor does not call any gmp functions) 53 | inline void init_gmp() { 54 | mp_set_memory_functions(mp_alloc_func, mp_realloc_func, mp_free_func); 55 | allow_integer_constructor=true; //make sure the old gmp allocator isn't used 56 | } 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/asm_base.h: -------------------------------------------------------------------------------- 1 | #ifdef GENERATE_ASM_TRACKING_DATA 2 | const bool generate_asm_tracking_data=true; 3 | #else 4 | const bool generate_asm_tracking_data=false; 5 | #endif 6 | 7 | namespace asm_code { 8 | 9 | 10 | string track_asm(string comment, string jump_to = "") { 11 | if (!generate_asm_tracking_data) { 12 | return jump_to; 13 | } 14 | 15 | mark_vdf_test(); 16 | 17 | static map id_map; 18 | static int next_id=1; 19 | 20 | int& id=id_map[comment]; 21 | if (id==0) { 22 | id=next_id; 23 | ++next_id; 24 | } 25 | 26 | assert(id>=1 && id<=num_asm_tracking_data); 27 | 28 | // 29 | // 30 | 31 | static bool init=false; 32 | if (!init) { 33 | APPEND_M(str( ".data" )); 34 | APPEND_M(str( ".balign 8" )); 35 | 36 | APPEND_M(str( "track_asm_rax: .quad 0" )); 37 | 38 | //APPEND_M(str( ".global asm_tracking_data" )); 39 | //APPEND_M(str( "asm_tracking_data:" )); 40 | //for (int x=0;x, string> constant_map; 103 | string& name=constant_map[make_pair(value_bits_0, value_bits_1)]; 104 | 105 | if (name.empty()) { 106 | name=m.alloc_label(); 107 | 108 | #ifdef CHIAOSX 109 | APPEND_M(str( ".text " )); 110 | #else 111 | APPEND_M(str( ".text 1" )); 112 | #endif 113 | APPEND_M(str( ".balign 16" )); 114 | APPEND_M(str( "#:", name )); 115 | APPEND_M(str( ".quad #", to_hex(value_bits_0) )); //lane 0 116 | APPEND_M(str( ".quad #", to_hex(value_bits_1) )); //lane 1 117 | APPEND_M(str( ".text" )); 118 | } 119 | #ifdef CHIAOSX 120 | return (use_brackets)? str( "[RIP+#]", name ) : name; 121 | #else 122 | return (use_brackets)? str( "[#]", name ) : name; 123 | #endif 124 | } 125 | 126 | string constant_address_double(double value_0, double value_1, bool use_brackets=true) { 127 | uint64 value_bits_0=*(uint64*)&value_0; 128 | uint64 value_bits_1=*(uint64*)&value_1; 129 | return constant_address_uint64(value_bits_0, value_bits_1, use_brackets); 130 | } 131 | 132 | string constant_address_avx512_uint64(array value, bool use_brackets=true) { 133 | static map, string> constant_map; 134 | string& name=constant_map[value]; 135 | 136 | if (name.empty()) { 137 | name=m.alloc_label(); 138 | 139 | #ifdef CHIAOSX 140 | APPEND_M(str( ".text " )); 141 | #else 142 | APPEND_M(str( ".text 1" )); 143 | #endif 144 | APPEND_M(str( ".balign 64" )); 145 | APPEND_M(str( "#:", name )); 146 | for (int x=0;x<8;++x) { 147 | APPEND_M(str( ".quad #", to_hex(value[x]) )); //lane x 148 | } 149 | APPEND_M(str( ".text" )); 150 | } 151 | #ifdef CHIAOSX 152 | return (use_brackets)? str( "ZMMWORD PTR [RIP+#]", name ) : name; 153 | #else 154 | return (use_brackets)? str( "ZMMWORD PTR [#]", name ) : name; 155 | #endif 156 | } 157 | 158 | string constant_address_avx512_uint64(uint64 value, bool use_brackets=true) { 159 | array value_array; 160 | for (int x=0;x<8;++x) { 161 | value_array[x]=value; 162 | } 163 | 164 | return constant_address_avx512_uint64(value_array); 165 | } 166 | 167 | 168 | } -------------------------------------------------------------------------------- /src/asm_vm.h: -------------------------------------------------------------------------------- 1 | namespace asm_code { 2 | 3 | 4 | string vpermq_mask(array lanes) { 5 | int res=0; 6 | for (int x=0;x<4;++x) { 7 | int lane=lanes[x]; 8 | assert(lane>=0 && lane<4); 9 | res|=lane << (2*x); 10 | } 11 | return to_hex(res); 12 | } 13 | 14 | string vpblendd_mask_4(array lanes) { 15 | int res=0; 16 | for (int x=0;x<4;++x) { 17 | int lane=lanes[x]; 18 | assert(lane>=0 && lane<2); 19 | res|=((lane==1)? 3 : 0) << (2*x); 20 | } 21 | return to_hex(res); 22 | } 23 | 24 | string vpblendd_mask_8(array lanes) { 25 | int res=0; 26 | for (int x=0;x<8;++x) { 27 | int lane=lanes[x]; 28 | assert(lane>=0 && lane<2); 29 | res|=((lane==1)? 1 : 0) << x; 30 | } 31 | return to_hex(res); 32 | } 33 | 34 | struct asm_function { 35 | string name; 36 | 37 | //this excludes the argument regs (if any). can add them after they are done being used 38 | reg_alloc regs; 39 | 40 | vector args; 41 | 42 | vector pop_regs; 43 | const vector all_save_regs={reg_rbp, reg_rbx, reg_r12, reg_r13, reg_r14, reg_r15}; 44 | const vector all_arg_regs={reg_rdi, reg_rsi, reg_rdx, reg_rcx, reg_r8, reg_r9}; 45 | const reg_scalar return_reg=reg_rax; 46 | 47 | bool d_align_stack=true; 48 | bool d_return_error_code=true; 49 | 50 | //the scratch area ends at RSP (i.e. the last byte is at address RSP-1) 51 | //RSP is 64-byte aligned 52 | //RSP must be preserved but all other registers can be changed 53 | // 54 | //the arguments are stored in: RDI, RSI, RDX, RCX, R8, R9 55 | //each argument is up to 8 bytes 56 | asm_function(string t_name, int num_args=0, int num_regs=15, bool align_stack=true, bool return_error_code=true) { 57 | EXPAND_MACROS_SCOPE; 58 | 59 | d_align_stack=align_stack; 60 | d_return_error_code=return_error_code; 61 | 62 | static bool outputted_header=false; 63 | if (!outputted_header) { 64 | APPEND_M(str( ".intel_syntax noprefix" )); 65 | outputted_header=true; 66 | } 67 | 68 | name=t_name; 69 | 70 | #ifdef CHIAOSX 71 | APPEND_M(str( ".global _asm_")+asmprefix+str("func_#", t_name )); 72 | APPEND_M(str( "_asm_")+asmprefix+str("func_#:", t_name )); 73 | #else 74 | APPEND_M(str( ".global asm_")+asmprefix+str("func_#", t_name )); 75 | APPEND_M(str( "asm_")+asmprefix+str("func_#:", t_name )); 76 | #endif 77 | 78 | assert(num_regs<=15); 79 | regs.init(); 80 | 81 | for (int x=0;xnum_available_regs) { 91 | APPEND_M(str( "PUSH #", s.name() )); 92 | pop_regs.push_back(s); 93 | ++num_available_regs; 94 | } else { 95 | regs.get_scalar(s); 96 | } 97 | } 98 | assert(num_available_regs==num_regs); 99 | 100 | if (align_stack) { 101 | // RSP'=RSP&(~63) ; this makes it 64-aligned and can only reduce its value 102 | // RSP''=RSP'-64 ; still 64-aligned but now there is at least 64 bytes of unused stuff 103 | // [RSP'']=RSP ; store old value in unused area 104 | APPEND_M(str( "MOV RAX, RSP" )); 105 | APPEND_M(str( "AND RSP, -64" )); //-64 equals ~63 106 | APPEND_M(str( "SUB RSP, 64" )); 107 | APPEND_M(str( "MOV [RSP], RAX" )); 108 | } 109 | } 110 | 111 | //the return value is the error code (0 if no error). it is put in RAX 112 | ~asm_function() { 113 | EXPAND_MACROS_SCOPE; 114 | 115 | if (d_return_error_code) { 116 | //default return value of 0 117 | APPEND_M(str( "MOV RAX, 0" )); 118 | } 119 | 120 | string end_label=m.alloc_label(); 121 | APPEND_M(str( "#:", end_label )); 122 | 123 | //this takes 4 cycles including ret, if there is nothing else to do 124 | if (d_align_stack) { 125 | APPEND_M(str( "MOV RSP, [RSP]" )); 126 | } 127 | for (int x=pop_regs.size()-1;x>=0;--x) { 128 | APPEND_M(str( "POP #", pop_regs[x].name() )); 129 | } 130 | APPEND_M(str( "RET" )); 131 | 132 | while (m.next_output_error_label_id 22 | 23 | #include 24 | 25 | template void do_benchmark(string name, int num_bits) { 26 | const int num_iterations_outer=1000; 27 | const int num_iterations_inner=1000; 28 | 29 | name += "_" + to_string(num_bits); 30 | 31 | integer a_int=rand_integer(num_bits); 32 | integer b_int=rand_integer(num_bits); 33 | 34 | intnx a; 35 | mpz_set(a._(), a_int.impl); 36 | 37 | intnx b; 38 | mpz_set(b._(), b_int.impl); 39 | 40 | intnx c; 41 | intjx d; 42 | 43 | avx512_intnx a_avx512; 44 | avx512_intnx b_avx512; 45 | avx512_intnx c_avx512; 46 | avx512_intjx d_avx512; 47 | 48 | #define DO_BENCH(x) for (int i=0;i void load_int(const char* num_bits, avx512_intnx& out) { 67 | int num_bits_int=from_string(num_bits); 68 | integer test_int=rand_integer((num_bits_int<0)? -num_bits_int : num_bits_int); 69 | //integer test_int=integer(1)<<((num_bits_int<0)? -num_bits_int : num_bits_int); 70 | if (num_bits_int<0) { 71 | test_int=-test_int; 72 | } 73 | intnx test; 74 | mpz_set(test._(), test_int.impl); 75 | out=test; 76 | } */ 77 | 78 | int main(int argc, char **argv) { 79 | assert(is_vdf_test); //assertions should be disabled in VDF_MODE==0 80 | init_gmp(); 81 | set_rounding_mode(); 82 | 83 | enable_avx512_ifma=false; 84 | 85 | if (argc==2 && string(argv[1]) == "avx512") { 86 | integer a_int=rand_integer(512); 87 | 88 | int1x a; 89 | mpz_set(a._(), a_int.impl); 90 | 91 | avx512_int1x a_avx512; 92 | 93 | while (1) { a_avx512=a; } 94 | } 95 | 96 | if (argc==2 && string(argv[1]) == "gmp") { 97 | while (1); 98 | } 99 | 100 | 101 | do_benchmark( "benchmark", 512 ); 102 | do_benchmark( "benchmark", 1024 ); 103 | 104 | TRACK_CYCLES_OUTPUT_STATS 105 | 106 | /*assert(argc>=3); 107 | 108 | avx512_int4x test_1_avx512; 109 | load_int(argv[1], test_1_avx512); 110 | 111 | avx512_int4x test_2_avx512; 112 | load_int(argv[2], test_2_avx512); 113 | 114 | avx512_int4x test_3_avx512; 115 | test_3_avx512.set_mul(test_1_avx512, test_2_avx512); 116 | 117 | for (int x=0;x v(test_3_avx512.data()[x]); 119 | cout << dec << x << ": " << hex << v << "\n"; 120 | }*/ 121 | } 122 | -------------------------------------------------------------------------------- /src/bit_manipulation.h: -------------------------------------------------------------------------------- 1 | #ifndef BIT_MANIPULATION_H 2 | #define BIT_MANIPULATION_H 3 | /*uint64 funnel_shift(uint64 low, uint64 high, int start, int size) { 4 | assert(start>=0 && size>0 && start+size<=128); 5 | 6 | uint128 v=(uint128(high)<<64) | uint128(low); 7 | v>>=start; 8 | v&=~(uint128(1)<=0 && start<64); 14 | assert(size>=0 && start+size<=64); 15 | 16 | t >>= start; 17 | t &= (1ull<=0 && start<64); 23 | assert(size>=0 && start+size<=64); 24 | assert( 25 | ( bits & ~((1ull<0 && size<64); 41 | assert( 42 | ( bits & ~((1ull<=0;--x) { 47 | bool v=bits&(1ull< 5 | 6 | #include 7 | #include 8 | 9 | 10 | struct qfb_c { 11 | mpz_t a; 12 | mpz_t t; 13 | mpz_t g; 14 | mpz_t b0; 15 | bool b_sign; 16 | }; 17 | 18 | #define BQFC_MAX_D_BITS 1024 19 | /* Force all forms to have the same size (100 bytes). */ 20 | #define BQFC_FORM_SIZE ((BQFC_MAX_D_BITS + 31) / 32 * 3 + 4) 21 | 22 | int bqfc_compr(struct qfb_c *out_c, mpz_t a, mpz_t b); 23 | 24 | int bqfc_decompr(mpz_t out_a, mpz_t out_b, const mpz_t D, const struct qfb_c *c); 25 | 26 | int bqfc_serialize_only(uint8_t *out_str, const struct qfb_c *c, size_t d_bits); 27 | int bqfc_deserialize_only(struct qfb_c *out_c, const uint8_t *str, size_t d_bits); 28 | 29 | int bqfc_serialize(uint8_t *out_str, mpz_t a, mpz_t b, size_t d_bits); 30 | int bqfc_deserialize(mpz_t out_a, mpz_t out_b, const mpz_t D, const uint8_t *str, size_t size, size_t d_bits); 31 | 32 | #endif // BQFC_H 33 | -------------------------------------------------------------------------------- /src/c_bindings/c_wrapper.cpp: -------------------------------------------------------------------------------- 1 | #include "c_wrapper.h" 2 | #include 3 | #include 4 | #include "../verifier.h" 5 | #include "../prover_slow.h" 6 | 7 | extern "C" { 8 | // C wrapper function 9 | bool create_discriminant_wrapper(const uint8_t* seed, size_t seed_size, size_t size_bits, uint8_t* result) { 10 | try { 11 | std::vector seed_vector(seed, seed + seed_size); 12 | integer discriminant = CreateDiscriminant(seed_vector, size_bits); 13 | mpz_export(result, NULL, 1, 1, 0, 0, discriminant.impl); 14 | return true; 15 | } catch (...) { 16 | return false; 17 | } 18 | } 19 | 20 | ByteArray prove_wrapper(const uint8_t* challenge_hash, size_t challenge_size, const uint8_t* x_s, size_t x_s_size, size_t discriminant_size_bits, uint64_t num_iterations) { 21 | try { 22 | std::vector challenge_hash_bytes(challenge_hash, challenge_hash + challenge_size); 23 | integer discriminant = CreateDiscriminant(challenge_hash_bytes, discriminant_size_bits); 24 | form x = DeserializeForm(discriminant, x_s, x_s_size); 25 | std::vector result = ProveSlow(discriminant, x, num_iterations, ""); 26 | 27 | // Allocate memory for the result and copy data 28 | uint8_t* resultData = new uint8_t[result.size()]; 29 | std::copy(result.begin(), result.end(), resultData); 30 | 31 | return ByteArray { resultData, result.size() }; 32 | } catch (...) { 33 | return ByteArray { nullptr, 0 }; 34 | } 35 | } 36 | 37 | bool verify_n_wesolowski_wrapper(const uint8_t* discriminant_bytes, size_t discriminant_size, const uint8_t* x_s, const uint8_t* proof_blob, size_t proof_blob_size, uint64_t num_iterations, uint64_t recursion) { 38 | try { 39 | integer discriminant; 40 | mpz_import(discriminant.impl, discriminant_size, 1, 1, 0, 0, discriminant_bytes); 41 | 42 | return CheckProofOfTimeNWesolowski( 43 | -discriminant, 44 | x_s, 45 | proof_blob, 46 | proof_blob_size, 47 | num_iterations, 48 | discriminant_size * 8, 49 | recursion 50 | ); 51 | } catch (...) { 52 | return false; 53 | } 54 | } 55 | 56 | void delete_byte_array(ByteArray array) { 57 | delete[] array.data; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/c_bindings/c_wrapper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #ifdef __cplusplus 6 | #include // for size_t 7 | #include // for uint8_t 8 | extern "C" { 9 | #endif 10 | 11 | bool create_discriminant_wrapper(const uint8_t* seed, size_t seed_size, size_t size_bits, uint8_t* result); 12 | 13 | // Define a struct to hold the byte array and its length 14 | typedef struct { 15 | uint8_t* data; 16 | size_t length; 17 | } ByteArray; 18 | ByteArray prove_wrapper(const uint8_t* challenge_hash, size_t challenge_size, const uint8_t* x_s, size_t x_s_size, size_t discriminant_size_bits, uint64_t num_iterations); 19 | 20 | bool verify_n_wesolowski_wrapper(const uint8_t* discriminant_bytes, size_t discriminant_size, const uint8_t* x_s, const uint8_t* proof_blob, size_t proof_blob_size, uint64_t num_iterations, uint64_t recursion); 21 | void delete_byte_array(ByteArray array); 22 | 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | -------------------------------------------------------------------------------- /src/c_bindings/readme.md: -------------------------------------------------------------------------------- 1 | # C Bindings 2 | 3 | C bindings here wrap a few of the C++ functions with purely c compatible types, so that other languages which require 4 | c bindings (golang for example) can create bindings to the C++ functions. 5 | -------------------------------------------------------------------------------- /src/cmake/FindGMP.cmake: -------------------------------------------------------------------------------- 1 | # Try to find the GMP library 2 | # https://gmplib.org/ 3 | # 4 | # This module supports requiring a minimum version, e.g. you can do 5 | # find_package(GMP 6.0.0) 6 | # to require version 6.0.0 to newer of GMP. 7 | # 8 | # Once done this will define 9 | # 10 | # GMP_FOUND - system has GMP lib with correct version 11 | # GMP_INCLUDES - the GMP include directory 12 | # GMP_LIBRARIES - the GMP library 13 | # GMP_VERSION - GMP version 14 | # 15 | # Copyright (c) 2016 Jack Poulson, 16 | # Redistribution and use is allowed according to the terms of the BSD license. 17 | 18 | find_path(GMP_INCLUDES NAMES gmp.h PATHS $ENV{GMPDIR} ${INCLUDE_INSTALL_DIR}) 19 | 20 | # Set GMP_FIND_VERSION to 5.1.0 if no minimum version is specified 21 | if(NOT GMP_FIND_VERSION) 22 | if(NOT GMP_FIND_VERSION_MAJOR) 23 | set(GMP_FIND_VERSION_MAJOR 5) 24 | endif() 25 | if(NOT GMP_FIND_VERSION_MINOR) 26 | set(GMP_FIND_VERSION_MINOR 1) 27 | endif() 28 | if(NOT GMP_FIND_VERSION_PATCH) 29 | set(GMP_FIND_VERSION_PATCH 0) 30 | endif() 31 | set(GMP_FIND_VERSION 32 | "${GMP_FIND_VERSION_MAJOR}.${GMP_FIND_VERSION_MINOR}.${GMP_FIND_VERSION_PATCH}") 33 | endif() 34 | 35 | message("GMP_INCLUDES=${GMP_INCLUDES}") 36 | if(GMP_INCLUDES) 37 | # Since the GMP version macros may be in a file included by gmp.h of the form 38 | # gmp-.*[_]?.*.h (e.g., gmp-x86_64.h), we search each of them. 39 | file(GLOB GMP_HEADERS "${GMP_INCLUDES}/gmp.h" "${GMP_INCLUDES}/gmp-*.h") 40 | foreach(gmp_header_filename ${GMP_HEADERS}) 41 | file(READ "${gmp_header_filename}" _gmp_version_header) 42 | string(REGEX MATCH 43 | "define[ \t]+__GNU_MP_VERSION[ \t]+([0-9]+)" _gmp_major_version_match 44 | "${_gmp_version_header}") 45 | if(_gmp_major_version_match) 46 | set(GMP_MAJOR_VERSION "${CMAKE_MATCH_1}") 47 | string(REGEX MATCH "define[ \t]+__GNU_MP_VERSION_MINOR[ \t]+([0-9]+)" 48 | _gmp_minor_version_match "${_gmp_version_header}") 49 | set(GMP_MINOR_VERSION "${CMAKE_MATCH_1}") 50 | string(REGEX MATCH "define[ \t]+__GNU_MP_VERSION_PATCHLEVEL[ \t]+([0-9]+)" 51 | _gmp_patchlevel_version_match "${_gmp_version_header}") 52 | set(GMP_PATCHLEVEL_VERSION "${CMAKE_MATCH_1}") 53 | set(GMP_VERSION 54 | ${GMP_MAJOR_VERSION}.${GMP_MINOR_VERSION}.${GMP_PATCHLEVEL_VERSION}) 55 | endif() 56 | endforeach() 57 | 58 | # Check whether found version exists and exceeds the minimum requirement 59 | if(NOT GMP_VERSION) 60 | set(GMP_VERSION_OK FALSE) 61 | message(STATUS "GMP version was not detected") 62 | elseif(${GMP_VERSION} VERSION_LESS ${GMP_FIND_VERSION}) 63 | set(GMP_VERSION_OK FALSE) 64 | message(STATUS "GMP version ${GMP_VERSION} found in ${GMP_INCLUDES}, " 65 | "but at least version ${GMP_FIND_VERSION} is required") 66 | else() 67 | set(GMP_VERSION_OK TRUE) 68 | endif() 69 | endif() 70 | 71 | find_library(GMP_LIBRARIES gmp PATHS $ENV{GMPDIR} ${LIB_INSTALL_DIR}) 72 | 73 | include(FindPackageHandleStandardArgs) 74 | find_package_handle_standard_args(GMP DEFAULT_MSG 75 | GMP_INCLUDES GMP_LIBRARIES GMP_VERSION_OK) 76 | mark_as_advanced(GMP_INCLUDES GMP_LIBRARIES) 77 | -------------------------------------------------------------------------------- /src/cmake/FindGMPXX.cmake: -------------------------------------------------------------------------------- 1 | # https://svn.zib.de/lenne3d/lib/qpl_cgal/3.5.1/cmake/modules/FindGMPXX.cmake 2 | # 3 | # Try to find the GMPXX libraries 4 | # GMPXX_FOUND - system has GMPXX lib 5 | # GMPXX_INCLUDE_DIR - the GMPXX include directory 6 | # GMPXX_LIBRARIES - Libraries needed to use GMPXX 7 | 8 | # TODO: support Windows and MacOSX 9 | 10 | # GMPXX needs GMP 11 | 12 | find_package( GMP QUIET ) 13 | 14 | if(GMP_FOUND) 15 | 16 | if (GMPXX_INCLUDE_DIR AND GMPXX_LIBRARIES) 17 | # Already in cache, be silent 18 | set(GMPXX_FIND_QUIETLY TRUE) 19 | endif() 20 | 21 | find_path(GMPXX_INCLUDE_DIR NAMES gmpxx.h 22 | PATHS ${GMP_INCLUDE_DIR_SEARCH} 23 | DOC "The directory containing the GMPXX include files" 24 | ) 25 | 26 | find_library(GMPXX_LIBRARIES NAMES gmpxx 27 | PATHS ${GMP_LIBRARIES_DIR_SEARCH} 28 | DOC "Path to the GMPXX library" 29 | ) 30 | 31 | 32 | 33 | find_package_handle_standard_args(GMPXX "DEFAULT_MSG" GMPXX_LIBRARIES GMPXX_INCLUDE_DIR ) 34 | 35 | endif() 36 | -------------------------------------------------------------------------------- /src/compile_asm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "include.h" 4 | 5 | bool use_divide_table=false; 6 | int gcd_base_bits=50; 7 | int gcd_128_max_iter=3; 8 | std::string asmprefix="cel_"; 9 | bool enable_all_instructions=false; 10 | 11 | #include "parameters.h" 12 | 13 | #define COMPILE_ASM 14 | 15 | #ifdef TEST_ASM 16 | #undef TEST_ASM 17 | #endif 18 | 19 | #include "bit_manipulation.h" 20 | #include "double_utility.h" 21 | #include "integer.h" 22 | 23 | #include "gpu_integer.h" 24 | #include "gpu_integer_divide.h" 25 | 26 | #include "gcd_base_continued_fractions.h" 27 | #include "gcd_base_divide_table.h" 28 | #include "gcd_128.h" 29 | #include "gcd_unsigned.h" 30 | 31 | #include "asm_types.h" 32 | #include "asm_vm.h" 33 | 34 | #include "asm_base.h" 35 | #include "asm_gcd_base_continued_fractions.h" 36 | #include "asm_gcd_base_divide_table.h" 37 | #include "asm_gcd_128.h" 38 | #include "asm_gcd_unsigned.h" 39 | #include "asm_avx512_ifma.h" 40 | 41 | #include "asm_main.h" 42 | 43 | int main(int argc, char** argv) { 44 | set_rounding_mode(); 45 | 46 | string filename="asm_compiled.s"; 47 | 48 | bool compile_avx512=false; 49 | 50 | if((argc==2)&&(strcmp(argv[1],"avx2")==0)) 51 | { 52 | use_divide_table=true; 53 | gcd_base_bits=63; 54 | gcd_128_max_iter=2; 55 | asmprefix="avx2_"; 56 | enable_all_instructions=true; 57 | filename="avx2_asm_compiled.s"; 58 | } 59 | 60 | if((argc==2)&&(strcmp(argv[1],"avx512")==0)) 61 | { 62 | enable_all_instructions=true; 63 | asmprefix="avx512_"; 64 | filename="avx512_asm_compiled.s"; 65 | compile_avx512=true; 66 | } 67 | 68 | if (compile_avx512) { 69 | asm_code::compile_asm_avx512(filename); 70 | } else { 71 | asm_code::compile_asm(filename); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/create_discriminant.h: -------------------------------------------------------------------------------- 1 | #ifndef CREATE_DISCRIMINANT_H 2 | #define CREATE_DISCRIMINANT_H 3 | 4 | #include "proof_common.h" 5 | 6 | integer CreateDiscriminant(std::vector& seed, int length = 1024) { 7 | return HashPrime(seed, length, {0, 1, 2, length - 1}) * integer(-1); 8 | } 9 | 10 | #endif // CREATE_DISCRIMINANT_H 11 | -------------------------------------------------------------------------------- /src/double_utility.h: -------------------------------------------------------------------------------- 1 | #ifndef DOUBLE_UTILITY_H 2 | #define DOUBLE_UTILITY_H 3 | 4 | struct double_bits { 5 | static const int exponent_num_bits=11; 6 | static const int fraction_num_bits=52; 7 | 8 | bool sign=false; 9 | int exponent=0; //11 bits; starting value is -1023 10 | uint64 fraction=0; //52 bits 11 | 12 | double_bits() {} 13 | double_bits(double v) { 14 | uint64 v_bits=*(uint64*)(&v); 15 | sign=extract_bits(v_bits, 63, 1); 16 | exponent=extract_bits(v_bits, 52, 11); 17 | fraction=extract_bits(v_bits, 0, 52); 18 | } 19 | 20 | void set_exponent(int v) { 21 | exponent=v+1023; 22 | } 23 | 24 | uint64 to_uint64() const { 25 | uint64 v_bits=0; 26 | v_bits=insert_bits(v_bits, sign, 63, 1); 27 | v_bits=insert_bits(v_bits, exponent, 52, 11); 28 | v_bits=insert_bits(v_bits, fraction, 0, 52); 29 | return v_bits; 30 | } 31 | 32 | double to_double() const { 33 | uint64 v_bits=to_uint64(); 34 | return *((double*)&v_bits); 35 | } 36 | 37 | void output(ostream& out, bool decimal=false) const { 38 | out << (sign? "-" : "+"); 39 | 40 | if (exponent==0 && fraction==0) { 41 | out << "0"; 42 | } 43 | 44 | if (exponent==0b11111111111) { 45 | out << ((fraction==0)? "INF" : "NAN"); 46 | } 47 | 48 | if (decimal) { 49 | uint64 v=fraction | (1ull<<52); 50 | out << v << "*2^" << exponent-1023-52; 51 | } else { 52 | out << ((exponent==0)? "0b0" : "0b1"); 53 | output_bits(out, fraction, 52); 54 | out << "*2^" << exponent-1023-52; 55 | } 56 | } 57 | }; 58 | 59 | void set_rounding_mode() { 60 | assert(fesetround(FE_TOWARDZERO)==0); //truncated rounding 61 | } 62 | 63 | double d_exp2(int i) { 64 | double_bits d; 65 | d.sign=false; 66 | d.set_exponent(i); //bit shift and integer add (either order) 67 | return d.to_double(); 68 | } 69 | 70 | //the cpu has to handle values of i that are above 2^52-1 so the built in instruction is slower than doing it this way 71 | //can make this add a shift easily 72 | double double_from_int(uint64 i) { 73 | assert(i<(1ull<<52)); 74 | 75 | //b>=1 && b<2 76 | double_bits b; 77 | b.set_exponent(0); 78 | b.fraction=i; 79 | 80 | //res_1>=0 && res_1<1 81 | double res_1=b.to_double(); //1 bitwise or (for the exponent) 82 | 83 | double res=fma(res_1, d_exp2(52), -d_exp2(52)); 84 | 85 | //double_bits res_b=res_1-1; 86 | //res_b.exponent+=52; //can't overflow; 1 uint64 add without shifts. can also use a 32/16 bit add or a double multiply 87 | //double res=res_b.to_double(); 88 | 89 | assert(res==i); 90 | return res; 91 | } 92 | 93 | //can make this handle shifted doubles easily 94 | uint64 int_from_double(double v, bool exact=true) { 95 | if (exact) { 96 | uint64 v_test=v; 97 | assert(v_test==v); 98 | assert(v_test<(1ull<<52)); 99 | } 100 | 101 | double res_1=fma(v, d_exp2(-52), 1); //one fma 102 | 103 | double_bits b(res_1); 104 | uint64 res=b.fraction; //1 bitwise and (for exponent) 105 | 106 | if (exact) { 107 | assert(res==v); 108 | } 109 | return res; 110 | } 111 | 112 | uint64 make_uint64(uint32 high, uint32 low) { 113 | return uint64(high)<<32 | uint32(low); 114 | } 115 | 116 | uint128 make_uint128(uint64 high, uint64 low) { 117 | return uint128(high)<<64 | uint128(low); 118 | } 119 | 120 | // end Headerguard DOUBLE_UTILITY_H 121 | #endif 122 | -------------------------------------------------------------------------------- /src/fast_storage.h: -------------------------------------------------------------------------------- 1 | 2 | // If 'FAST_MACHINE' is set to 1, the machine needs to have a high number 3 | // of CPUs. This will optimize the runtime, 4 | // by not storing any intermediate values in main VDF worker loop. 5 | // Other threads will come back and redo the work, this 6 | // time storing the intermediates as well. 7 | // For machines with small numbers of CPU, setting this to 1 will slow 8 | // down everything, possible even stall. 9 | #ifndef FAST_STORAGE_H 10 | #define FAST_STORAGE_H 11 | 12 | #include "vdf_new.h" 13 | 14 | extern bool new_event; 15 | extern std::mutex new_event_mutex; 16 | extern std::condition_variable new_event_cv; 17 | 18 | class FastStorage { 19 | public: 20 | FastStorage(FastAlgorithmCallback* weso) { 21 | stopped = false; 22 | this->weso = weso; 23 | intermediates_stored = new bool[(1 << 19)]; 24 | for (int i = 0; i < (1 << 19); i++) 25 | intermediates_stored[i] = 0; 26 | 27 | for (int i = 0; i < intermediates_threads; i++) { 28 | storage_threads.push_back(std::thread([=] {CalculateIntermediatesThread(); })); 29 | } 30 | } 31 | 32 | ~FastStorage() { 33 | { 34 | std::lock_guard lk(intermediates_mutex); 35 | stopped = true; 36 | } 37 | intermediates_cv.notify_all(); 38 | for (int i = 0; i < storage_threads.size(); i++) { 39 | storage_threads[i].join(); 40 | } 41 | delete[] intermediates_stored; 42 | std::cout << "Fast storage fully stopped.\n" << std::flush; 43 | } 44 | 45 | void AddIntermediates(uint64_t iter) { 46 | int bucket = iter / (1 << 16); 47 | int subbucket = 0; 48 | if (iter % (1 << 16)) 49 | subbucket = 1; 50 | bool arrived_segment = false; 51 | bool has_event = false; 52 | { 53 | intermediates_stored[2 * bucket + subbucket] = true; 54 | if (intermediates_stored[2 * bucket] == true && 55 | intermediates_stored[2 * bucket + 1] == true) 56 | has_event = true; 57 | } 58 | if (has_event) { 59 | { 60 | std::lock_guard lk(new_event_mutex); 61 | new_event = true; 62 | } 63 | new_event_cv.notify_all(); 64 | } 65 | } 66 | 67 | void CalculateIntermediatesInner(form y, uint64_t iter_begin) { 68 | PulmarkReducer reducer; 69 | integer& D = weso->D; 70 | integer& L = weso->L; 71 | int segments = weso->segments; 72 | for (uint64_t iteration = iter_begin; iteration < iter_begin + (1 << 15); iteration++) { 73 | for (int i = 0; i < segments; i++) { 74 | uint64_t power_2 = 1LL << (16 + 2 * i); 75 | int kl = (i == 0) ? 10 : (12 * (power_2 >> 18)); 76 | if ((iteration % power_2) % kl == 0) { 77 | if (stopped) return; 78 | form* mulf = weso->GetForm(iteration, i); 79 | weso->SetForm(NL_FORM, &y, mulf, /*reduced=*/false); 80 | } 81 | } 82 | nudupl_form(y, y, D, L); 83 | reducer.reduce(y); 84 | } 85 | AddIntermediates(iter_begin); 86 | } 87 | 88 | void SubmitCheckpoint(form y_ret, uint64_t iteration) { 89 | { 90 | std::lock_guard lk(intermediates_mutex); 91 | pending_intermediates[iteration] = y_ret; 92 | } 93 | intermediates_cv.notify_all(); 94 | } 95 | 96 | uint64_t GetFinishedSegment() { 97 | while (intermediates_stored[2 * (intermediates_iter / (1 << 16))] == true && 98 | intermediates_stored[2 * (intermediates_iter / (1 << 16)) + 1] == true) { 99 | intermediates_iter += (1 << 16); 100 | } 101 | return intermediates_iter; 102 | } 103 | 104 | void CalculateIntermediatesThread() { 105 | while (!stopped) { 106 | { 107 | std::unique_lock lk(intermediates_mutex); 108 | intermediates_cv.wait(lk, [&] { 109 | return (!pending_intermediates.empty() || stopped); 110 | }); 111 | if (!stopped) { 112 | uint64_t iter_begin = (*pending_intermediates.begin()).first; 113 | form y = (*pending_intermediates.begin()).second; 114 | pending_intermediates.erase(pending_intermediates.begin()); 115 | lk.unlock(); 116 | CalculateIntermediatesInner(y, iter_begin); 117 | } 118 | } 119 | } 120 | } 121 | 122 | private: 123 | std::vector storage_threads; 124 | FastAlgorithmCallback* weso; 125 | bool* intermediates_stored; 126 | bool stopped; 127 | std::map pending_intermediates; 128 | int intermediates_threads = 6; 129 | std::mutex intermediates_mutex; 130 | std::condition_variable intermediates_cv; 131 | uint64_t intermediates_iter = 0; 132 | }; 133 | 134 | #endif // FAST_STORAGE_H 135 | -------------------------------------------------------------------------------- /src/generic_macros.h: -------------------------------------------------------------------------------- 1 | #ifndef ILYA_SHARED_HEADER_GENERIC_MACROS 2 | #define ILYA_SHARED_HEADER_GENERIC_MACROS 3 | 4 | /* 5 | #define main(...) \ 6 | main_inner(int argc, char** argv); \ 7 | int main(int argc, char** argv) { \ 8 | try {\ 9 | return main_inner(argc, argv);\ 10 | } catch(const std::exception& e) {\ 11 | std::cerr << "\n\nUncaught exception: " << e.what() << "\n";\ 12 | char *f=0; *f=1;\ 13 | } catch(const std::string& e) {\ 14 | std::cerr << "\n\nUncaught exception: " << e << "\n";\ 15 | char *f=0; *f=1;\ 16 | } catch(const char* e) {\ 17 | std::cerr << "\n\nUncaught exception: " << e << "\n";\ 18 | char *f=0; *f=1;\ 19 | } catch(...) {\ 20 | std::cerr << "\n\nUncaught exception.\n";\ 21 | char *f=0; *f=1;\ 22 | }\ 23 | }\ 24 | int main_inner(int argc, char** argv) 25 | 26 | #ifndef NO_GENERIC_H_ASSERT 27 | #ifdef assert 28 | #undef assert 29 | #endif 30 | #define assert(v) if (!(v)) { std::cerr << "\n\nAssertion failed: " << __FILE__ << " : " << __LINE__ << "\n"; char* shared_generic_assert_char_123=nullptr; *shared_generic_assert_char_123=1; throw 0; } (void)0 31 | #endif 32 | */ 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/gpu_integer_gcd.h: -------------------------------------------------------------------------------- 1 | template struct fixed_gcd_res { 2 | fixed_integer gcd; //unsigned; final value of a 3 | fixed_integer gcd_2; //unsigned; final value of b. this is 0 for a normal gcd 4 | fixed_integer s; //signed 5 | fixed_integer t; //signed 6 | fixed_integer s_2; //signed 7 | fixed_integer t_2; //signed 8 | }; 9 | 10 | //threshold is 0 to calculate the normal gcd 11 | //this calculates either s (u) or t (v) 12 | template fixed_gcd_res gcd( 13 | fixed_integer a_signed, fixed_integer b_signed, fixed_integer threshold, 14 | bool calculate_u 15 | ) { 16 | assert(!threshold.is_negative()); 17 | 18 | bool a_negative=a_signed.is_negative(); 19 | bool b_negative=b_signed.is_negative(); 20 | assert(!b_negative); 21 | 22 | array, 2> ab; //unsigned 23 | ab[0]=a_signed; 24 | ab[0].set_negative(false); 25 | 26 | ab[1]=b_signed; 27 | ab[1].set_negative(false); 28 | 29 | array, 2> uv; //unsigned 30 | 31 | int parity; 32 | 33 | if (ab[0] res; 71 | res.gcd=ab[0]; 72 | res.gcd_2=ab[1]; 73 | 74 | //if a was negative, negate the parity 75 | //if the parity is -1, negate the parity and negate the result u/v values. the parity is now 1 76 | //for u, u0 is positive and u1 is negative 77 | //for v, v0 is negative and u1 is positive 78 | if (calculate_u) { 79 | res.s=uv[0]; 80 | res.s.set_negative(a_negative != (parity==-1)); 81 | 82 | res.s_2=uv[1]; 83 | res.s_2.set_negative(a_negative != (parity==1)); 84 | } else { 85 | res.t=uv[0]; 86 | res.t.set_negative(a_negative != (parity==1)); 87 | 88 | res.t_2=uv[1]; 89 | res.t_2.set_negative(a_negative != (parity==-1)); 90 | } 91 | 92 | if (threshold.is_zero()) { 93 | auto expected_gcd_res=gcd(integer(a_signed), integer(b_signed)); 94 | assert(expected_gcd_res.gcd==integer(res.gcd)); 95 | 96 | if (calculate_u) { 97 | assert(expected_gcd_res.s==integer(res.s)); 98 | } else { 99 | assert(expected_gcd_res.t==integer(res.t)); 100 | } 101 | } else { 102 | //integer a_copy(a_signed); 103 | //integer b_copy(a_signed); 104 | //integer u_copy; 105 | //integer v_copy; 106 | //xgcd_partial(u_copy, v_copy, a_copy, b_copy, integer(threshold)); 107 | 108 | //assert(a_copy==res.gcd); 109 | //assert(b_copy==res.gcd_2); 110 | 111 | //if (calculate_t) { 112 | //assert(u_copy==-res.t); 113 | //assert(v_copy==-res.t_2); 114 | //} 115 | } 116 | 117 | return res; 118 | } 119 | -------------------------------------------------------------------------------- /src/hw/chia_driver.cpp: -------------------------------------------------------------------------------- 1 | #include "chia_driver.hpp" 2 | 3 | void ChiaDriver::SerializeJob(uint8_t *buf, 4 | uint32_t job_id, 5 | uint64_t iteration_count, 6 | mpz_t a, 7 | mpz_t f, 8 | mpz_t d, 9 | mpz_t l) { 10 | size_t offset = 0; 11 | const size_t reg_size = REG_BYTES; 12 | offset += write_bytes(reg_size, offset, buf, job_id); 13 | offset += write_bytes(2 * reg_size, offset, buf, iteration_count); 14 | offset += write_bytes(reg_size * CHIA_VDF_CMD_A_MULTIREG_COUNT, 15 | offset, buf, a, NUM_2X_COEFFS); 16 | offset += write_bytes(reg_size * CHIA_VDF_CMD_F_MULTIREG_COUNT, 17 | offset, buf, f, NUM_2X_COEFFS); 18 | offset += write_bytes(reg_size * CHIA_VDF_CMD_D_MULTIREG_COUNT, 19 | offset, buf, d, NUM_4X_COEFFS); 20 | offset += write_bytes(reg_size * CHIA_VDF_CMD_L_MULTIREG_COUNT, 21 | offset, buf, l, NUM_1X_COEFFS); 22 | // The last word is 0x1 to start the job 23 | offset += write_bytes(reg_size, offset, buf, (uint32_t)0x1); 24 | } 25 | 26 | void ChiaDriver::DeserializeJob(uint8_t *buf, 27 | uint32_t &job_id, 28 | uint64_t &iteration_count, 29 | mpz_t a, 30 | mpz_t f) { 31 | size_t offset = 0; 32 | const size_t reg_size = REG_BYTES; 33 | offset += read_bytes(reg_size, offset, buf, job_id); 34 | offset += read_bytes(2 * reg_size, offset, buf, iteration_count); 35 | offset += read_bytes(reg_size * CHIA_VDF_STATUS_A_MULTIREG_COUNT, 36 | offset, buf, a, NUM_2X_COEFFS); 37 | offset += read_bytes(reg_size * CHIA_VDF_STATUS_F_MULTIREG_COUNT, 38 | offset, buf, f, NUM_2X_COEFFS); 39 | } 40 | -------------------------------------------------------------------------------- /src/hw/chia_driver.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CHIA_DRIVER_HPP 2 | #define CHIA_DRIVER_HPP 3 | 4 | #include 5 | 6 | #include "vdf_driver.hpp" 7 | #include "chia_registers.hpp" 8 | 9 | class ChiaDriver : public VdfDriver { 10 | public: 11 | const static unsigned NUM_1X_COEFFS = 18; 12 | const static unsigned NUM_2X_COEFFS = 34; 13 | const static unsigned NUM_3X_COEFFS = 52; 14 | const static unsigned NUM_4X_COEFFS = 68; 15 | 16 | ChiaDriver() : 17 | VdfDriver(16 /*WORD_BITS*/, 19 /*REDUNDANT_BITS*/, true) { 18 | } 19 | 20 | virtual size_t CmdSize() { 21 | return (CHIA_VDF_CMD_START_REG_OFFSET - 22 | CHIA_VDF_CMD_JOB_ID_REG_OFFSET + REG_BYTES); 23 | } 24 | 25 | virtual size_t StatusSize() { 26 | return (CHIA_VDF_STATUS_END_REG_OFFSET - 27 | CHIA_VDF_STATUS_ITER_0_REG_OFFSET); 28 | } 29 | 30 | void SerializeJob(uint8_t *buf, 31 | uint32_t job_id, 32 | uint64_t iteration_count, 33 | mpz_t a, 34 | mpz_t f, 35 | mpz_t d, 36 | mpz_t l); 37 | 38 | void DeserializeJob(uint8_t *buf, 39 | uint32_t &job_id, 40 | uint64_t &iteration_count, 41 | mpz_t a, 42 | mpz_t f); 43 | }; 44 | #endif 45 | -------------------------------------------------------------------------------- /src/hw/clock.hpp: -------------------------------------------------------------------------------- 1 | // Generated register defines for clock 2 | #ifndef _CLOCK_REG_DEFS_ 3 | #define _CLOCK_REG_DEFS_ 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | // Register width 9 | #define CLOCK_PARAM_REG_WIDTH 32 10 | 11 | // PLL control register 12 | #define CLOCK_CONTROL_REG_OFFSET 0x0 13 | #define CLOCK_CONTROL_REG_RESVAL 0x3 14 | #define CLOCK_CONTROL_RESET_BIT 0 15 | #define CLOCK_CONTROL_BYPASS_BIT 1 16 | #define CLOCK_CONTROL_NEWDIV_BIT 2 17 | #define CLOCK_CONTROL_USEREF_BIT 3 18 | 19 | // PLL pre-divider register 20 | #define CLOCK_PRE_DIVIDE_REG_OFFSET 0x4 21 | #define CLOCK_PRE_DIVIDE_REG_RESVAL 0x0 22 | #define CLOCK_PRE_DIVIDE_DIVR_MASK 0x3f 23 | #define CLOCK_PRE_DIVIDE_DIVR_OFFSET 0 24 | #define CLOCK_PRE_DIVIDE_DIVR_FIELD \ 25 | ((bitfield_field32_t) { .mask = CLOCK_PRE_DIVIDE_DIVR_MASK, .index = CLOCK_PRE_DIVIDE_DIVR_OFFSET }) 26 | 27 | // PLL feedback integer divider register 28 | #define CLOCK_FB_DIVIDE_INTEGER_REG_OFFSET 0x8 29 | #define CLOCK_FB_DIVIDE_INTEGER_REG_RESVAL 0x0 30 | #define CLOCK_FB_DIVIDE_INTEGER_DIVFI_MASK 0x1ff 31 | #define CLOCK_FB_DIVIDE_INTEGER_DIVFI_OFFSET 0 32 | #define CLOCK_FB_DIVIDE_INTEGER_DIVFI_FIELD \ 33 | ((bitfield_field32_t) { .mask = CLOCK_FB_DIVIDE_INTEGER_DIVFI_MASK, .index = CLOCK_FB_DIVIDE_INTEGER_DIVFI_OFFSET }) 34 | 35 | // PLL feedback factional divider register 36 | #define CLOCK_FB_DIVIDE_FRACTION_REG_OFFSET 0xc 37 | #define CLOCK_FB_DIVIDE_FRACTION_REG_RESVAL 0x0 38 | #define CLOCK_FB_DIVIDE_FRACTION_DIVFF_MASK 0xffffff 39 | #define CLOCK_FB_DIVIDE_FRACTION_DIVFF_OFFSET 0 40 | #define CLOCK_FB_DIVIDE_FRACTION_DIVFF_FIELD \ 41 | ((bitfield_field32_t) { .mask = CLOCK_FB_DIVIDE_FRACTION_DIVFF_MASK, .index = CLOCK_FB_DIVIDE_FRACTION_DIVFF_OFFSET }) 42 | 43 | // PLL post-divider register 44 | #define CLOCK_POST_DIVIDE_REG_OFFSET 0x10 45 | #define CLOCK_POST_DIVIDE_REG_RESVAL 0x0 46 | #define CLOCK_POST_DIVIDE_DIVQ_MASK 0x1f 47 | #define CLOCK_POST_DIVIDE_DIVQ_OFFSET 0 48 | #define CLOCK_POST_DIVIDE_DIVQ_FIELD \ 49 | ((bitfield_field32_t) { .mask = CLOCK_POST_DIVIDE_DIVQ_MASK, .index = CLOCK_POST_DIVIDE_DIVQ_OFFSET }) 50 | 51 | // PLL filter range selection 52 | #define CLOCK_FILTER_RANGE_REG_OFFSET 0x14 53 | #define CLOCK_FILTER_RANGE_REG_RESVAL 0x0 54 | #define CLOCK_FILTER_RANGE_RANGE_MASK 0x7 55 | #define CLOCK_FILTER_RANGE_RANGE_OFFSET 0 56 | #define CLOCK_FILTER_RANGE_RANGE_FIELD \ 57 | ((bitfield_field32_t) { .mask = CLOCK_FILTER_RANGE_RANGE_MASK, .index = CLOCK_FILTER_RANGE_RANGE_OFFSET }) 58 | 59 | // PLL spread spectrum controls 60 | #define CLOCK_SPREAD_SPECTRUM_REG_OFFSET 0x18 61 | #define CLOCK_SPREAD_SPECTRUM_REG_RESVAL 0x0 62 | #define CLOCK_SPREAD_SPECTRUM_ENABLE_BIT 0 63 | #define CLOCK_SPREAD_SPECTRUM_DOWN_SPREAD_BIT 1 64 | #define CLOCK_SPREAD_SPECTRUM_MODULATION_DEPTH_MASK 0x7 65 | #define CLOCK_SPREAD_SPECTRUM_MODULATION_DEPTH_OFFSET 4 66 | #define CLOCK_SPREAD_SPECTRUM_MODULATION_DEPTH_FIELD \ 67 | ((bitfield_field32_t) { .mask = CLOCK_SPREAD_SPECTRUM_MODULATION_DEPTH_MASK, .index = CLOCK_SPREAD_SPECTRUM_MODULATION_DEPTH_OFFSET }) 68 | #define CLOCK_SPREAD_SPECTRUM_MODULATION_FREQUENCY_MASK 0xf 69 | #define CLOCK_SPREAD_SPECTRUM_MODULATION_FREQUENCY_OFFSET 8 70 | #define CLOCK_SPREAD_SPECTRUM_MODULATION_FREQUENCY_FIELD \ 71 | ((bitfield_field32_t) { .mask = CLOCK_SPREAD_SPECTRUM_MODULATION_FREQUENCY_MASK, .index = CLOCK_SPREAD_SPECTRUM_MODULATION_FREQUENCY_OFFSET }) 72 | 73 | // PLL status register 74 | #define CLOCK_STATUS_REG_OFFSET 0x1c 75 | #define CLOCK_STATUS_REG_RESVAL 0x0 76 | #define CLOCK_STATUS_LOCK_BIT 0 77 | #define CLOCK_STATUS_DIVACK_BIT 1 78 | 79 | // Mark the end of the address space 80 | #define CLOCK_END_OF_RANGE_REG_OFFSET 0xfffc 81 | #define CLOCK_END_OF_RANGE_REG_RESVAL 0x0 82 | #define CLOCK_END_OF_RANGE_MARKER_BIT 0 83 | 84 | #ifdef __cplusplus 85 | } // extern "C" 86 | #endif 87 | #endif // _CLOCK_REG_DEFS_ 88 | // End generated register defines for clock 89 | -------------------------------------------------------------------------------- /src/hw/emu_funcs.cpp: -------------------------------------------------------------------------------- 1 | #include "emu_runner.hpp" 2 | #include "libft4222.h" 3 | 4 | //#include "verifier.h" 5 | //#include "bit_manipulation.h" 6 | //#include "alloc.hpp" 7 | //#include "double_utility.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | int chia_vdf_is_emu = 1; 14 | 15 | #define EMU_LOC_ID 0x88888 16 | #define SPI_FT_BASE 0x100 17 | #define GPIO_FT_BASE 0x200 18 | 19 | FTD2XX_API FT_STATUS WINAPI FT_CreateDeviceInfoList(LPDWORD lpdwNumDevs) 20 | { 21 | *lpdwNumDevs = 1; 22 | return 0; 23 | } 24 | 25 | FTD2XX_API FT_STATUS WINAPI FT_GetDeviceInfoList( 26 | FT_DEVICE_LIST_INFO_NODE *pDest, LPDWORD lpdwNumDevs) 27 | { 28 | *lpdwNumDevs = 1; 29 | 30 | pDest->Type = FT_DEVICE_4222H_0; 31 | strcpy(pDest->Description, "A"); 32 | pDest->LocId = EMU_LOC_ID; 33 | 34 | return 0; 35 | } 36 | 37 | FTD2XX_API FT_STATUS WINAPI FT_OpenEx(PVOID pArg1, DWORD Flags, FT_HANDLE *pHandle) 38 | { 39 | if ((uintptr_t)pArg1 == EMU_LOC_ID) { 40 | *pHandle = (void *)SPI_FT_BASE; 41 | } else if ((uintptr_t)pArg1 == EMU_LOC_ID + 3) { 42 | *pHandle = (void *)GPIO_FT_BASE; 43 | } else { 44 | return FT4222_DEVICE_NOT_FOUND; 45 | } 46 | 47 | return 0; 48 | } 49 | 50 | LIBFT4222_API FT4222_STATUS FT4222_SPIMaster_Init(FT_HANDLE ftHandle, FT4222_SPIMode ioLine, FT4222_SPIClock clock, FT4222_SPICPOL cpol, FT4222_SPICPHA cpha, uint8 ssoMap) 51 | { 52 | return FT4222_OK; 53 | } 54 | 55 | LIBFT4222_API FT4222_STATUS FT4222_SPIMaster_SetCS(FT_HANDLE ftHandle, SPI_ChipSelect cs) 56 | { 57 | return FT4222_OK; 58 | } 59 | 60 | LIBFT4222_API FT4222_STATUS FT4222_SetClock(FT_HANDLE ftHandle, FT4222_ClockRate clk) 61 | { 62 | return FT4222_OK; 63 | } 64 | 65 | FTD2XX_API FT_STATUS WINAPI FT_SetUSBParameters(FT_HANDLE ftHandle, ULONG ulInTransferSize, ULONG ulOutTransferSize) 66 | { 67 | return 0; 68 | } 69 | 70 | LIBFT4222_API FT4222_STATUS FT4222_SPI_SetDrivingStrength(FT_HANDLE ftHandle, SPI_DrivingStrength clkStrength, SPI_DrivingStrength ioStrength, SPI_DrivingStrength ssoStrength) 71 | { 72 | return FT4222_OK; 73 | } 74 | 75 | LIBFT4222_API FT4222_STATUS FT4222_GPIO_Init(FT_HANDLE ftHandle, GPIO_Dir gpioDir[4]) 76 | { 77 | return FT4222_OK; 78 | } 79 | 80 | LIBFT4222_API FT4222_STATUS FT4222_SetSuspendOut(FT_HANDLE ftHandle, BOOL enable) 81 | { 82 | return FT4222_OK; 83 | } 84 | 85 | LIBFT4222_API FT4222_STATUS FT4222_SetWakeUpInterrupt(FT_HANDLE ftHandle, BOOL enable) 86 | { 87 | return FT4222_OK; 88 | } 89 | 90 | LIBFT4222_API FT4222_STATUS FT4222_UnInitialize(FT_HANDLE ftHandle) 91 | { 92 | return FT4222_OK; 93 | } 94 | 95 | FTD2XX_API FT_STATUS WINAPI FT_Close(FT_HANDLE ftHandle) 96 | { 97 | return 0; 98 | } 99 | 100 | LIBFT4222_API FT4222_STATUS FT4222_SPIMaster_MultiReadWrite(FT_HANDLE ftHandle, uint8* readBuffer, uint8* writeBuffer, uint8 singleWriteBytes, uint16 multiWriteBytes, uint16 multiReadBytes, uint32* sizeOfRead) 101 | { 102 | int ret = emu_do_io(writeBuffer, multiWriteBytes, readBuffer, multiReadBytes); 103 | if (!ret) { 104 | *sizeOfRead = multiReadBytes; 105 | return FT4222_OK; 106 | } else { 107 | return FT4222_IO_ERROR; 108 | } 109 | } 110 | 111 | LIBFT4222_API FT4222_STATUS FT4222_GPIO_Write(FT_HANDLE ftHandle, GPIO_Port portNum, BOOL bValue) 112 | { 113 | return FT4222_OK; 114 | } 115 | 116 | LIBFT4222_API FT4222_STATUS FT4222_GPIO_Read(FT_HANDLE ftHandle, GPIO_Port portNum, BOOL* value) 117 | { 118 | return FT4222_OK; 119 | } 120 | 121 | LIBFT4222_API FT4222_STATUS FT4222_I2CMaster_Init(FT_HANDLE ftHandle, uint32 kbps) 122 | { 123 | return FT4222_OK; 124 | } 125 | 126 | LIBFT4222_API FT4222_STATUS FT4222_I2CMaster_ReadEx(FT_HANDLE ftHandle, uint16 deviceAddress, uint8 flag, uint8* buffer, uint16 bufferSize, uint16* sizeTransferred) 127 | { 128 | int ret = emu_do_io_i2c(buffer, bufferSize, deviceAddress, 1); 129 | if (!ret) { 130 | *sizeTransferred = bufferSize; 131 | return FT4222_OK; 132 | } else { 133 | return FT4222_IO_ERROR; 134 | } 135 | } 136 | 137 | LIBFT4222_API FT4222_STATUS FT4222_I2CMaster_WriteEx(FT_HANDLE ftHandle, uint16 deviceAddress, uint8 flag, uint8* buffer, uint16 bufferSize, uint16* sizeTransferred) 138 | { 139 | int ret = emu_do_io_i2c(buffer, bufferSize, deviceAddress, 0); 140 | if (!ret) { 141 | *sizeTransferred = bufferSize; 142 | return FT4222_OK; 143 | } else { 144 | return FT4222_IO_ERROR; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/hw/emu_runner.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int emu_do_io(uint8_t *buf_in, uint16_t size_in, uint8_t *buf_out, uint16_t size_out); 4 | int emu_do_io_i2c(uint8_t *buf, uint16_t size, uint32_t addr, int is_out); 5 | -------------------------------------------------------------------------------- /src/hw/ftdi_driver.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __FTDI_DRIVER_HPP__ 2 | #define __FTDI_DRIVER_HPP__ 3 | 4 | #include 5 | #include 6 | 7 | extern "C" { 8 | #include "ftd2xx.h" 9 | #include "libft4222.h" 10 | } 11 | 12 | void print_buf(size_t offset, uint8_t *buf, size_t size); 13 | 14 | class FtdiDriver { 15 | public: 16 | typedef unsigned int DWORD; 17 | static const unsigned GPIO2 = 2; 18 | static const unsigned GPIO3 = 3; 19 | 20 | FtdiDriver(); 21 | ~FtdiDriver(); 22 | 23 | void eprintf(const char * format, ...); 24 | 25 | int List(); 26 | 27 | bool IsOpen(); 28 | 29 | // Open a connection to an automatically located FTDI device 30 | // Returns status, 0 for success 31 | int Open(); 32 | 33 | // Open with given SPI clock parameters 34 | int OpenClk(unsigned sys_clk, unsigned clk_div, DWORD target_id = 0); 35 | 36 | // Open a connection to a specific FTDI device 37 | // Returns status, 0 for success 38 | int Open(DWORD spi_loc_id, unsigned sys_clk, unsigned clk_div); 39 | 40 | // Enumeration of FTDI statemachine operation modes 41 | typedef enum { 42 | MODE_none, 43 | MODE_spi, 44 | MODE_i2c 45 | } MODE_t; 46 | 47 | // Select mode for FTDI state machine transfers 48 | int SetMode(FtdiDriver::MODE_t mode); 49 | 50 | // Close the connection to the FTDI device 51 | int Close(); 52 | 53 | // Note: caller should set sys_clk and clk_div to a default value 54 | // prior to calling. 55 | static void ParseParams(unsigned &sys_clk, unsigned &clk_div, 56 | int argc, char *argv[]); 57 | 58 | // Write arbitrary size message 59 | // Returns status, 0 for success 60 | int Write(uint32_t addr, uint8_t *write_data, size_t size); 61 | 62 | // Read arbitrary size message 63 | // Returns status, 0 for success 64 | int Read(uint32_t addr, uint8_t *read_data, size_t size); 65 | 66 | // Write size bytes, starting at the specified register. 67 | // Size must be 4, 16, 32, 64, 128, or 256 68 | // Returns status, 0 for success 69 | int WriteCmd(uint32_t addr, uint8_t *write_data, size_t size); 70 | 71 | // Read size bytes, starting at the specified register. 72 | // Size must be 4, 16, 32, 64, 128, or 256 73 | // Returns status, 0 for success 74 | int ReadCmd(uint32_t addr, uint8_t *read_data, size_t size); 75 | 76 | // Write size bytes, starting at the specified register. 77 | // Returns status, 0 for success 78 | int WriteRaw(uint8_t *write_buf, size_t size); 79 | 80 | // Read size bytes, starting at the specified register. 81 | // Returns status, 0 for success 82 | int ReadRaw(uint8_t *write_buf, size_t wsize, 83 | uint8_t *read_buf, size_t rsize); 84 | 85 | // TODO - Read/write abritrary sizes 86 | 87 | // Set GPIO output value 88 | // Port must be GPIO_PORT2 or GPIO_PORT3. 89 | int SetGPIO(GPIO_Port port, bool value); 90 | 91 | // Get GPIO output value 92 | // Port must be GPIO_PORT2 or GPIO_PORT3. 93 | int GetGPIO(GPIO_Port port, bool &value); 94 | 95 | // Tristate GPIO output 96 | // Port must be GPIO_PORT2 or GPIO_PORT3. 97 | int TriGPIO(GPIO_Port port); 98 | 99 | // Toggle GPIO 100 | void ToggleGPIO(GPIO_Port port); 101 | 102 | typedef enum { 103 | I2C_RETURN_CODE_success = 0, 104 | I2C_RETURN_CODE_fail, 105 | I2C_RETURN_CODE_timeout, 106 | I2C_RETURN_CODE_sdaBadState, 107 | I2C_RETURN_CODE_lostArbitration, 108 | I2C_RETURN_CODE_nack, 109 | I2C_RETURN_CODE_checkSumFail, 110 | I2C_RETURN_CODE__last 111 | } I2C_RETURN_CODE_t; 112 | 113 | const char *i2c_error_string[32] = 114 | { 115 | "success", 116 | "fail", 117 | "timeout", 118 | "sdaBadState", 119 | "lostArbitration", 120 | "nack", 121 | "checkSumFail", 122 | "(badErrorCode)" 123 | }; 124 | 125 | int i2c_Init(); 126 | 127 | int SendStopCondition(); 128 | 129 | int i2c_TransmitX 130 | ( int sendStartCondition, 131 | int sendStopCondition, 132 | int slaveAddress, 133 | uint8_t *buf, 134 | uint16_t bytesToXfer, 135 | uint16_t &bytesXfered ); 136 | 137 | int i2c_ReceiveX 138 | ( int sendStartCondition, 139 | int sendStopCondition, 140 | int slaveAddress, 141 | uint8_t *buf, 142 | uint16_t bytesToXfer, 143 | uint16_t &bytesXfered ); 144 | 145 | const char *i2c_error 146 | (int code); 147 | 148 | private: 149 | // Opaque pointer for the FTDI driver 150 | void *spi_ft_handle; 151 | void *gpio_ft_handle; 152 | 153 | // Location ID for the FTDI SPI device 154 | DWORD spi_loc_id; 155 | 156 | // Location ID for the FTDI GPIO device 157 | DWORD gpio_loc_id; 158 | 159 | // Internal transaction buffer 160 | uint8_t *int_write_buf; 161 | uint8_t *int_read_buf; 162 | 163 | // detected FTDI4222 CNF MODE 164 | int cnfmode; 165 | 166 | GPIO_Dir gpio_dir[4]; 167 | 168 | // selected SPI clock divider 169 | FT4222_SPIClock spi_clk_div; 170 | 171 | // current state machine transfer mode 172 | MODE_t active_mode = MODE_none; 173 | }; 174 | #endif 175 | -------------------------------------------------------------------------------- /src/hw/hw_interface.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HW_INTERFACE_H 2 | #define HW_INTERFACE_H 3 | 4 | #include 5 | #include 6 | 7 | #define N_HW_VDFS 3 8 | #define HW_VDF_STATUS_SIZE 0xb4 9 | #define HW_VDF_BURST_START 0x300014 10 | #define HW_VDF_TEMP_FLAG (1 << 3) 11 | 12 | #define HW_VDF_DEF_FREQ 1100.0 13 | #define HW_VDF_DEF_VOLTAGE 0.88 14 | 15 | struct vdf_value { 16 | uint64_t iters; 17 | mpz_t a, b; 18 | }; 19 | 20 | extern int chia_vdf_is_emu; 21 | 22 | class ChiaDriver; 23 | ChiaDriver *init_hw(double freq, double brd_voltage); 24 | void stop_hw(ChiaDriver *drv); 25 | 26 | struct vdf_state; 27 | void init_vdf_value(struct vdf_value *val); 28 | void clear_vdf_value(struct vdf_value *val); 29 | void copy_vdf_value(struct vdf_value *dst, struct vdf_value *src); 30 | 31 | int list_hw(void); 32 | void adjust_hw_freq(ChiaDriver *drv, uint8_t idx_mask, int direction); 33 | int start_hw_vdf(ChiaDriver *drv, mpz_t d, mpz_t a, mpz_t b, uint64_t n_iters, int idx); 34 | void stop_hw_vdf(ChiaDriver *drv, int idx); 35 | int read_hw_status(ChiaDriver *drv, uint8_t idx_mask, struct vdf_value *values); 36 | 37 | #endif // HW_INTERFACE_H 38 | -------------------------------------------------------------------------------- /src/hw/hw_proof.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HW_PROOF_H 2 | #define HW_PROOF_H 3 | 4 | #include "vdf_base.hpp" 5 | #include "hw_interface.hpp" 6 | #include "hw_util.hpp" 7 | #include "bqfc.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define HW_VDF_VALUE_INTERVAL 4000 15 | #define HW_VDF_VALUE_INTERVAL_DIVISORS { 2, 4, 5, 8, 10, 16, 20 } 16 | #define HW_VDF_CHKP_INTERVAL 1000000 17 | #define HW_VDF_MAX_AUX_THREADS 64 18 | #define HW_VDF_DEFAULT_MAX_AUX_THREADS 4 19 | #define HW_VDF_WQ_WARN_MULT 2 20 | #define HW_VDF_B_SIZE 33 21 | 22 | #define HW_VDF_PROOF_FLAG_DONE 1 23 | #define HW_VDF_PROOF_FLAG_IS_REQ 2 24 | #define HW_VDF_PROOF_FLAG_STARTED 4 25 | #define HW_VDF_PROOF_NONE ((uint16_t)0xffff) 26 | 27 | struct vdf_work { 28 | //size_t raw_idx; 29 | struct vdf_value start_val; 30 | uint64_t start_iters; 31 | uint32_t n_steps; 32 | }; 33 | 34 | struct vdf_proof_req { 35 | uint64_t iters; 36 | bool is_chkp; 37 | }; 38 | 39 | struct vdf_proof { 40 | uint64_t iters; 41 | uint64_t seg_iters; 42 | uint8_t y[BQFC_FORM_SIZE]; 43 | uint8_t proof[BQFC_FORM_SIZE]; 44 | uint8_t B[HW_VDF_B_SIZE]; 45 | 46 | uint8_t flags; 47 | uint16_t prev; 48 | uint16_t ref_cnt; 49 | }; 50 | 51 | struct vdf_proof_opts { 52 | uint8_t max_aux_threads; 53 | uint8_t max_proof_threads; 54 | }; 55 | 56 | struct vdf_state { 57 | uint64_t target_iters; 58 | uint64_t iters_offset; 59 | uint64_t cur_iters; 60 | std::atomic done_values; 61 | std::atomic done_iters; 62 | std::atomic elapsed_us; 63 | timepoint_t start_time; 64 | //std::vector raw_values; 65 | struct vdf_value last_val; 66 | std::vector
values; 67 | std::vector valid_values; 68 | std::mutex valid_values_mtx; 69 | std::deque req_proofs; 70 | std::vector proofs; 71 | std::mutex proofs_resize_mtx; 72 | std::vector queued_proofs; /* sorted queued proofs */ 73 | std::vector done_proofs; /* requested and not sent */ 74 | integer D, L, a2; 75 | std::deque wq; 76 | uint32_t wq_warn_thres[2]; 77 | //std::mutex wq_mtx; 78 | uint32_t interval; 79 | uint32_t chkp_interval; 80 | uint32_t n_bad; 81 | uint32_t n_skipped; 82 | uint32_t log_cnt; 83 | std::atomic aux_threads_busy; 84 | std::atomic n_proof_threads; 85 | uint8_t idx; 86 | uint8_t max_aux_threads; 87 | uint8_t max_proof_threads; 88 | bool completed; 89 | bool stopping; 90 | bool init_done; 91 | }; 92 | 93 | int hw_proof_add_value(struct vdf_state *vdf, struct vdf_value *val); 94 | form *hw_proof_last_good_form(struct vdf_state *vdf, size_t *out_pos); 95 | void hw_proof_handle_value(struct vdf_state *vdf, struct vdf_value *val); 96 | void hw_stop_proof(struct vdf_state *vdf); 97 | void hw_request_proof(struct vdf_state *vdf, uint64_t iters, bool is_chkp); 98 | void hw_compute_proof(struct vdf_state *vdf, size_t proof_idx, struct vdf_proof *out_proof, uint8_t thr_idx); 99 | int hw_retrieve_proof(struct vdf_state *vdf, struct vdf_proof **proof); 100 | void init_vdf_state(struct vdf_state *vdf, struct vdf_proof_opts *params, const char *d_str, const uint8_t *init_form, uint64_t n_iters, uint8_t idx); 101 | void clear_vdf_state(struct vdf_state *vdf); 102 | 103 | #endif // HW_PROOF_H 104 | -------------------------------------------------------------------------------- /src/hw/hw_test.cpp: -------------------------------------------------------------------------------- 1 | //#include "chia_driver.hpp" 2 | //#include "chia_vdf.hpp" 3 | 4 | //#include 5 | //#include 6 | //#include 7 | //#include 8 | //#include "alloc.hpp" 9 | //#include "create_discriminant.h" 10 | 11 | //#include "libft4222.h" 12 | 13 | //#include 14 | #include "hw_interface.hpp" 15 | #include "hw_proof.hpp" 16 | #include "vdf_base.hpp" 17 | #include "bqfc.h" 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | static const char *discrs[] = { 25 | "-0xac566497f63870a7b661f5482f47336cd1aa85ab43914828b7998f255916729c2ad965bcf7fe231721d96706ea7d823ed4adf663a0263714bb80144aebafcdd2915b6c7ef68c2d19447be83e7f39b4a7442640914053d2e7d6a561aa29b9449c815717af7da97a823798f402d073901a1f2bd8cd879b8b1afe2496649197021f", 26 | "-0xc3657f850b3f2b659d70273704564bc69b849fe1d8c70b096933efdcf7143931b393676f500d79624da783d73e0c5303ae48fb9543502c4161586d8fdaf03709d2115df21aeeee4a58614050cbdfe74024063b9620de084d8ef46f474fa57983c4bebfa7e8a69efeb523a167558fe1487a086c11337e20b773ad3d4710671417", 27 | "-0xffbd9edace9d59e0a395f0358698e8cd68a3dcb90af740d1fe14f58a06678e11d47e19b4f9b9237689084b724db8a912bb32d64614bbb5c4df5c54e9b574b8f7b7c59c8ac2522aae4777220696ee4dd11942e9e85b07de0454a491db7b19baa4c2f1eb3ae8930c44984c767c664d85e337b8e90cf1c5d3d30a9ec7ddcc3b26d7" 28 | }; 29 | 30 | void init_chia(void) 31 | { 32 | VdfBaseInit(); 33 | //init_gmp(); 34 | //set_rounding_mode(); 35 | //fesetround(FE_TOWARDZERO); 36 | } 37 | 38 | int hw_test_main(int argc, char **argv) 39 | { 40 | uint64_t n_iters = 1000000; 41 | uint8_t n_vdfs = 3; 42 | uint8_t n_completed = 0; 43 | uint8_t vdfs_mask; 44 | uint8_t init_form[BQFC_FORM_SIZE] = { 0x08 }; 45 | struct vdf_state vdfs[N_HW_VDFS]; 46 | struct vdf_value values[N_HW_VDFS]; 47 | struct vdf_proof proofs[N_HW_VDFS]; 48 | std::thread proof_threads[N_HW_VDFS]; 49 | //std::thread vdf_threads[N_HW_VDFS]; 50 | ChiaDriver *drv; 51 | uint64_t read_cnt = 0; 52 | uint32_t temp_period = chia_vdf_is_emu ? 200 : 2000; 53 | 54 | if (argc > 1) { 55 | n_vdfs = strtoul(argv[1], NULL, 0); 56 | if (n_vdfs > 3 || n_vdfs < 1) { 57 | n_vdfs = 2; 58 | } 59 | } 60 | if (argc > 2) { 61 | n_iters = strtoul(argv[2], NULL, 0); 62 | } 63 | init_chia(); 64 | drv = init_hw(HW_VDF_DEF_FREQ, HW_VDF_DEF_VOLTAGE); 65 | if (!drv) { 66 | return 1; 67 | } 68 | 69 | for (uint8_t i = 0; i < n_vdfs; i++) { 70 | struct vdf_state *vdf = &vdfs[i]; 71 | 72 | init_vdf_state(vdf, NULL, discrs[i], init_form, n_iters, i); 73 | 74 | //run_hw(vdf->d, n_iters, vdf); 75 | //vdf_threads[i] = std::thread(start_vdf_job, vdf, i); 76 | start_hw_vdf(drv, vdf->D.impl, vdf->last_val.a, vdf->last_val.b, vdf->target_iters, i); 77 | init_vdf_value(&values[i]); 78 | } 79 | 80 | vdfs_mask = (1 << n_vdfs) - 1; 81 | while (vdfs_mask) { 82 | uint8_t temp_flag = read_cnt % temp_period ? 0 : HW_VDF_TEMP_FLAG; 83 | read_hw_status(drv, vdfs_mask | temp_flag, values); 84 | for (uint8_t i = 0; i < n_vdfs; i++) { 85 | if (vdfs_mask & (1 << i)) { 86 | hw_proof_add_value(&vdfs[i], &values[i]); 87 | } 88 | 89 | if (vdfs[i].completed && (vdfs_mask & (1 << i))) { 90 | stop_hw_vdf(drv, i); 91 | vdfs_mask &= ~(1 << i); 92 | n_completed++; 93 | proofs[i].iters = n_iters; 94 | proofs[i].seg_iters = n_iters; 95 | proof_threads[i] = std::thread(hw_compute_proof, &vdfs[i], SIZE_MAX, &proofs[i], 255); 96 | } 97 | } 98 | //if (i == n_vdfs) { 99 | //fprintf(stderr, "Proofs completed: %d\n", (int)i); 100 | //break; 101 | //} 102 | if (chia_vdf_is_emu) { 103 | usleep(50000); 104 | } 105 | read_cnt++; 106 | } 107 | 108 | for (uint8_t i = 0; i < n_vdfs; i++) { 109 | proof_threads[i].join(); 110 | clear_vdf_state(&vdfs[i]); 111 | clear_vdf_value(&values[i]); 112 | } 113 | stop_hw(drv); 114 | return 0; 115 | } 116 | 117 | int main(int argc, char **argv) 118 | { 119 | init_chia(); 120 | return hw_test_main(argc, argv); 121 | } 122 | -------------------------------------------------------------------------------- /src/hw/hw_util.cpp: -------------------------------------------------------------------------------- 1 | #include "hw_util.hpp" 2 | 3 | #include 4 | #include 5 | 6 | void vdf_do_log(const char *msg, ...) 7 | { 8 | va_list ap; 9 | struct tm cal; 10 | char time_str[25]; 11 | struct timespec ts; 12 | 13 | clock_gettime(CLOCK_REALTIME, &ts); 14 | localtime_r(&ts.tv_sec, &cal); 15 | strftime(time_str, sizeof(time_str), "%FT%T", &cal); 16 | fprintf(stderr, "%s.%03ld ", time_str, ts.tv_nsec / 1000000); 17 | 18 | va_start(ap, msg); 19 | vfprintf(stderr, msg, ap); 20 | va_end(ap); 21 | } 22 | -------------------------------------------------------------------------------- /src/hw/hw_util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HW_UTIL_H 2 | #define HW_UTIL_H 3 | 4 | #include 5 | #include 6 | 7 | void vdf_do_log(const char *msg, ...); 8 | 9 | #ifndef VDF_ENABLE_LOG_DEBUG 10 | # define VDF_ENABLE_LOG_DEBUG 0 11 | #endif 12 | 13 | #if VDF_ENABLE_LOG_DEBUG 14 | # define LOG_DEBUG(msg, ...) vdf_do_log(msg "\n", ##__VA_ARGS__) 15 | #else 16 | # define LOG_DEBUG(msg, ...) ((void)msg) 17 | #endif 18 | #define LOG_INFO(msg, ...) vdf_do_log(msg "\n", ##__VA_ARGS__) 19 | #define LOG_ERROR(msg, ...) vdf_do_log(msg "\n", ##__VA_ARGS__) 20 | 21 | #define LOG_SIMPLE(msg, ...) fprintf(stderr, msg "\n", ##__VA_ARGS__) 22 | 23 | typedef std::chrono::time_point timepoint_t; 24 | 25 | static inline timepoint_t vdf_get_cur_time(void) 26 | { 27 | return std::chrono::high_resolution_clock::now(); 28 | } 29 | 30 | static inline uint64_t vdf_get_elapsed_us(timepoint_t &t1) 31 | { 32 | auto t2 = vdf_get_cur_time(); 33 | return std::chrono::duration_cast(t2 - t1).count(); 34 | } 35 | 36 | #endif /* HW_UTIL_H */ 37 | -------------------------------------------------------------------------------- /src/hw/pll_freqs.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _PLL_FREQS_HPP_ 2 | #define _PLL_FREQS_HPP_ 3 | 4 | #include 5 | 6 | #define VALID_PLL_FILTER_RANGES 8 7 | #define VALID_PLL_FREQS 8003 8 | 9 | /* 10 | struct pll_filter_range { 11 | double low; 12 | double high; 13 | }; 14 | 15 | pll_filter_range pll_filter_ranges[VALID_PLL_FILTER_RANGES] = { 16 | {5.0, 7.5}, 17 | {7.5, 11.0}, 18 | {11.0, 18.0}, 19 | {18.0, 30.0}, 20 | {30.0, 50.0}, 21 | {50.0, 80.0}, 22 | {80.0, 130.0}, 23 | {130.0, 200,0} 24 | }; 25 | */ 26 | 27 | extern double pll_filter_ranges[VALID_PLL_FILTER_RANGES]; 28 | 29 | struct pll_settings { 30 | uint32_t divr; 31 | uint32_t divfi; 32 | uint32_t divq; 33 | }; 34 | 35 | // freq = 400 * (divfi + 1) / (2 * (divr + 1) * (divq + 1)) 36 | struct pll_entry { 37 | double freq; 38 | pll_settings settings; 39 | }; 40 | 41 | extern pll_entry pll_entries[VALID_PLL_FREQS]; 42 | 43 | #endif // _PLL_FREQS_HPP_ 44 | -------------------------------------------------------------------------------- /src/hw/pvt.hpp: -------------------------------------------------------------------------------- 1 | // Generated register defines for pvt 2 | #ifndef _PVT_REG_DEFS_ 3 | #define _PVT_REG_DEFS_ 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | // Register width 9 | #define PVT_PARAM_REG_WIDTH 32 10 | 11 | // PVT measurement control 12 | #define PVT_ENABLE_REG_OFFSET 0x10000 13 | #define PVT_ENABLE_REG_RESVAL 0x0 14 | #define PVT_ENABLE_ENABLE_BIT 0 15 | #define PVT_ENABLE_STATUS_MASK 0xf 16 | #define PVT_ENABLE_STATUS_OFFSET 12 17 | #define PVT_ENABLE_STATUS_FIELD \ 18 | ((bitfield_field32_t) { .mask = PVT_ENABLE_STATUS_MASK, .index = PVT_ENABLE_STATUS_OFFSET }) 19 | 20 | // PVT clock period 21 | #define PVT_PERIOD_REG_OFFSET 0x10004 22 | #define PVT_PERIOD_REG_RESVAL 0x0 23 | #define PVT_PERIOD_PERIOD_MASK 0x3f 24 | #define PVT_PERIOD_PERIOD_OFFSET 0 25 | #define PVT_PERIOD_PERIOD_FIELD \ 26 | ((bitfield_field32_t) { .mask = PVT_PERIOD_PERIOD_MASK, .index = PVT_PERIOD_PERIOD_OFFSET }) 27 | 28 | // PVT tuning for TRIMG 29 | #define PVT_TRIMG_REG_OFFSET 0x10008 30 | #define PVT_TRIMG_REG_RESVAL 0xf 31 | #define PVT_TRIMG_TRIMG_MASK 0x1f 32 | #define PVT_TRIMG_TRIMG_OFFSET 0 33 | #define PVT_TRIMG_TRIMG_FIELD \ 34 | ((bitfield_field32_t) { .mask = PVT_TRIMG_TRIMG_MASK, .index = PVT_TRIMG_TRIMG_OFFSET }) 35 | 36 | // PVT tuning for TRIMO 37 | #define PVT_TRIMO_REG_OFFSET 0x1000c 38 | #define PVT_TRIMO_REG_RESVAL 0x0 39 | #define PVT_TRIMO_TRIMO_MASK 0x3f 40 | #define PVT_TRIMO_TRIMO_OFFSET 0 41 | #define PVT_TRIMO_TRIMO_FIELD \ 42 | ((bitfield_field32_t) { .mask = PVT_TRIMO_TRIMO_MASK, .index = PVT_TRIMO_TRIMO_OFFSET }) 43 | 44 | // Die temperature measurement 45 | #define PVT_TEMPERATURE_REG_OFFSET 0x10010 46 | #define PVT_TEMPERATURE_REG_RESVAL 0x0 47 | #define PVT_TEMPERATURE_TEMPERATURE_MASK 0x3ff 48 | #define PVT_TEMPERATURE_TEMPERATURE_OFFSET 0 49 | #define PVT_TEMPERATURE_TEMPERATURE_FIELD \ 50 | ((bitfield_field32_t) { .mask = PVT_TEMPERATURE_TEMPERATURE_MASK, .index = PVT_TEMPERATURE_TEMPERATURE_OFFSET }) 51 | 52 | // Die LVT process measurement 53 | #define PVT_PROCESS_LVT_REG_OFFSET 0x10014 54 | #define PVT_PROCESS_LVT_REG_RESVAL 0x0 55 | #define PVT_PROCESS_LVT_LVT_MASK 0x3ff 56 | #define PVT_PROCESS_LVT_LVT_OFFSET 0 57 | #define PVT_PROCESS_LVT_LVT_FIELD \ 58 | ((bitfield_field32_t) { .mask = PVT_PROCESS_LVT_LVT_MASK, .index = PVT_PROCESS_LVT_LVT_OFFSET }) 59 | 60 | // Die SLVT process measurement 61 | #define PVT_PROCESS_SLVT_REG_OFFSET 0x10018 62 | #define PVT_PROCESS_SLVT_REG_RESVAL 0x0 63 | #define PVT_PROCESS_SLVT_SLVT_MASK 0x3ff 64 | #define PVT_PROCESS_SLVT_SLVT_OFFSET 0 65 | #define PVT_PROCESS_SLVT_SLVT_FIELD \ 66 | ((bitfield_field32_t) { .mask = PVT_PROCESS_SLVT_SLVT_MASK, .index = PVT_PROCESS_SLVT_SLVT_OFFSET }) 67 | 68 | // Die RVT process measurement 69 | #define PVT_PROCESS_RVT_REG_OFFSET 0x1001c 70 | #define PVT_PROCESS_RVT_REG_RESVAL 0x0 71 | #define PVT_PROCESS_RVT_RVT_MASK 0x3ff 72 | #define PVT_PROCESS_RVT_RVT_OFFSET 0 73 | #define PVT_PROCESS_RVT_RVT_FIELD \ 74 | ((bitfield_field32_t) { .mask = PVT_PROCESS_RVT_RVT_MASK, .index = PVT_PROCESS_RVT_RVT_OFFSET }) 75 | 76 | // Voltage measurement 77 | #define PVT_VOLTAGE_REG_OFFSET 0x10020 78 | #define PVT_VOLTAGE_REG_RESVAL 0x0 79 | #define PVT_VOLTAGE_VOLTAGE_MASK 0x3ff 80 | #define PVT_VOLTAGE_VOLTAGE_OFFSET 0 81 | #define PVT_VOLTAGE_VOLTAGE_FIELD \ 82 | ((bitfield_field32_t) { .mask = PVT_VOLTAGE_VOLTAGE_MASK, .index = PVT_VOLTAGE_VOLTAGE_OFFSET }) 83 | 84 | // Temperature alarm to external pin. 85 | #define PVT_TEMP_ALARM_EXTERNAL_REG_OFFSET 0x10024 86 | #define PVT_TEMP_ALARM_EXTERNAL_REG_RESVAL 0x3ff 87 | #define PVT_TEMP_ALARM_EXTERNAL_THRESHOLD_MASK 0x3ff 88 | #define PVT_TEMP_ALARM_EXTERNAL_THRESHOLD_OFFSET 0 89 | #define PVT_TEMP_ALARM_EXTERNAL_THRESHOLD_FIELD \ 90 | ((bitfield_field32_t) { .mask = PVT_TEMP_ALARM_EXTERNAL_THRESHOLD_MASK, .index = PVT_TEMP_ALARM_EXTERNAL_THRESHOLD_OFFSET }) 91 | #define PVT_TEMP_ALARM_EXTERNAL_STATUS_BIT 15 92 | 93 | // Temperature alarm to engines 94 | #define PVT_TEMP_ALARM_ENGINE_REG_OFFSET 0x10028 95 | #define PVT_TEMP_ALARM_ENGINE_REG_RESVAL 0x3ff 96 | #define PVT_TEMP_ALARM_ENGINE_THRESHOLD_MASK 0x3ff 97 | #define PVT_TEMP_ALARM_ENGINE_THRESHOLD_OFFSET 0 98 | #define PVT_TEMP_ALARM_ENGINE_THRESHOLD_FIELD \ 99 | ((bitfield_field32_t) { .mask = PVT_TEMP_ALARM_ENGINE_THRESHOLD_MASK, .index = PVT_TEMP_ALARM_ENGINE_THRESHOLD_OFFSET }) 100 | #define PVT_TEMP_ALARM_ENGINE_STATUS_BIT 15 101 | 102 | // Mark the end of the address space 103 | #define PVT_END_OF_RANGE_REG_OFFSET 0x1fffc 104 | #define PVT_END_OF_RANGE_REG_RESVAL 0x0 105 | #define PVT_END_OF_RANGE_MARKER_BIT 0 106 | 107 | #ifdef __cplusplus 108 | } // extern "C" 109 | #endif 110 | #endif // _PVT_REG_DEFS_ 111 | // End generated register defines for pvt 112 | -------------------------------------------------------------------------------- /src/hw/real_hw.cpp: -------------------------------------------------------------------------------- 1 | // This is used to determine that we're running the real hardware (not emulated) 2 | int chia_vdf_is_emu = 0; 3 | -------------------------------------------------------------------------------- /src/hw/vdf_driver.hpp: -------------------------------------------------------------------------------- 1 | #ifndef VDF_DRIVER_HPP 2 | #define VDF_DRIVER_HPP 3 | 4 | #include 5 | #include "ftdi_driver.hpp" 6 | #include "pvt.hpp" 7 | #include "clock.hpp" 8 | #include "hw_util.hpp" 9 | 10 | // Define the baseclass VDF driver 11 | class VdfDriver { 12 | public: 13 | // Bit sizes for polynomial conversion 14 | unsigned WORD_BITS; 15 | unsigned REDUNDANT_BITS; 16 | bool SIGNED; 17 | 18 | // CSR width 19 | static const unsigned REG_BITS = 32; 20 | static const unsigned REG_BYTES = 32/8; 21 | static const uint32_t RESET_BIT = 0x10; 22 | static const uint32_t CLOCK_BIT = 0x01; 23 | static const uint32_t BURST_ADDR = 0x300000; 24 | static const uint32_t VDF_ENGINE_STRIDE = 0x10000; 25 | 26 | FtdiDriver ftdi; 27 | uint32_t freq_idx; 28 | timepoint_t last_freq_update; 29 | 30 | VdfDriver(unsigned _WORD_BITS, unsigned _REDUNDANT_BITS, 31 | bool _SIGNED) 32 | : WORD_BITS(_WORD_BITS), REDUNDANT_BITS(_REDUNDANT_BITS), 33 | SIGNED(_SIGNED) { 34 | } 35 | 36 | virtual ~VdfDriver() {} 37 | 38 | // Functions for serializing to buffers 39 | // Append size bytes to 'buf' starting at offset. Returns size. 40 | size_t write_bytes(size_t size, size_t offset, uint8_t *buf, 41 | uint32_t val); 42 | size_t write_bytes(size_t size, size_t offset, uint8_t *buf, 43 | uint64_t val); 44 | size_t write_bytes(size_t size, size_t offset, uint8_t *buf, 45 | mpz_t val, size_t num_coeffs); 46 | 47 | // Functions for deserializing from buffers 48 | size_t read_bytes(size_t size, size_t offset, uint8_t *buf, 49 | uint32_t &val); 50 | size_t read_bytes(size_t size, size_t offset, uint8_t *buf, 51 | uint64_t &val); 52 | size_t read_bytes(size_t size, size_t offset, uint8_t *buf, 53 | mpz_t val, size_t num_coeffs); 54 | 55 | // Returns the buffer size needed for a command 56 | virtual size_t CmdSize() = 0; 57 | 58 | // Returns the buffer size needed for a status 59 | virtual size_t StatusSize() = 0; 60 | 61 | // Perform a single dword register write, return 0 on success 62 | int RegWrite(uint32_t addr, uint32_t data) { 63 | uint32_t buf; 64 | int stat; 65 | write_bytes(REG_BYTES, 0, (uint8_t *)&buf, data); 66 | stat = ftdi.WriteCmd(addr, (uint8_t *)&buf, REG_BYTES); 67 | if (stat) { 68 | fprintf(stderr, "ftdi.WriteCmd in RegWrite failed (error %d)\n",stat); 69 | return stat; 70 | }; 71 | return 0; 72 | } 73 | 74 | // Perform a single dword register read, return 0 on success 75 | int RegRead(uint32_t addr, uint32_t &data) { 76 | uint32_t buf; 77 | int stat; 78 | stat = ftdi.ReadCmd(addr, (uint8_t *)&buf, REG_BYTES); 79 | if (stat) { 80 | fprintf(stderr, "ftdi.ReadCmd in RegRead failed (error %d)\n",stat); 81 | return stat; 82 | }; 83 | read_bytes(REG_BYTES, 0, (uint8_t *)&buf, data); 84 | return 0; 85 | } 86 | 87 | // Enable the engine at the provided address 88 | void EnableEngine(unsigned control_csr) { 89 | RegWrite(control_csr, (uint32_t)0); // Clear reset 90 | RegWrite(control_csr, CLOCK_BIT); // Enable clock 91 | } 92 | 93 | // Disable the engine at the provided address 94 | void DisableEngine(unsigned control_csr) { 95 | RegWrite(control_csr, (uint32_t)0); // Disable clock 96 | RegWrite(control_csr, RESET_BIT); // Set reset 97 | } 98 | 99 | // Reset the engine at the provided address 100 | void ResetEngine(unsigned control_csr) { 101 | DisableEngine(control_csr); 102 | EnableEngine(control_csr); 103 | } 104 | 105 | void EnablePvt(); 106 | double ValueToTemp(uint32_t temp_code); 107 | uint32_t TempToValue(double temp); 108 | double GetPvtTemp(); 109 | double GetPvtVoltage(); 110 | double GetTempAlarmExternal(); 111 | double GetTempAlarmEngine(); 112 | bool IsTempAlarmExternalSet(); 113 | bool IsTempAlarmEngineSet(); 114 | bool IsTempAlarmSet(); 115 | bool SetTempAlarmExternal(double temp); 116 | bool SetTempAlarmEngine(double temp); 117 | void ResetPLL(); 118 | bool SetPLLFrequency(double frequency, uint32_t entry_index); 119 | double GetPLLFrequency(); 120 | int Reset(uint32_t sleep_duration); 121 | int TurnFanOn(); 122 | int TurnFanOff(); 123 | int I2CWriteReg(uint8_t i2c_addr, uint8_t reg_addr, uint8_t data); 124 | int I2CReadReg(uint8_t i2c_addr, uint8_t reg_addr, size_t len, uint8_t* data); 125 | double GetBoardVoltage(); 126 | int SetBoardVoltage(double voltage); 127 | double GetBoardCurrent(); 128 | double GetPower(); 129 | }; 130 | #endif 131 | -------------------------------------------------------------------------------- /src/include.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_H 2 | #define INCLUDE_H 3 | 4 | #ifdef NDEBUG 5 | #undef NDEBUG 6 | #endif 7 | 8 | #if VDF_MODE==0 9 | #define NDEBUG 10 | #endif 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #ifndef _WIN32 24 | #include 25 | typedef unsigned __int128 uint128; 26 | typedef __int128 int128; 27 | #define USED __attribute__((used)) 28 | #else 29 | #include "uint128_t/uint128_t.h" 30 | #define USED 31 | #endif 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include "generic.h" 47 | #include 48 | 49 | using namespace std; 50 | using namespace generic; 51 | 52 | typedef uint8_t uint8; 53 | typedef uint16_t uint16; 54 | typedef uint32_t uint32; 55 | typedef uint64_t uint64; 56 | typedef int8_t int8; 57 | typedef int16_t int16; 58 | typedef int32_t int32; 59 | typedef int64_t int64; 60 | 61 | #define todo 62 | 63 | #endif // INCLUDE_H 64 | -------------------------------------------------------------------------------- /src/integer.h: -------------------------------------------------------------------------------- 1 | #ifndef INTEGER_H 2 | #define INTEGER_H 3 | 4 | #include "integer_common.h" 5 | #include "xgcd_partial.c" 6 | 7 | //a and b are nonnegative 8 | void xgcd_partial(integer& u, integer& v, integer& a, integer& b, const integer& L) { 9 | mpz_t f_u; mpz_init(f_u); 10 | mpz_t f_v; mpz_init(f_v); 11 | mpz_t f_a; mpz_init(f_a); 12 | mpz_t f_b; mpz_init(f_b); 13 | mpz_t f_L; mpz_init(f_L); 14 | 15 | mpz_set(f_a, a.impl); 16 | mpz_set(f_b, b.impl); 17 | mpz_set(f_L, L.impl); 18 | 19 | mpz_xgcd_partial(f_u, f_v, f_a, f_b, f_L); 20 | 21 | mpz_set(u.impl, f_u); 22 | mpz_set(v.impl, f_v); 23 | mpz_set(a.impl, f_a); 24 | mpz_set(b.impl, f_b); 25 | 26 | mpz_clear(f_u); 27 | mpz_clear(f_v); 28 | mpz_clear(f_a); 29 | mpz_clear(f_b); 30 | mpz_clear(f_L); 31 | } 32 | 33 | void inject_error(mpz_struct* i) { 34 | if (!enable_random_error_injection) { 35 | return; 36 | } 37 | 38 | mark_vdf_test(); 39 | 40 | double v=rand_integer(32).to_vector()[0]/double(1ull<<32); 41 | 42 | if (v 32 | #include "gmp-impl.h" 33 | 34 | /* RUNTIMECPUID */ 35 | int bCheckedBMI = 0; 36 | int bBMI1 = 0; 37 | int bBMI2 = 0; 38 | int bCheckedLZCNT = 0; 39 | int bLZCNT = 0; 40 | 41 | /* mpn_divexact_by3 was a function in gmp 3.0.1, but as of gmp 3.1 it's a 42 | macro calling mpn_divexact_by3c. */ 43 | mp_limb_t 44 | __MPN (divexact_by3) (mp_ptr dst, mp_srcptr src, mp_size_t size) 45 | { 46 | return mpn_divexact_by3 (dst, src, size); 47 | } 48 | 49 | 50 | /* mpn_divmod_1 was a function in gmp 3.0.1 and earlier, but marked obsolete 51 | in both gmp 2 and 3. As of gmp 3.1 it's a macro calling mpn_divrem_1. */ 52 | mp_limb_t 53 | __MPN (divmod_1) (mp_ptr dst, mp_srcptr src, mp_size_t size, mp_limb_t divisor) 54 | { 55 | return mpn_divmod_1 (dst, src, size, divisor); 56 | } 57 | 58 | 59 | /* mpz_legendre was a separate function in gmp 3.1.1 and earlier, but as of 60 | 4.0 it's a #define alias for mpz_jacobi. */ 61 | int 62 | __gmpz_legendre (mpz_srcptr a, mpz_srcptr b) 63 | { 64 | return mpz_jacobi (a, b); 65 | } 66 | -------------------------------------------------------------------------------- /src/primetest.h: -------------------------------------------------------------------------------- 1 | #ifndef PRIMETEST_H 2 | #define PRIMETEST_H 3 | 4 | #include "pprods.h" 5 | 6 | static int miller_rabin(const mpz_t n, mpz_t b, mpz_t d) 7 | { 8 | int r = 0, s = mpz_scan1(n, 1); 9 | 10 | mpz_tdiv_q_2exp(d, n, s); 11 | mpz_powm(b, b, d, n); 12 | if (!mpz_cmp_ui(b, 1)) 13 | return 1; 14 | 15 | while (1) { 16 | mpz_add_ui(b, b, 1); 17 | if (!mpz_cmp(b, n)) 18 | return 1; 19 | r++; 20 | if (r == s) 21 | return 0; 22 | mpz_sub_ui(b, b, 1); 23 | mpz_powm_ui(b, b, 2, n); 24 | } 25 | } 26 | 27 | #define VPRP_MAX_D 1000 28 | 29 | static int find_pq(int *out_p, int *out_q, const mpz_t n, mpz_t tmp) 30 | { 31 | int d = 5; 32 | 33 | for (; d < VPRP_MAX_D; d = abs(d) + 2) { 34 | d = d % 4 == 1 ? d : -d; 35 | mpz_set_si(tmp, d); 36 | if (mpz_jacobi(tmp, n) == -1) { 37 | if (d == 5) { 38 | *out_p = 5; 39 | *out_q = 5; 40 | } else { 41 | *out_p = 1; 42 | *out_q = (1 - d) / 4; 43 | } 44 | return 0; 45 | } 46 | } 47 | 48 | /* Failed to find suitable d < VPRP_MAX_D. */ 49 | return 1; 50 | } 51 | 52 | static void addmul_si(mpz_t p, mpz_t a, int b) 53 | { 54 | if (b < 0) 55 | mpz_submul_ui(p, a, -b); 56 | else 57 | mpz_addmul_ui(p, a, b); 58 | } 59 | 60 | static void find_lucas_v(mpz_t u1, mpz_t e, const mpz_t m, int p, int q, 61 | mpz_t u2, mpz_t tmp2) 62 | { 63 | int i, l = mpz_sizeinbase(e, 2), minus_2q = -2 * q; 64 | 65 | mpz_set_ui(u1, 1); /* U1 */ 66 | mpz_set_ui(u2, p); /* U2 */ 67 | 68 | for (i = l - 2; i >= 0; i--) { 69 | mpz_mul(tmp2, u2, u1); /* U1 * U2 */ 70 | mpz_mul(u2, u2, u2); /* U2**2 */ 71 | mpz_mul(u1, u1, u1); /* U1**2 */ 72 | if (mpz_tstbit(e, i)) { 73 | mpz_mul_si(u1, u1, q); 74 | mpz_sub(u1, u2, u1); /* U1 = U2**2 - Q * U1**2 */ 75 | 76 | if (p != 1) { 77 | mpz_mul_ui(u2, u2, p); 78 | } 79 | addmul_si(u2, tmp2, minus_2q); 80 | 81 | } else { 82 | addmul_si(u2, u1, -q); /* U2 = U2**2 - Q * U1**2 */ 83 | mpz_mul_2exp(tmp2, tmp2, 1); 84 | if (p != 1) { 85 | mpz_submul_ui(tmp2, u1, p); 86 | mpz_swap(tmp2, u1); 87 | } else { 88 | mpz_sub(u1, tmp2, u1); 89 | } 90 | } 91 | mpz_tdiv_r(u1, u1, m); 92 | mpz_tdiv_r(u2, u2, m); 93 | } 94 | /* V1 = 2 * U2 - P * U1 */ 95 | mpz_mul_2exp(u2, u2, 1); 96 | mpz_submul_ui(u2, u1, p); 97 | mpz_swap(u1, u2); /* Place V1 into U1 */ 98 | /*mpz_tdiv_r(u1, u1, m);*/ 99 | } 100 | 101 | static int is_vprp(const mpz_t n, mpz_t tmp1, mpz_t tmp2) 102 | { 103 | int p, q, res = 0; 104 | mpz_t e, v; 105 | 106 | mpz_inits(v, e, NULL); 107 | 108 | if (find_pq(&p, &q, n, tmp1)) 109 | goto out; 110 | 111 | mpz_add_ui(e, n, 1); 112 | 113 | find_lucas_v(v, e, n, p, q, tmp1, tmp2); 114 | if (q < 0) 115 | mpz_cdiv_r(v, v, n); 116 | else 117 | mpz_fdiv_r(v, v, n); 118 | res = mpz_cmp_si(v, 2 * q) == 0; 119 | 120 | out: 121 | mpz_clears(v, e, NULL); 122 | return res; 123 | } 124 | 125 | /* 126 | * This is a "strengthened" Baillie-PSW primality test based on "Strengthening 127 | * the Baillie-PSW primality test" paper (https://arxiv.org/abs/2006.14425). 128 | * 129 | * The primality test consists of 3 steps: 130 | * 1. Compute GCDs of n (the number being tested) and products of small primes. 131 | * Declare n to be composite if any of those GCDs is not equal to 1. For a 132 | * randomly generated odd n of 1024 bits in length, the test ends here in 88% 133 | * of cases, and it's substantially faster than running a single round of 134 | * Miller-Rabin test. 135 | * 2. Do a single round of Miller-Rabin test with base 2. 136 | * 3. Do the Lucas-V Probable Prime test (vprp). 137 | */ 138 | static int is_prime_bpsw(const mpz_t n) 139 | { 140 | int i, ret = 0; 141 | int min_pprods = 5, n_pprods = sizeof(pprods) / sizeof(*pprods); 142 | mpz_t b, d; 143 | 144 | /* Discard even numbers. */ 145 | if (!mpz_tstbit(n, 0) && mpz_cmp_ui(n, 2)) 146 | return 0; 147 | 148 | /* Adjust the number of GCDs to compute with products of small primes 149 | * if bit length of n is less than 1024. As the computational cost of 150 | * Miller-Rabin test is cubic in bit length of n, choose the number of 151 | * GCD tests proportionally to the cube of n's bit length. */ 152 | if (n->_mp_size < 16) { 153 | int size_cube = n->_mp_size * n->_mp_size * n->_mp_size; 154 | n_pprods = min_pprods + (n_pprods - min_pprods) * size_cube / (16*16*16); 155 | } 156 | 157 | for (i = 0; i < n_pprods; i++) { 158 | if (mpn_gcd_1(n->_mp_d, n->_mp_size, pprods[i]) != 1) 159 | return 0; 160 | } 161 | 162 | mpz_init2(b, n->_mp_size * sizeof(n->_mp_d[0]) * 8); 163 | mpz_init2(d, n->_mp_size * sizeof(n->_mp_d[0]) * 8); 164 | mpz_set_ui(b, 2); 165 | ret = miller_rabin(n, b, d) && is_vprp(n, b, d); 166 | 167 | mpz_clears(b, d, NULL); 168 | return ret ? 2 : 0; 169 | } 170 | 171 | #endif // PRIMETEST_H 172 | -------------------------------------------------------------------------------- /src/proof_common.h: -------------------------------------------------------------------------------- 1 | #ifndef PROOF_COMMON_H 2 | #define PROOF_COMMON_H 3 | #include "Reducer.h" 4 | #include "bqfc.c" 5 | 6 | const int B_bits = 264; 7 | const int B_bytes = (B_bits + 7) / 8; 8 | 9 | 10 | // Generates a random pseudoprime using the hash and check method: 11 | // Randomly chooses x with bit-length `length`, then applies a mask 12 | // (for b in bitmask) { x |= (1 << b) }. 13 | // Then return x if it is a pseudoprime, otherwise repeat. 14 | integer HashPrime(std::vector seed, int length, vector bitmask) { 15 | assert (length % 8 == 0); 16 | std::vector hash(picosha2::k_digest_size); // output of sha256 17 | std::vector blob; // output of 1024 bit hash expansions 18 | std::vector sprout = seed; // seed plus nonce 19 | 20 | while (true) { // While prime is not found 21 | blob.resize(0); 22 | while ((int) blob.size() * 8 < length) { 23 | // Increment sprout by 1 24 | for (int i = (int) sprout.size() - 1; i >= 0; --i) { 25 | sprout[i]++; 26 | if (sprout[i]) 27 | break; 28 | } 29 | picosha2::hash256(sprout.begin(), sprout.end(), hash.begin(), hash.end()); 30 | // Visual Studio doesn't like pointer arithmetic past the bounds 31 | blob.insert(blob.end(), hash.begin(), 32 | hash.begin() + std::min(hash.size(), length / 8 - blob.size())); 33 | } 34 | 35 | assert ((int) blob.size() * 8 == length); 36 | integer p(blob); // p = 7 (mod 8), 2^1023 <= p < 2^1024 37 | for (int b: bitmask) 38 | p.set_bit(b, true); 39 | // Force the number to be odd 40 | p.set_bit(0, true); 41 | if (p.prime()) 42 | return p; 43 | } 44 | } 45 | 46 | std::vector SerializeForm(form &y, int d_bits) 47 | { 48 | y.reduce(); 49 | std::vector res(BQFC_FORM_SIZE); 50 | bqfc_serialize(res.data(), y.a.impl, y.b.impl, d_bits); 51 | return res; 52 | } 53 | 54 | form DeserializeForm(const integer &D, const uint8_t *bytes, size_t size) 55 | { 56 | integer a, b; 57 | if (bqfc_deserialize(a.impl, b.impl, D.impl, bytes, size, D.num_bits())) { 58 | throw std::runtime_error("Deserializing compressed form failed"); 59 | } 60 | form f = form::from_abd(a, b, D); 61 | if (!f.is_reduced()) { 62 | throw std::runtime_error("Form is not reduced"); 63 | } 64 | return f; 65 | } 66 | 67 | integer FastPow(uint64_t a, uint64_t b, integer& c) { 68 | integer res, a1 = integer(a); 69 | if (mpz_sgn(c.impl) == 0) { 70 | throw std::runtime_error("Division by 0"); 71 | } 72 | mpz_powm_ui(res.impl, a1.impl, b, c.impl); 73 | return res; 74 | } 75 | 76 | integer GetB(const integer& D, form &x, form& y) { 77 | int d_bits = D.num_bits(); 78 | std::vector serialization = SerializeForm(x, d_bits); 79 | std::vector serialization_y = SerializeForm(y, d_bits); 80 | serialization.insert(serialization.end(), serialization_y.begin(), serialization_y.end()); 81 | return HashPrime(serialization, B_bits, {B_bits - 1}); 82 | } 83 | 84 | class PulmarkReducer { 85 | ClassGroupContext *t; 86 | Reducer *reducer; 87 | 88 | public: 89 | PulmarkReducer() { 90 | t=new ClassGroupContext(4096); 91 | reducer=new Reducer(*t); 92 | } 93 | 94 | ~PulmarkReducer() { 95 | delete(reducer); 96 | delete(t); 97 | } 98 | 99 | void reduce(form &f) { 100 | mpz_set(t->a, f.a.impl); 101 | mpz_set(t->b, f.b.impl); 102 | mpz_set(t->c, f.c.impl); 103 | 104 | reducer->run(); 105 | 106 | mpz_set(f.a.impl, t->a); 107 | mpz_set(f.b.impl, t->b); 108 | mpz_set(f.c.impl, t->c); 109 | } 110 | }; 111 | 112 | form FastPowFormNucomp(form x, integer &D, integer num_iterations, integer &L, PulmarkReducer& reducer) 113 | { 114 | if (!mpz_sgn(num_iterations.impl)) 115 | return form::identity(D); 116 | 117 | form res = x; 118 | int max_size = -D.impl->_mp_size / 2, i; 119 | 120 | // Do exponentiation by squaring from top bits of exponent to bottom 121 | for (i = num_iterations.num_bits() - 2; i >= 0; i--) { 122 | nudupl_form(res, res, D, L); 123 | if (res.a.impl->_mp_size > max_size) { 124 | // Reduce only when 'a' exceeds a half of the discriminant size 125 | reducer.reduce(res); 126 | } 127 | 128 | if (num_iterations.get_bit(i)) { 129 | nucomp_form(res, res, x, D, L); 130 | } 131 | } 132 | 133 | reducer.reduce(res); 134 | return res; 135 | } 136 | 137 | # endif // PROOF_COMMON_H 138 | -------------------------------------------------------------------------------- /src/prover_base.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROVER_BASE_H 2 | #define PROVER_BASE_H 3 | 4 | #include 5 | 6 | #include "proof_common.h" 7 | #include "util.h" 8 | 9 | class Prover { 10 | public: 11 | Prover(Segment segm, integer D) { 12 | this->segm = segm; 13 | this->D = D; 14 | this->num_iterations = segm.length; 15 | is_finished = false; 16 | } 17 | 18 | virtual form* GetForm(uint64_t iteration) = 0; 19 | virtual void start() = 0; 20 | virtual void stop() = 0; 21 | virtual bool PerformExtraStep() = 0; 22 | virtual void OnFinish() = 0; 23 | 24 | bool IsFinished() { 25 | return is_finished; 26 | } 27 | 28 | form GetProof() { 29 | return proof; 30 | } 31 | 32 | uint64_t GetBlock(uint64_t i, uint64_t k, uint64_t T, integer& B) { 33 | integer res = FastPow(2, T - k * (i + 1), B); 34 | mpz_mul_2exp(res.impl, res.impl, k); 35 | res = res / B; 36 | auto res_vector = res.to_vector(); 37 | return res_vector.empty() ? 0 : res_vector[0]; 38 | } 39 | 40 | void GenerateProof() { 41 | PulmarkReducer reducer; 42 | 43 | integer B = GetB(D, segm.x, segm.y); 44 | integer L=root(-D, 4); 45 | form id; 46 | try { 47 | id = form::identity(D); 48 | } catch(std::exception& e) { 49 | std::cout << "Warning: Could not create identity: " << e.what() << "\n"; 50 | std::cout << "Discriminant: " << D.impl << "\n"; 51 | std::cout << "Segment start:" << segm.start << "\n"; 52 | std::cout << "Segment length:" << segm.length << "\n"; 53 | std::cout << std::flush; 54 | 55 | return ; 56 | } 57 | uint64_t k1 = k / 2; 58 | uint64_t k0 = k - k1; 59 | form x = id; 60 | 61 | for (int64_t j = l - 1; j >= 0; j--) { 62 | x = FastPowFormNucomp(x, D, integer(1 << k), L, reducer); 63 | 64 | std::vector ys((1 << k)); 65 | for (uint64_t i = 0; i < (1UL << k); i++) 66 | ys[i] = id; 67 | 68 | form *tmp; 69 | uint64_t limit = num_iterations / (k * l); 70 | if (num_iterations % (k * l)) 71 | limit++; 72 | for (uint64_t i = 0; i < limit; i++) { 73 | if (num_iterations >= k * (i * l + j + 1)) { 74 | uint64_t b = GetBlock(i*l + j, k, num_iterations, B); 75 | if (!PerformExtraStep()) return; 76 | tmp = GetForm(i); 77 | nucomp_form(ys[b], ys[b], *tmp, D, L); 78 | } 79 | } 80 | 81 | for (uint64_t b1 = 0; b1 < (1UL << k1); b1++) { 82 | form z = id; 83 | for (uint64_t b0 = 0; b0 < (1UL << k0); b0++) { 84 | if (!PerformExtraStep()) return; 85 | nucomp_form(z, z, ys[b1 * (1 << k0) + b0], D, L); 86 | } 87 | z = FastPowFormNucomp(z, D, integer(b1 * (1 << k0)), L, reducer); 88 | nucomp_form(x, x, z, D, L); 89 | } 90 | 91 | for (uint64_t b0 = 0; b0 < (1UL << k0); b0++) { 92 | form z = id; 93 | for (uint64_t b1 = 0; b1 < (1UL << k1); b1++) { 94 | if (!PerformExtraStep()) return; 95 | nucomp_form(z, z, ys[b1 * (1 << k0) + b0], D, L); 96 | } 97 | z = FastPowFormNucomp(z, D, integer(b0), L, reducer); 98 | nucomp_form(x, x, z, D, L); 99 | } 100 | } 101 | reducer.reduce(x); 102 | proof = x; 103 | OnFinish(); 104 | } 105 | 106 | protected: 107 | Segment segm; 108 | integer D; 109 | form proof; 110 | uint64_t num_iterations; 111 | uint32_t k; 112 | uint32_t l; 113 | std::atomic is_finished; 114 | }; 115 | #endif // PROVER_BASE_H 116 | -------------------------------------------------------------------------------- /src/prover_parallel.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROVER_PARALLEL_H 2 | #define PROVER_PARALLEL_H 3 | 4 | #include 5 | 6 | #include "proof_common.h" 7 | #include "util.h" 8 | 9 | #define PARALLEL_PROVER_N_THREADS 2 10 | 11 | class ParallelProver : public Prover { 12 | private: 13 | static void ProofThread(ParallelProver *prover, uint8_t thr_idx, uint32_t start, uint32_t len) { 14 | prover->ProvePart(thr_idx, start, len); 15 | } 16 | 17 | void SquareFormN(form &f, uint64_t cnt, PulmarkReducer &reducer) 18 | { 19 | for (uint64_t i = 0; i < cnt; i++) { 20 | nudupl_form(f, f, D, L); 21 | reducer.reduce(f); 22 | } 23 | } 24 | 25 | void ProvePart(uint8_t thr_idx, uint32_t start, uint32_t len) { 26 | PulmarkReducer reducer; 27 | 28 | uint64_t k1 = k / 2; 29 | uint64_t k0 = k - k1; 30 | form x = id; 31 | int64_t end = start - len; 32 | int64_t j; 33 | 34 | for (j = start - 1; j >= end; j--) { 35 | x = FastPowFormNucomp(x, D, integer(1 << k), L, reducer); 36 | 37 | std::vector ys((1 << k)); 38 | for (uint64_t i = 0; i < (1UL << k); i++) 39 | ys[i] = id; 40 | 41 | form *tmp; 42 | uint64_t limit = num_iterations / (k * l); 43 | if (num_iterations % (k * l)) 44 | limit++; 45 | for (uint64_t i = 0; i < limit; i++) { 46 | if (num_iterations >= k * (i * l + j + 1)) { 47 | uint64_t b = GetBlock(i*l + j, k, num_iterations, B); 48 | if (!PerformExtraStep()) return; 49 | tmp = GetForm(i); 50 | nucomp_form(ys[b], ys[b], *tmp, D, L); 51 | } 52 | } 53 | 54 | for (uint64_t b1 = 0; b1 < (1UL << k1); b1++) { 55 | form z = id; 56 | for (uint64_t b0 = 0; b0 < (1UL << k0); b0++) { 57 | if (!PerformExtraStep()) return; 58 | nucomp_form(z, z, ys[b1 * (1 << k0) + b0], D, L); 59 | } 60 | z = FastPowFormNucomp(z, D, integer(b1 * (1 << k0)), L, reducer); 61 | nucomp_form(x, x, z, D, L); 62 | } 63 | 64 | for (uint64_t b0 = 0; b0 < (1UL << k0); b0++) { 65 | form z = id; 66 | for (uint64_t b1 = 0; b1 < (1UL << k1); b1++) { 67 | if (!PerformExtraStep()) return; 68 | nucomp_form(z, z, ys[b1 * (1 << k0) + b0], D, L); 69 | } 70 | z = FastPowFormNucomp(z, D, integer(b0), L, reducer); 71 | nucomp_form(x, x, z, D, L); 72 | } 73 | } 74 | 75 | SquareFormN(x, end * k, reducer); 76 | x_vals[thr_idx] = x; 77 | } 78 | public: 79 | ParallelProver(Segment segm, integer D) : Prover(segm, D) {} 80 | void GenerateProof(); 81 | 82 | protected: 83 | integer B; 84 | integer L; 85 | form id; 86 | form x_vals[PARALLEL_PROVER_N_THREADS]; 87 | }; 88 | 89 | void ParallelProver::GenerateProof() { 90 | PulmarkReducer reducer; 91 | 92 | this->B = GetB(D, segm.x, segm.y); 93 | this->L = root(-D, 4); 94 | this->id = form::identity(D); 95 | 96 | uint32_t l0 = l / 2; 97 | uint32_t l1 = l - l0; 98 | std::thread proof_thr(ParallelProver::ProofThread, this, 0, l, l0); 99 | ProvePart(1, l1, l1); 100 | 101 | proof_thr.join(); 102 | if (!PerformExtraStep()) { 103 | return; 104 | } 105 | nucomp_form(proof, x_vals[0], x_vals[1], D, L); 106 | reducer.reduce(proof); 107 | OnFinish(); 108 | } 109 | 110 | #endif // PROVER_PARALLEL_H 111 | -------------------------------------------------------------------------------- /src/prover_slow.h: -------------------------------------------------------------------------------- 1 | #include "include.h" 2 | #include "create_discriminant.h" 3 | #include "integer_common.h" 4 | #include "vdf_new.h" 5 | #include "nucomp.h" 6 | #include "picosha2.h" 7 | #include "proof_common.h" 8 | #include 9 | 10 | 11 | // TODO: Refactor to use 'Prover' class once new_vdf is merged in. 12 | 13 | void ApproximateParameters(uint64_t T, int& l, int& k) { 14 | double log_memory = 23.25349666; 15 | double log_T = log2(T); 16 | l = 1; 17 | if (log_T - log_memory > 0.000001) { 18 | l = ceil(pow(2, log_memory - 20)); 19 | } 20 | double intermediate = T * (double)0.6931471 / (2.0 * l); 21 | k = std::max(std::round(log(intermediate) - log(log(intermediate)) + 0.25), 1.0); 22 | } 23 | 24 | uint64_t GetBlock(uint64_t i, uint64_t k, uint64_t T, integer& B) { 25 | integer res = FastPow(2, T - k * (i + 1), B); 26 | mpz_mul_2exp(res.impl, res.impl, k); 27 | res = res / B; 28 | auto res_vector = res.to_vector(); 29 | // 0 value results in empty vector from mpz_export 30 | // https://gmplib.org/list-archives/gmp-bugs/2009-July/001534.html 31 | return res_vector.empty() ? 0 : res_vector[0]; 32 | } 33 | 34 | form GenerateWesolowski(form &y, form &x_init, 35 | integer &D, PulmarkReducer& reducer, 36 | std::vector const& intermediates, 37 | uint64_t num_iterations, 38 | uint64_t k, uint64_t l) { 39 | integer B = GetB(D, x_init, y); 40 | integer L=root(-D, 4); 41 | 42 | uint64_t k1 = k / 2; 43 | uint64_t k0 = k - k1; 44 | assert(k > 0); 45 | assert(l > 0); 46 | 47 | form x = form::identity(D); 48 | 49 | for (int64_t j = l - 1; j >= 0; j--) { 50 | x = FastPowFormNucomp(x, D, integer(1 << k), L, reducer); 51 | 52 | std::vector ys((1ULL << k)); 53 | for (uint64_t i = 0; i < (1ULL << k); i++) 54 | ys[i] = form::identity(D); 55 | 56 | for (uint64_t i = 0; i < (num_iterations + k * l - 1) / (k * l); i++) { 57 | if (num_iterations >= k * (i * l + j + 1)) { 58 | uint64_t b = GetBlock(i*l + j, k, num_iterations, B); 59 | nucomp_form(ys[b], ys[b], intermediates[i], D, L); 60 | } 61 | } 62 | for (uint64_t b1 = 0; b1 < (1ULL << k1); b1++) { 63 | form z = form::identity(D); 64 | for (uint64_t b0 = 0; b0 < (1ULL << k0); b0++) { 65 | nucomp_form(z, z, ys[b1 * (1ULL << k0) + b0], D, L); 66 | } 67 | z = FastPowFormNucomp(z, D, integer(b1 * (1 << k0)), L, reducer); 68 | nucomp_form(x, x, z, D, L); 69 | } 70 | for (uint64_t b0 = 0; b0 < (1ULL << k0); b0++) { 71 | form z = form::identity(D); 72 | for (uint64_t b1 = 0; b1 < (1ULL << k1); b1++) { 73 | nucomp_form(z, z, ys[b1 * (1ULL << k0) + b0], D, L); 74 | } 75 | z = FastPowFormNucomp(z, D, integer(b0), L, reducer); 76 | nucomp_form(x, x, z, D, L); 77 | } 78 | } 79 | 80 | reducer.reduce(x); 81 | return x; 82 | } 83 | 84 | std::vector ProveSlow(integer& D, form& x, uint64_t num_iterations, std::string shutdown_file_path) { 85 | integer L = root(-D, 4); 86 | PulmarkReducer reducer; 87 | form y = form::from_abd(x.a, x.b, D); 88 | int d_bits = D.num_bits(); 89 | 90 | int k, l; 91 | ApproximateParameters(num_iterations, l, k); 92 | if (k <= 0) k = 1; 93 | if (l <= 0) l = 1; 94 | int const kl = k * l; 95 | 96 | uint64_t const size_vec = (num_iterations + kl - 1) / kl; 97 | std::vector intermediates(size_vec); 98 | form* cursor = intermediates.data(); 99 | for (uint64_t i = 0; i < num_iterations; i++) { 100 | if (i % kl == 0) { 101 | *cursor = y; 102 | ++cursor; 103 | } 104 | nudupl_form(y, y, D, L); 105 | reducer.reduce(y); 106 | 107 | // Check for cancellation every 65535 interations 108 | if ((i&0xffff)==0) { 109 | // Only if we have a shutdown path 110 | if (shutdown_file_path!="") { 111 | struct stat buffer; 112 | 113 | int statrst = stat(shutdown_file_path.c_str(), &buffer); 114 | if ((statrst != 0) && (errno != EINTR)) { 115 | // shutdown file doesn't exist, abort out 116 | return {}; 117 | } 118 | } 119 | } 120 | } 121 | 122 | form proof = GenerateWesolowski(y, x, D, reducer, intermediates, num_iterations, k, l); 123 | std::vector result = SerializeForm(y, d_bits); 124 | std::vector proof_bytes = SerializeForm(proof, d_bits); 125 | result.insert(result.end(), proof_bytes.begin(), proof_bytes.end()); 126 | return result; 127 | } 128 | -------------------------------------------------------------------------------- /src/prover_test.cpp: -------------------------------------------------------------------------------- 1 | #include "vdf.h" 2 | #include "verifier.h" 3 | #include "create_discriminant.h" 4 | #include 5 | 6 | int segments = 7; 7 | int thread_count = 3; 8 | std::atomic stop_signal{false}; 9 | 10 | Proof CreateProof(integer D, ProverManager& pm, uint64_t iteration) { 11 | Proof proof = pm.Prove(iteration); 12 | if (!stop_signal) { 13 | form x = form::generator(D); 14 | std::vector bytes; 15 | bytes.insert(bytes.end(), proof.y.begin(), proof.y.end()); 16 | bytes.insert(bytes.end(), proof.proof.begin(), proof.proof.end()); 17 | if (CheckProofOfTimeNWesolowski(D, DEFAULT_ELEMENT, bytes.data(), bytes.size(), iteration, 1024, proof.witness_type)) { 18 | std::cout << "Correct proof"; 19 | } else { 20 | std::cout << "Incorrect proof\n"; 21 | std::exit(1); 22 | } 23 | std::cout << " (iteration: " << iteration << ").\n"; 24 | } 25 | return proof; 26 | } 27 | 28 | int gcd_base_bits=50; 29 | int gcd_128_max_iter=3; 30 | 31 | int main() { 32 | assert(is_vdf_test); //assertions should be disabled in VDF_MODE==0 33 | init_gmp(); 34 | if(hasAVX2()) 35 | { 36 | gcd_base_bits=63; 37 | gcd_128_max_iter=2; 38 | } 39 | std::vector challenge_hash({0, 0, 1, 2, 3, 3, 4, 4}); 40 | integer D = CreateDiscriminant(challenge_hash, 1024); 41 | 42 | if (getenv( "warn_on_corruption_in_production" )!=nullptr) { 43 | warn_on_corruption_in_production=true; 44 | } 45 | set_rounding_mode(); 46 | 47 | integer L=root(-D, 4); 48 | form f=form::generator(D); 49 | bool multi_proc_machine = (std::thread::hardware_concurrency() >= 16) ? true : false; 50 | 51 | WesolowskiCallback* weso = new FastAlgorithmCallback(segments, D, f, multi_proc_machine); 52 | std::cout << "Discriminant: " << D.impl << "\n"; 53 | std::atomic stopped = false; 54 | fast_algorithm = true; 55 | FastStorage* fast_storage = NULL; 56 | if (multi_proc_machine) { 57 | fast_storage = new FastStorage((FastAlgorithmCallback*)weso); 58 | } 59 | std::thread vdf_worker(repeated_square, 0, f, D, L, weso, fast_storage, std::ref(stopped)); 60 | ProverManager pm(D, (FastAlgorithmCallback*)weso, fast_storage, segments, thread_count); 61 | pm.start(); 62 | std::vector threads; 63 | for (int i = 0; i <= 30; i++) { 64 | threads.emplace_back(CreateProof, D, std::ref(pm), (1 << 21) * i + 60000); 65 | } 66 | std::this_thread::sleep_for (std::chrono::seconds(300)); 67 | stop_signal = true; 68 | std::cout << "Stopping everything.\n"; 69 | pm.stop(); 70 | stopped = true; 71 | for (auto& t : threads) t.join(); 72 | vdf_worker.join(); 73 | if (fast_storage != NULL) 74 | delete(fast_storage); 75 | delete(weso); 76 | } 77 | -------------------------------------------------------------------------------- /src/provers.h: -------------------------------------------------------------------------------- 1 | #ifndef PROVERS_H 2 | #define PROVERS_H 3 | 4 | #include "prover_base.hpp" 5 | #include "callback.h" 6 | 7 | class OneWesolowskiProver : public Prover { 8 | public: 9 | OneWesolowskiProver(Segment segm, integer D, form* intermediates, std::atomic& stop_signal) 10 | : Prover(segm, D), stop_signal(stop_signal) 11 | { 12 | this->intermediates = intermediates; 13 | if (num_iterations >= (1 << 16)) { 14 | ApproximateParameters(num_iterations, l, k); 15 | } else { 16 | k = 10; 17 | l = 1; 18 | } 19 | } 20 | 21 | form* GetForm(uint64_t iteration) { 22 | return &intermediates[iteration]; 23 | } 24 | 25 | void start() { 26 | GenerateProof(); 27 | } 28 | 29 | void stop() { 30 | } 31 | 32 | bool PerformExtraStep() { 33 | return !stop_signal; 34 | } 35 | 36 | void OnFinish() { 37 | is_finished = true; 38 | } 39 | 40 | private: 41 | form* intermediates; 42 | std::atomic& stop_signal; 43 | }; 44 | 45 | class TwoWesolowskiProver : public Prover{ 46 | public: 47 | TwoWesolowskiProver(Segment segm, integer D, TwoWesolowskiCallback* weso, std::atomic& stop_signal): 48 | Prover(segm, D), stop_signal(stop_signal) 49 | { 50 | this->weso = weso; 51 | this->done_iterations = segm.start; 52 | k = 10; 53 | l = (segm.length < 10000000) ? 1 : 10; 54 | } 55 | 56 | void start() { 57 | std::thread t([=] { GenerateProof(); }); 58 | t.detach(); 59 | } 60 | 61 | virtual form* GetForm(uint64_t i) { 62 | return weso->GetForm(done_iterations + i * k * l); 63 | } 64 | 65 | void stop() { 66 | } 67 | 68 | bool PerformExtraStep() { 69 | return !stop_signal; 70 | } 71 | 72 | void OnFinish() { 73 | is_finished = true; 74 | } 75 | 76 | private: 77 | TwoWesolowskiCallback* weso; 78 | std::atomic& stop_signal; 79 | uint64_t done_iterations; 80 | }; 81 | 82 | extern bool new_event; 83 | extern std::mutex new_event_mutex; 84 | extern std::condition_variable new_event_cv; 85 | 86 | class InterruptableProver: public Prover { 87 | public: 88 | InterruptableProver(Segment segm, integer D, FastAlgorithmCallback* weso) : Prover(segm, D) { 89 | this->weso = weso; 90 | this->done_iterations = segm.start; 91 | this->bucket = segm.GetSegmentBucket(); 92 | if (segm.length <= (1 << 16)) 93 | k = 10; 94 | else 95 | k = 12; 96 | if (segm.length <= (1 << 18)) 97 | l = 1; 98 | else 99 | l = (segm.length >> 18); 100 | is_paused = false; 101 | is_fully_finished = false; 102 | joined = false; 103 | } 104 | 105 | ~InterruptableProver() { 106 | if (!joined) { 107 | th->join(); 108 | } 109 | delete(th); 110 | } 111 | 112 | form* GetForm(uint64_t i) { 113 | return weso->GetForm(done_iterations + i * k * l, bucket); 114 | } 115 | 116 | void start() { 117 | th = new std::thread([=] { GenerateProof(); }); 118 | } 119 | 120 | void stop() { 121 | { 122 | std::lock_guard lk(m); 123 | is_finished = true; 124 | is_fully_finished = true; 125 | if (is_paused) { 126 | is_paused = false; 127 | } 128 | } 129 | cv.notify_one(); 130 | th->join(); 131 | joined = true; 132 | } 133 | 134 | bool PerformExtraStep() { 135 | if (is_finished) { 136 | return false; 137 | } 138 | std::unique_lock lk(m); 139 | cv.wait(lk, [&] { 140 | return !is_paused; 141 | }); 142 | return true; 143 | } 144 | 145 | void pause() { 146 | std::lock_guard lk(m); 147 | is_paused = true; 148 | } 149 | 150 | void resume() { 151 | { 152 | std::lock_guard lk(m); 153 | is_paused = false; 154 | } 155 | cv.notify_one(); 156 | } 157 | 158 | bool IsRunning() { 159 | return !is_paused; 160 | } 161 | 162 | bool IsFullyFinished() { 163 | return is_fully_finished; 164 | } 165 | 166 | void OnFinish() { 167 | is_finished = true; 168 | if (!is_fully_finished) { 169 | // Notify event loop a proving thread is free. 170 | { 171 | std::lock_guard lk(new_event_mutex); 172 | new_event = true; 173 | } 174 | new_event_cv.notify_one(); 175 | is_fully_finished = true; 176 | } 177 | } 178 | 179 | private: 180 | std::thread* th; 181 | FastAlgorithmCallback* weso; 182 | std::condition_variable cv; 183 | std::mutex m; 184 | bool is_paused; 185 | std::atomic is_fully_finished; 186 | bool joined; 187 | uint64_t done_iterations; 188 | int bucket; 189 | }; 190 | 191 | #endif // PROVERS_H 192 | -------------------------------------------------------------------------------- /src/refcode/README.md: -------------------------------------------------------------------------------- 1 | This is a repository of reference code for various things that I don't ever want to have to figure out ever 2 | again. Each file is a sample program for performing a specific task. The idea is to just use the code as a 3 | reference, or as the starting point for a custom implementation. 4 | 5 | Consider all of the code in this repository a choice public domain or MIT-0 (No Attribution). 6 | 7 | 8 | License 9 | ======= 10 | This software is available as a choice of the following licenses. Choose 11 | whichever you prefer. 12 | 13 | ALTERNATIVE 1 - Public Domain (www.unlicense.org) 14 | ------------------------------------------------- 15 | This is free and unencumbered software released into the public domain. 16 | 17 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 18 | software, either in source code form or as a compiled binary, for any purpose, 19 | commercial or non-commercial, and by any means. 20 | 21 | In jurisdictions that recognize copyright laws, the author or authors of this 22 | software dedicate any and all copyright interest in the software to the public 23 | domain. We make this dedication for the benefit of the public at large and to 24 | the detriment of our heirs and successors. We intend this dedication to be an 25 | overt act of relinquishment in perpetuity of all present and future rights to 26 | this software under copyright law. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 32 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 33 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | 35 | For more information, please refer to 36 | 37 | ALTERNATIVE 2 - MIT No Attribution 38 | ---------------------------------- 39 | Copyright 2020 David Reid 40 | 41 | Permission is hereby granted, free of charge, to any person obtaining a copy of 42 | this software and associated documentation files (the "Software"), to deal in 43 | the Software without restriction, including without limitation the rights to 44 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 45 | of the Software, and to permit persons to whom the Software is furnished to do 46 | so. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 49 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 50 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 51 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 52 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 53 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 54 | SOFTWARE. -------------------------------------------------------------------------------- /src/stress_test.cpp: -------------------------------------------------------------------------------- 1 | #include "verifier.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | std::vector HexToBytes(const char *hex_proof) { 8 | int len = strlen(hex_proof); 9 | assert(len % 2 == 0); 10 | std::vector result; 11 | for (int i = 0; i < len; i += 2) 12 | { 13 | int hex1 = hex_proof[i] >= 'a' ? (hex_proof[i] - 'a' + 10) : (hex_proof[i] - '0'); 14 | int hex2 = hex_proof[i + 1] >= 'a' ? (hex_proof[i + 1] - 'a' + 10) : (hex_proof[i + 1] - '0'); 15 | result.push_back(hex1 * 16 + hex2); 16 | } 17 | return result; 18 | } 19 | 20 | struct job 21 | { 22 | std::vector challengebytes; 23 | std::vector inputbytes; 24 | std::vector outputbytes; 25 | uint64 number_of_iterations; 26 | uint32 discriminant_size; 27 | uint8 witness_type; 28 | }; 29 | 30 | void doit(int thread, std::vector const& jobs) 31 | { 32 | int cnt = 0; 33 | for (job const& j : jobs) 34 | { 35 | bool const is_valid = CreateDiscriminantAndCheckProofOfTimeNWesolowski( 36 | j.challengebytes, 37 | j.discriminant_size, 38 | j.inputbytes.data(), 39 | j.outputbytes.data(), 40 | j.outputbytes.size(), 41 | j.number_of_iterations, 42 | j.witness_type); 43 | if (!is_valid) { 44 | printf("thread %d cnt %d is valid %d %llu %d\n", 45 | thread, 46 | cnt, 47 | is_valid, 48 | j.number_of_iterations, 49 | j.witness_type); 50 | std::terminate(); 51 | } 52 | cnt++; 53 | } 54 | } 55 | 56 | int main() 57 | { 58 | std::ifstream infile("vdf.txt"); 59 | 60 | std::string challenge; 61 | std::string discriminant_size; 62 | std::string input_el; 63 | std::string output; 64 | std::string number_of_iterations; 65 | std::string witness_type; 66 | 67 | std::vector jobs; 68 | 69 | while (true) { 70 | std::getline(infile, challenge); 71 | if (infile.eof()) 72 | break; 73 | std::getline(infile, discriminant_size); 74 | std::getline(infile, input_el); 75 | std::getline(infile, output); 76 | std::getline(infile, number_of_iterations); 77 | std::getline(infile, witness_type); 78 | 79 | std::vector challengebytes=HexToBytes(challenge.c_str()); 80 | std::vector inputbytes=HexToBytes(input_el.c_str()); 81 | std::vector outputbytes=HexToBytes(output.c_str()); 82 | 83 | char *endptr; 84 | 85 | uint64 noi=strtoll(number_of_iterations.c_str(),&endptr,10); 86 | if (errno == ERANGE) std::terminate(); 87 | uint32 ds=strtoll(discriminant_size.c_str(),&endptr,10); 88 | if (errno == ERANGE) std::terminate(); 89 | uint8 wt=strtoll(witness_type.c_str(),&endptr,10); 90 | if (errno == ERANGE) std::terminate(); 91 | 92 | jobs.push_back({challengebytes, inputbytes, outputbytes, noi, ds, wt}); 93 | } 94 | 95 | std::vector threads; 96 | for (int i = 0; i < 20; ++i) 97 | threads.emplace_back(doit, i, std::ref(jobs)); 98 | 99 | for (auto& t : threads) 100 | t.join(); 101 | 102 | return 0; 103 | } 104 | -------------------------------------------------------------------------------- /src/uint128_t/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 - 2017 Jason Lee @ calccrypto at gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/uint128_t/README.md: -------------------------------------------------------------------------------- 1 | # uint128_t 2 | 3 | An unsigned 128 bit integer type for C++ 4 | 5 | Copyright (c) 2013 - 2018 Jason Lee @ calccrypto at gmail.com 6 | 7 | Please see LICENSE file for license. 8 | 9 | [![Build Status](https://travis-ci.org/calccrypto/uint128_t.svg?branch=master)](https://travis-ci.org/calccrypto/uint128_t) 10 | 11 | ## Acknowledgements 12 | With much help from Auston Sterling 13 | 14 | Thanks to Stefan Deigmüller for finding 15 | a bug in operator*. 16 | 17 | Thanks to François Dessenne for convincing me 18 | to do a general rewrite of this class. 19 | 20 | Thanks to John Skaller for making symbols visible 21 | when compiling as a shared library. This was originally 22 | done in `uint256_t`, which I copied into here. 23 | 24 | ## Usage 25 | This is simple implementation of an unsigned 128 bit 26 | integer type in C++. It's meant to be used like a standard 27 | `uintX_t`, except with a larger bit size than those provided 28 | by C/C++. 29 | 30 | ### In Code 31 | All that needs to be done in code is `#include "uint128_t.h"` 32 | 33 | ```c++ 34 | #include 35 | #include "uint128_t.h" 36 | 37 | int main() { 38 | uint128_t a = 1; 39 | uint128_t b = 2; 40 | std::cout << (a | b) << std::endl; 41 | return 0; 42 | } 43 | ``` 44 | 45 | ### Compilation 46 | A C++ compiler supporting at least C++11 is required. 47 | 48 | Compilation can be done by directly including `uint128_t.cpp` in your compile command, e.g. `g++ -std=c++11 main.cpp uint128_t.cpp`, or other ways, such as linking the `uint128_t.o` file, or creating a library, and linking the library in. 49 | -------------------------------------------------------------------------------- /src/uint128_t/uint128_t.build: -------------------------------------------------------------------------------- 1 | // IMPLEMENTATION BUILD HEADER 2 | #ifndef _UINT128_T_BUILD 3 | #define _UINT128_T_BUILD 4 | #include "uint128_t_config.include" 5 | #define UINT128_T_EXTERN _UINT128_T_EXPORT 6 | #endif 7 | #include "uint128_t.include" 8 | 9 | -------------------------------------------------------------------------------- /src/uint128_t/uint128_t.h: -------------------------------------------------------------------------------- 1 | // PUBLIC IMPORT HEADER 2 | #ifndef _UINT128_H_ 3 | #define _UINT128_H_ 4 | #include "uint128_t_config.include" 5 | #define UINT128_T_EXTERN _UINT128_T_IMPORT 6 | #include "uint128_t.include" 7 | #endif 8 | 9 | -------------------------------------------------------------------------------- /src/uint128_t/uint128_t.include: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chia-Network/chiavdf/e2e30791d67c15390654b6bbbefff0f28526ab2e/src/uint128_t/uint128_t.include -------------------------------------------------------------------------------- /src/uint128_t/uint128_t_config.include: -------------------------------------------------------------------------------- 1 | #ifndef _UINT128_T_CONFIG_ 2 | #define _UINT128_T_CONFIG_ 3 | #if defined(_MSC_VER) 4 | #if defined(_DLL) 5 | #define _UINT128_T_EXPORT __declspec(dllexport) 6 | #define _UINT128_T_IMPORT __declspec(dllimport) 7 | #else 8 | #define _UINT128_T_EXPORT 9 | #define _UINT128_T_IMPORT 10 | #endif 11 | #else 12 | // All modules on Unix are compiled with -fvisibility=hidden 13 | // All API symbols get visibility default 14 | // whether or not we're static linking or dynamic linking (with -fPIC) 15 | #define _UINT128_T_EXPORT __attribute__((visibility("default"))) 16 | #define _UINT128_T_IMPORT __attribute__((visibility("default"))) 17 | #endif 18 | #endif 19 | 20 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #include "vdf_new.h" 5 | 6 | /* Platform-specific byte swap macros. */ 7 | #if defined(_WIN32) 8 | #include 9 | 10 | #define bswap_16(x) _byteswap_ushort(x) 11 | #define bswap_32(x) _byteswap_ulong(x) 12 | #define bswap_64(x) _byteswap_uint64(x) 13 | #elif defined(__APPLE__) 14 | 15 | #include 16 | 17 | #define bswap_16(x) OSSwapInt16(x) 18 | #define bswap_32(x) OSSwapInt32(x) 19 | #define bswap_64(x) OSSwapInt64(x) 20 | #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) 21 | # if defined(__OpenBSD__) 22 | # include 23 | # define bswap_16(x) swap16(x) 24 | # define bswap_32(x) swap32(x) 25 | # define bswap_64(x) swap64(x) 26 | # else 27 | # include 28 | # define bswap_16(x) bswap16(x) 29 | # define bswap_32(x) bswap32(x) 30 | # define bswap_64(x) bswap64(x) 31 | # endif 32 | #else 33 | #include 34 | #endif 35 | 36 | 37 | struct Segment { 38 | uint64_t start; 39 | uint64_t length; 40 | form x; 41 | form y; 42 | form proof; 43 | bool is_empty; 44 | 45 | Segment() { 46 | is_empty = true; 47 | } 48 | 49 | Segment(uint64_t start, uint64_t length, form& x, form& y) { 50 | this->start = start; 51 | this->length = length; 52 | this->x = x; 53 | this->y = y; 54 | is_empty = false; 55 | } 56 | 57 | bool IsWorseThan(Segment& other) { 58 | if (is_empty) { 59 | if (!other.is_empty) 60 | return true; 61 | return false; 62 | } 63 | if (length > other.length) 64 | return true; 65 | if (length < other.length) 66 | return false; 67 | return start > other.start; 68 | } 69 | 70 | int GetSegmentBucket() { 71 | uint64_t c_length = length; 72 | length >>= 16; 73 | int index = 0; 74 | while (length > 1) { 75 | index++; 76 | if (length == 2 || length == 3) { 77 | std::cout << "Warning: Invalid segment length.\n"; 78 | } 79 | length >>= 2; 80 | } 81 | length = c_length; 82 | return index; 83 | } 84 | }; 85 | 86 | void Int64ToBytes(uint8_t *result, uint64_t input) 87 | { 88 | uint64_t r = bswap_64(input); 89 | memcpy(result, &r, sizeof(r)); 90 | } 91 | 92 | void Int32ToBytes(uint8_t *result, uint32_t input) 93 | { 94 | uint32_t r = bswap_32(input); 95 | memcpy(result, &r, sizeof(r)); 96 | } 97 | 98 | uint64_t BytesToInt64(const uint8_t *bytes) 99 | { 100 | uint64_t i; 101 | memcpy(&i, bytes, sizeof(i)); 102 | return bswap_64(i); 103 | } 104 | 105 | template 106 | void VectorAppend(std::vector &dst, const std::vector &src) 107 | { 108 | dst.insert(dst.end(), src.begin(), src.end()); 109 | } 110 | 111 | template 112 | void VectorAppendArray(std::vector &dst, const T *src, size_t len) 113 | { 114 | dst.insert(dst.end(), src, src + len); 115 | } 116 | 117 | std::string BytesToStr(const std::vector &in) 118 | { 119 | std::vector::const_iterator from = in.cbegin(); 120 | std::vector::const_iterator to = in.cend(); 121 | std::ostringstream oss; 122 | for (; from != to; ++from) 123 | oss << std::hex << std::setw(2) << std::setfill('0') << static_cast(*from); 124 | return oss.str(); 125 | } 126 | 127 | void ApproximateParameters(uint64_t T, uint32_t& L, uint32_t& K) { 128 | double log_memory = 23.25349666; 129 | double log_T = log2(T); 130 | L = 1; 131 | if (log_T - log_memory > 0.000001) { 132 | L = ceil(pow(2, log_memory - 20)); 133 | } 134 | double intermediate = T * (double)0.6931471 / (2.0 * L); 135 | K = std::max(std::round(log(intermediate) - log(log(intermediate)) + 0.25), 1.0); 136 | } 137 | 138 | struct Proof { 139 | Proof() { 140 | 141 | } 142 | 143 | Proof(std::vector y, std::vector proof) { 144 | this->y = y; 145 | this->proof = proof; 146 | } 147 | 148 | string hex() { 149 | std::vector bytes(y); 150 | bytes.insert(bytes.end(), proof.begin(), proof.end()); 151 | return BytesToStr(bytes); 152 | } 153 | 154 | std::vector y; 155 | std::vector proof; 156 | uint8_t witness_type; 157 | }; 158 | 159 | #endif // UTIL_H 160 | -------------------------------------------------------------------------------- /src/vdf_base.cpp: -------------------------------------------------------------------------------- 1 | #include "verifier.h" 2 | #include "prover_slow.h" 3 | #include "alloc.hpp" 4 | #include "prover_base.hpp" 5 | #include "prover_parallel.hpp" 6 | 7 | void VdfBaseInit(void) 8 | { 9 | init_gmp(); 10 | fesetround(FE_TOWARDZERO); 11 | } 12 | 13 | bool dummy_form_check_valid(form &f, integer &d) 14 | { 15 | return f.check_valid(d); 16 | } 17 | 18 | form dummy_get_proof(ParallelProver &p) 19 | { 20 | p.GenerateProof(); 21 | return p.GetProof(); 22 | } 23 | -------------------------------------------------------------------------------- /src/vdf_bench.cpp: -------------------------------------------------------------------------------- 1 | #include "include.h" 2 | #include "bit_manipulation.h" 3 | #include "double_utility.h" 4 | #include "parameters.h" 5 | #include "asm_main.h" 6 | #include "integer.h" 7 | #include "vdf_new.h" 8 | #include "nucomp.h" 9 | #include "picosha2.h" 10 | #include "proof_common.h" 11 | 12 | #include "threading.h" 13 | #include "avx512_integer.h" 14 | #include "vdf_fast.h" 15 | #include "create_discriminant.h" 16 | 17 | #include 18 | 19 | #define CH_SIZE 32 20 | 21 | static void usage(const char *progname) 22 | { 23 | fprintf(stderr, "Usage: %s {square_asm|square|discr} N\n", progname); 24 | } 25 | 26 | int main(int argc, char **argv) 27 | { 28 | assert(is_vdf_test); //assertions should be disabled in VDF_MODE==0 29 | init_gmp(); 30 | set_rounding_mode(); 31 | 32 | if (argc < 3) { 33 | usage(argv[0]); 34 | return 1; 35 | } 36 | int iters = atoi(argv[2]); 37 | auto D = integer("-141140317794792668862943332656856519378482291428727287413318722089216448567155737094768903643716404517549715385664163360316296284155310058980984373770517398492951860161717960368874227473669336541818575166839209228684755811071416376384551902149780184532086881683576071479646499601330824259260645952517205526679"); 38 | 39 | form y = form::generator(D); 40 | integer L = root(-D, 4); 41 | int i, n_slow = 0; 42 | PulmarkReducer reducer; 43 | bool is_comp = true, is_asm = false; 44 | 45 | 46 | auto t1 = std::chrono::high_resolution_clock::now(); 47 | if (!strcmp(argv[1], "square_asm")) { 48 | is_asm = true; 49 | for (i = 0; i < iters; ) { 50 | square_state_type sq_state; 51 | sq_state.pairindex = 0; 52 | uint64_t done; 53 | 54 | done = repeated_square_fast(sq_state, y, D, L, 0, iters - i, NULL); 55 | if (!done) { 56 | nudupl_form(y, y, D, L); 57 | reducer.reduce(y); 58 | i++; 59 | n_slow++; 60 | } else if (done == ~0ULL) { 61 | printf("Fail\n"); 62 | break; 63 | } else { 64 | i += done; 65 | } 66 | } 67 | } else if (!strcmp(argv[1], "square")) { 68 | for (i = 0; i < iters; i++) { 69 | nudupl_form(y, y, D, L); 70 | if (__GMP_ABS(y.a.impl->_mp_size) > 8) { 71 | reducer.reduce(y); 72 | } 73 | } 74 | } else if (!strcmp(argv[1], "discr")) { 75 | uint8_t ch[CH_SIZE]; 76 | std::vector ch_vec; 77 | 78 | is_comp = false; 79 | for (i = 0; i < CH_SIZE; i++) { 80 | ch[i] = i; 81 | } 82 | 83 | ch_vec = std::vector(ch, ch + CH_SIZE); 84 | 85 | for (i = 0; i < iters; i++) { 86 | ch_vec[i % CH_SIZE] += 1; 87 | integer discr = CreateDiscriminant(ch_vec, 1024); 88 | } 89 | } else { 90 | fprintf(stderr, "Unknown command\n"); 91 | usage(argv[0]); 92 | return 1; 93 | } 94 | 95 | if (is_comp) 96 | reducer.reduce(y); 97 | 98 | auto t2 = std::chrono::high_resolution_clock::now(); 99 | int duration = std::chrono::duration_cast(t2 - t1).count(); 100 | if (!duration) { 101 | printf("WARNING: too few iterations, results will be inaccurate!\n"); 102 | duration = 1; 103 | } 104 | printf("Time: %d ms; ", duration); 105 | if (is_comp) { 106 | if (is_asm) 107 | printf("n_slow: %d; ", n_slow); 108 | 109 | printf("speed: %d.%dK ips\n", iters/duration, iters*10/duration % 10); 110 | printf("a = %s\n", y.a.to_string().c_str()); 111 | printf("b = %s\n", y.b.to_string().c_str()); 112 | printf("c = %s\n", y.c.to_string().c_str()); 113 | } else { 114 | printf("speed: %d.%d ms/discr\n", duration/iters, duration*10/iters % 10); 115 | } 116 | return 0; 117 | } 118 | -------------------------------------------------------------------------------- /src/xgcd_partial.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2012 William Hart 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | software and associated documentation files (the "Software"), to deal in the Software 6 | without restriction, including without limitation the rights to use, copy, modify, merge, 7 | publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons 8 | to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or 11 | substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 14 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 15 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 16 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 17 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 18 | DEALINGS IN THE SOFTWARE. 19 | 20 | MIT licensing permission obtained January 13, 2020 by Chia Network Inc. from William Hart 21 | */ 22 | 23 | #ifndef _XGCD_PARTIAL 24 | #define _XGCD_PARTIAL 25 | 26 | #include 27 | 28 | void mpz_xgcd_partial(mpz_t co2, mpz_t co1, 29 | mpz_t r2, mpz_t r1, const mpz_t L) 30 | { 31 | mpz_t q, r; 32 | mp_limb_signed_t aa2, aa1, bb2, bb1, rr1, rr2, qq, bb, t1, t2, t3, i; 33 | mp_limb_signed_t bits, bits1, bits2; 34 | 35 | mpz_init(q); mpz_init(r); 36 | 37 | mpz_set_ui(co2, 0); 38 | mpz_set_si(co1, -1); 39 | 40 | while (mpz_cmp_ui(r1, 0) && mpz_cmp(r1, L) > 0) 41 | { 42 | bits2 = mpz_sizeinbase(r2, 2); 43 | bits1 = mpz_sizeinbase(r1, 2); 44 | bits = __GMP_MAX(bits2, bits1) - GMP_LIMB_BITS + 1; 45 | if (bits < 0) bits = 0; 46 | 47 | mpz_tdiv_q_2exp(r, r2, bits); 48 | rr2 = mpz_get_ui(r); 49 | mpz_tdiv_q_2exp(r, r1, bits); 50 | rr1 = mpz_get_ui(r); 51 | mpz_tdiv_q_2exp(r, L, bits); 52 | bb = mpz_get_ui(r); 53 | 54 | aa2 = 0; aa1 = 1; 55 | bb2 = 1; bb1 = 0; 56 | 57 | for (i = 0; rr1 != 0 && rr1 > bb; i++) 58 | { 59 | qq = rr2 / rr1; 60 | 61 | t1 = rr2 - qq*rr1; 62 | t2 = aa2 - qq*aa1; 63 | t3 = bb2 - qq*bb1; 64 | 65 | if (i & 1) 66 | { 67 | if (t1 < -t3 || rr1 - t1 < t2 - aa1) break; 68 | } else 69 | { 70 | if (t1 < -t2 || rr1 - t1 < t3 - bb1) break; 71 | } 72 | 73 | rr2 = rr1; rr1 = t1; 74 | aa2 = aa1; aa1 = t2; 75 | bb2 = bb1; bb1 = t3; 76 | } 77 | 78 | if (i == 0) 79 | { 80 | mpz_fdiv_qr(q, r2, r2, r1); 81 | mpz_swap(r2, r1); 82 | 83 | mpz_submul(co2, co1, q); 84 | mpz_swap(co2, co1); 85 | } else 86 | { 87 | mpz_mul_si(r, r2, bb2); 88 | if (aa2 >= 0) 89 | mpz_addmul_ui(r, r1, aa2); 90 | else 91 | mpz_submul_ui(r, r1, -aa2); 92 | mpz_mul_si(r1, r1, aa1); 93 | if (bb1 >= 0) 94 | mpz_addmul_ui(r1, r2, bb1); 95 | else 96 | mpz_submul_ui(r1, r2, -bb1); 97 | mpz_set(r2, r); 98 | 99 | mpz_mul_si(r, co2, bb2); 100 | if (aa2 >= 0) 101 | mpz_addmul_ui(r, co1, aa2); 102 | else 103 | mpz_submul_ui(r, co1, -aa2); 104 | mpz_mul_si(co1, co1, aa1); 105 | if (bb1 >= 0) 106 | mpz_addmul_ui(co1, co2, bb1); 107 | else 108 | mpz_submul_ui(co1, co2, -bb1); 109 | mpz_set(co2, r); 110 | 111 | if (mpz_sgn(r1) < 0) { mpz_neg(co1, co1); mpz_neg(r1, r1); } 112 | if (mpz_sgn(r2) < 0) { mpz_neg(co2, co2); mpz_neg(r2, r2); } 113 | } 114 | } 115 | 116 | if (mpz_sgn(r2) < 0) 117 | { 118 | mpz_neg(co2, co2); mpz_neg(co1, co1); 119 | mpz_neg(r2, r2); 120 | } 121 | 122 | mpz_clear(q); mpz_clear(r); 123 | } 124 | #endif /* _XGCD_PARTIAL */ 125 | -------------------------------------------------------------------------------- /tests/test_n_weso_verifier.py: -------------------------------------------------------------------------------- 1 | import secrets 2 | 3 | from chiavdf import ( 4 | create_discriminant, 5 | prove, 6 | verify_wesolowski, 7 | verify_n_wesolowski, 8 | verify_n_wesolowski_with_b, 9 | get_b_from_n_wesolowski, 10 | ) 11 | 12 | 13 | def prove_n_weso(discriminant_challenge, x, discriminant_size, form_size, iters, witness, wrong_segm): 14 | iters_chunk = iters // (witness + 1) 15 | partials = [] 16 | discriminant = create_discriminant(discriminant_challenge, discriminant_size) 17 | for _ in range(witness): 18 | result = prove(discriminant_challenge, x, discriminant_size, iters_chunk, "") 19 | y = result[:form_size] 20 | proof = result[form_size : 2 * form_size] 21 | partials.append((x, y, proof)) 22 | x = y 23 | iters -= iters_chunk * witness 24 | result = prove(discriminant_challenge, x, discriminant_size, iters, "") 25 | y_result = result[:form_size] 26 | y_proof = result[form_size : 2 * form_size] 27 | assert verify_wesolowski(discriminant, x, y_result, y_proof, iters) 28 | b_hex = get_b_from_n_wesolowski(discriminant, x, y_result + y_proof, iters, 0) 29 | is_valid, y_from_compression = verify_n_wesolowski_with_b( 30 | discriminant, 31 | b_hex, 32 | x, 33 | y_proof, 34 | iters, 35 | 0, 36 | ) 37 | assert is_valid 38 | assert y_from_compression == y_result 39 | inner_proof = b"" 40 | for x, y, proof in reversed(partials): 41 | b_hex = get_b_from_n_wesolowski(discriminant, x, y + proof, iters_chunk, 0) 42 | b = int(b_hex, 16) 43 | assert verify_wesolowski(discriminant, x, y, proof, iters_chunk) 44 | is_valid, y_from_compression = verify_n_wesolowski_with_b( 45 | discriminant, 46 | b_hex, 47 | x, 48 | proof, 49 | iters_chunk, 50 | 0, 51 | ) 52 | assert is_valid 53 | assert y == y_from_compression 54 | if not wrong_segm: 55 | inner_proof += iters_chunk.to_bytes(8, byteorder='big') 56 | else: 57 | iters_wrong = iters_chunk + 1 58 | inner_proof += iters_wrong.to_bytes(8, byteorder='big') 59 | wrong_segm = False 60 | inner_proof += b.to_bytes(33, byteorder='big') 61 | inner_proof += proof 62 | return y_result, y_proof + inner_proof 63 | 64 | 65 | def test_prove_n_weso_and_verify(): 66 | discriminant_challenge = secrets.token_bytes(10) 67 | discriminant_size = 512 68 | discriminant = create_discriminant(discriminant_challenge, discriminant_size) 69 | print(f"discriminant_challenge {discriminant_challenge.hex()} discriminant {discriminant}") 70 | form_size = 100 71 | initial_el = b"\x08" + (b"\x00" * 99) 72 | 73 | for iters in [1000000, 5000000, 10000000]: 74 | y, proof = prove_n_weso(discriminant_challenge, initial_el, discriminant_size, form_size, iters, 5, False) 75 | is_valid = verify_n_wesolowski( 76 | str(discriminant), 77 | initial_el, 78 | y + proof, 79 | iters, 80 | discriminant_size, 81 | 5, 82 | ) 83 | assert is_valid 84 | is_valid = verify_n_wesolowski( 85 | str(discriminant), 86 | initial_el, 87 | y + proof, 88 | iters + 1, 89 | discriminant_size, 90 | 5, 91 | ) 92 | assert not is_valid 93 | y, proof_wrong = prove_n_weso(discriminant_challenge, initial_el, discriminant_size, form_size, iters, 10, True) 94 | is_valid = verify_n_wesolowski( 95 | str(discriminant), 96 | initial_el, 97 | y + proof_wrong, 98 | iters, 99 | discriminant_size, 100 | 10, 101 | ) 102 | assert not is_valid 103 | b_hex = get_b_from_n_wesolowski(discriminant, initial_el, y + proof, iters, 5) 104 | is_valid, y_from_compression = verify_n_wesolowski_with_b( 105 | discriminant, 106 | b_hex, 107 | initial_el, 108 | proof, 109 | iters, 110 | 5, 111 | ) 112 | assert is_valid 113 | assert y_from_compression == y 114 | B = str(int(b_hex, 16)) 115 | is_valid, y_from_compression = verify_n_wesolowski_with_b( 116 | discriminant, 117 | B, 118 | initial_el, 119 | proof, 120 | iters, 121 | 5, 122 | ) 123 | assert is_valid 124 | assert y_from_compression == y 125 | B_wrong = str(int(b_hex, 16) + 1) 126 | is_valid, y_from_compression = verify_n_wesolowski_with_b( 127 | discriminant, 128 | B_wrong, 129 | initial_el, 130 | proof, 131 | iters, 132 | 5, 133 | ) 134 | assert not is_valid 135 | assert y_from_compression == b"" 136 | is_valid, y_from_compression = verify_n_wesolowski_with_b( 137 | discriminant, 138 | B, 139 | initial_el, 140 | proof_wrong, 141 | iters, 142 | 10, 143 | ) 144 | assert not is_valid 145 | assert y_from_compression == b"" 146 | initial_el = y 147 | 148 | 149 | test_prove_n_weso_and_verify() 150 | -------------------------------------------------------------------------------- /tests/test_verifier.py: -------------------------------------------------------------------------------- 1 | import secrets 2 | import time 3 | 4 | from chiavdf import create_discriminant, prove, verify_wesolowski 5 | 6 | 7 | def test_prove_and_verify(): 8 | discriminant_challenge = secrets.token_bytes(10) 9 | discriminant_size = 512 10 | discriminant = create_discriminant(discriminant_challenge, discriminant_size) 11 | form_size = 100 12 | initial_el = b"\x08" + (b"\x00" * 99) 13 | 14 | iters = 1000000 15 | t1 = time.time() 16 | result = prove(discriminant_challenge, initial_el, discriminant_size, iters, "") 17 | t2 = time.time() 18 | print(f"IPS: {iters / (t2 - t1)}") 19 | result_y = result[:form_size] 20 | proof = result[form_size : 2 * form_size] 21 | 22 | is_valid = verify_wesolowski( 23 | str(discriminant), 24 | initial_el, 25 | result_y, 26 | proof, 27 | iters, 28 | ) 29 | assert is_valid 30 | 31 | # Creates another proof starting at the previous output 32 | iters_2 = 200000 33 | t1 = time.time() 34 | result_2 = prove( 35 | discriminant_challenge, 36 | result_y, 37 | discriminant_size, 38 | iters_2, 39 | "" 40 | ) 41 | t2 = time.time() 42 | print(f"IPS: {iters_2 / (t2 - t1)}") 43 | 44 | is_valid = verify_wesolowski( 45 | str(discriminant), 46 | result_y, 47 | result_2[:form_size], 48 | result_2[form_size : 2 * form_size], 49 | iters_2, 50 | ) 51 | assert is_valid 52 | 53 | 54 | test_prove_and_verify() 55 | -------------------------------------------------------------------------------- /tools/gen_pprods.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import math 3 | import sys 4 | 5 | 6 | def dbg(*args): 7 | print(*args, file=sys.stderr) 8 | 9 | 10 | primes = ( 11 | 2, 12 | 3, 13 | 5, 14 | 7, 15 | 11, 16 | 13, 17 | 17, 18 | 19, 19 | 23, 20 | 29, 21 | 31, 22 | 37, 23 | 41, 24 | 43, 25 | 47, 26 | 53, 27 | 59, 28 | 61, 29 | 67, 30 | 71, 31 | 73, 32 | 79, 33 | 83, 34 | 89, 35 | 97, 36 | 101, 37 | 103, 38 | 107, 39 | 109, 40 | 113, 41 | 127, 42 | 131, 43 | 137, 44 | 139, 45 | 149, 46 | ) 47 | 48 | 49 | def is_prime(n): 50 | if n in primes: 51 | return True 52 | if n % 2 == 0: 53 | return False 54 | s = int(math.sqrt(n)) 55 | for p in primes: 56 | if p > s: 57 | return True 58 | if n % p == 0: 59 | return False 60 | raise ValueError("Too big n=%d" % (n,)) 61 | 62 | 63 | def print_pprods_h(prods, factors): 64 | tab = " " 65 | tmpl = """ 66 | #ifndef PPRODS_H 67 | #define PPRODS_H 68 | 69 | // This file is auto-generated by gen_pprods.py 70 | 71 | // The array contains products of consecutive odd prime numbers grouped so that 72 | // each product fits into 64 bits. 73 | static const uint64_t pprods[] = { 74 | %s 75 | }; 76 | #endif // PPRODS_H 77 | """ 78 | tmpl = tmpl.strip() 79 | prods_str = "" 80 | for i, pr in enumerate(prods): 81 | factors_str = "*".join(map(str, factors[i])) 82 | extra_spaces = " " * (16 - (pr.bit_length() + 3) // 4) 83 | prods_str += tab + "0x%x, %s// %s\n" % (pr, extra_spaces, factors_str) 84 | print(tmpl % (prods_str.rstrip(),)) 85 | 86 | 87 | n = int(sys.argv[1]) 88 | cnt = 0 89 | pr = 1 90 | arr = [] 91 | prods = [] 92 | factors = [] 93 | for i in range(3, n): 94 | if is_prime(i): 95 | # print(i, '', end='') 96 | cnt += 1 97 | pr *= i 98 | if pr.bit_length() > 64: 99 | pr //= i 100 | # print('%s: %s' % (hex(pr), arr)) 101 | prods.append(pr) 102 | factors.append(arr) 103 | pr = i 104 | arr = [] 105 | arr.append(i) 106 | 107 | print_pprods_h(prods, factors) 108 | 109 | dbg() 110 | dbg("n_primes=%d n_prods=%d last_prime=%d" % (cnt, len(prods), factors[-1][-1])) 111 | # dbg(*list('%s,' % (hex(i),) for i in prods)) 112 | --------------------------------------------------------------------------------