diff --git a/.github/workflows/hw-build.yml b/.github/workflows/hw-build.yml index 268191b1..692e563e 100644 --- a/.github/workflows/hw-build.yml +++ b/.github/workflows/hw-build.yml @@ -22,7 +22,7 @@ permissions: jobs: build-hw: - name: Build HW VDF Client + name: Build HW VDF Client (Ubuntu) runs-on: [ubuntu-22.04] steps: - name: Checkout code @@ -43,21 +43,25 @@ jobs: - name: Install deps run: | sudo apt-get update - sudo apt-get install -y build-essential cmake libgmp-dev libboost-system-dev + sudo apt-get install -y build-essential cmake libgmp-dev - name: Download USB drivers - run: | - mkdir libft4222 - cd libft4222 - wget https://download.chia.net/vdf/libft4222-linux-1.4.4.170.tgz - tar -xvzf libft4222-linux-1.4.4.170.tgz - ln -s ${{ github.workspace }}/libft4222 ${{ github.workspace }}/src/hw/libft4222 - ln -s ${{ github.workspace }}/libft4222/build-x86_64/libft4222.so.1.4.4.170 ${{ github.workspace }}/libft4222/build-x86_64/libft4222.so + run: ./scripts/get-libft4222.sh install - name: Compile working-directory: "${{ github.workspace }}/src" run: make -f Makefile.vdf-client emu_hw_test hw_test emu_hw_vdf_client hw_vdf_client + - name: Smoke test + working-directory: "${{ github.workspace }}/src" + run: | + # CI runners do not have FT4222 hardware attached; only validate binaries exist. + test -x ./hw_vdf_client + test -x ./hw_test + echo "Running emu_hw_test" + ./emu_hw_test 1 2000 + ./emu_hw_vdf_client --list + - name: Upload Artifacts uses: actions/upload-artifact@v5 with: @@ -119,3 +123,144 @@ jobs: glue_url: ${{ secrets.GLUE_API_URL }} glue_project: "chiavdf-hw" glue_path: "trigger" + + build-hw-macos-arm64: + name: Build HW VDF Client (macOS arm64) + runs-on: [macos-13-arm64] + steps: + - name: Checkout code + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - uses: Chia-Network/actions/setup-python@main + name: Install Python + with: + python-version: "3.10" + + - name: Set Env + uses: Chia-Network/actions/setjobenv@main + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Install deps + run: | + brew ls --versions gmp >/dev/null 2>&1 || brew install gmp + + - name: Download USB drivers (FT4222H) + run: ./scripts/get-libft4222.sh install + + - name: Compile + working-directory: "${{ github.workspace }}/src" + run: make -f Makefile.vdf-client emu_hw_test hw_test emu_hw_vdf_client hw_vdf_client + + - name: Smoke test + working-directory: "${{ github.workspace }}/src" + run: | + # CI runners do not have FT4222 hardware attached; only validate binaries exist. + test -x ./hw_vdf_client + test -x ./hw_test + echo "Running emu_hw_test" + ./emu_hw_test 1 2000 + ./emu_hw_vdf_client --list + + - name: Upload Artifacts + uses: actions/upload-artifact@v5 + with: + name: hw-vdf-macos-arm64 + path: | + ${{ github.workspace }}/src/emu_hw_test + ${{ github.workspace }}/src/hw_test + ${{ github.workspace }}/src/hw_vdf_client + ${{ github.workspace }}/src/emu_hw_vdf_client + ${{ github.workspace }}/src/hw/libft4222/libft4222.dylib + ${{ github.workspace }}/src/hw/libft4222/libft4222.1.4.4.190.dylib + ${{ github.workspace }}/src/hw/libft4222/libftd2xx.dylib + + build-hw-windows: + name: Build HW VDF Client (Windows) + runs-on: [windows-latest] + steps: + - name: Checkout code + uses: actions/checkout@v6 + with: + fetch-depth: 1 + + - name: Set Env + uses: Chia-Network/actions/setjobenv@main + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Checkout mpir for Windows + uses: actions/checkout@v6 + with: + repository: Chia-Network/mpir_gc_x64 + fetch-depth: 1 + path: mpir_gc_x64 + + - name: Install LLVM and Ninja on Windows + shell: pwsh + run: | + $llvmBin = "C:\Program Files\LLVM\bin\clang-cl.exe" + $ninjaBin = "C:\ProgramData\chocolatey\bin\ninja.exe" + if (-not (Test-Path $llvmBin) -or -not (Test-Path $ninjaBin)) { + choco install llvm ninja -y + } + "C:\Program Files\LLVM\bin" | Out-File -Append -FilePath $env:GITHUB_PATH + + - name: Set up MSVC environment (Windows SDK + CRT libs) + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + + - name: Download USB drivers (FT4222H) + run: powershell -ExecutionPolicy Bypass -File scripts/get-libft4222.ps1 install + + - name: Configure hw tools + shell: pwsh + run: | + cmake -S src -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_TRY_COMPILE_CONFIGURATION=Release -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_ASM_COMPILER=clang-cl -DBUILD_PYTHON=OFF -DBUILD_CHIAVDFC=OFF -DBUILD_HW_TOOLS=ON + + - name: Build hw tools + shell: pwsh + run: | + cmake --build build --target emu_hw_test + cmake --build build --target hw_test + cmake --build build --target emu_hw_vdf_client + cmake --build build --target hw_vdf_client + + - name: Smoke test + working-directory: "${{ github.workspace }}/build" + shell: pwsh + run: | + # Keep native stderr as log output; emu binaries log diagnostics there. + $PSNativeCommandUseErrorActionPreference = $false + $dllPaths = @() + if (Test-Path "$env:GITHUB_WORKSPACE\mpir_gc_x64") { $dllPaths += "$env:GITHUB_WORKSPACE\mpir_gc_x64" } + if (Test-Path "$env:GITHUB_WORKSPACE\src\hw\libft4222") { $dllPaths += "$env:GITHUB_WORKSPACE\src\hw\libft4222" } + if ($dllPaths.Count -gt 0) { $env:PATH = ($dllPaths -join ';') + ';' + $env:PATH } + if (-not (Test-Path ".\hw_test.exe")) { throw "hw_test.exe is missing" } + if (-not (Test-Path ".\hw_vdf_client.exe")) { throw "hw_vdf_client.exe is missing" } + Write-Host "hw_test.exe and hw_vdf_client.exe present" + Write-Host "Running emu_hw_test" + .\emu_hw_test.exe 1 2000 + if ($LASTEXITCODE -ne 0) { throw "emu_hw_test failed with exit code $LASTEXITCODE" } + Write-Host "emu_hw_test completed" + Write-Host "Running emu_hw_vdf_client --list" + .\emu_hw_vdf_client.exe --list + if ($LASTEXITCODE -ne 0) { throw "emu_hw_vdf_client --list failed with exit code $LASTEXITCODE" } + Write-Host "emu_hw_vdf_client --list completed" + + - name: Upload Artifacts + uses: actions/upload-artifact@v5 + with: + name: hw-vdf-windows + path: | + ${{ github.workspace }}/build/emu_hw_test.exe + ${{ github.workspace }}/build/hw_test.exe + ${{ github.workspace }}/build/emu_hw_vdf_client.exe + ${{ github.workspace }}/build/hw_vdf_client.exe + ${{ github.workspace }}/src/hw/libft4222/libft4222.dll + ${{ github.workspace }}/src/hw/libft4222/ftd2xx.dll + ${{ github.workspace }}/src/hw/libft4222/libft4222.lib + ${{ github.workspace }}/src/hw/libft4222/ftd2xx.lib diff --git a/scripts/get-libft4222.ps1 b/scripts/get-libft4222.ps1 new file mode 100644 index 00000000..1526fea8 --- /dev/null +++ b/scripts/get-libft4222.ps1 @@ -0,0 +1,174 @@ +Param( + [Parameter(Mandatory = $false)] + [ValidateSet("install", "clean")] + [string]$Action = "install" +) + +$ErrorActionPreference = "Stop" + +$RootDir = Resolve-Path (Join-Path $PSScriptRoot "..") +$SrcDir = Join-Path $RootDir "src" +$WorkDir = Join-Path $SrcDir "libft4222" +$HwDir = Join-Path $SrcDir "hw\libft4222" + +$ZipUrl = "https://download.chia.net/vdf/LibFT4222-v1.4.8.zip" +$ZipPath = Join-Path $WorkDir "LibFT4222-v1.4.8.zip" +$ExtractDir = Join-Path $WorkDir "extract" + +Add-Type -AssemblyName System.IO.Compression.FileSystem + +function Ensure-Dir { + param([string]$Path) + if (-not (Test-Path $Path)) { + New-Item -ItemType Directory -Path $Path | Out-Null + } +} + +function Choose-Best { + param([System.IO.FileInfo[]]$Items) + if (-not $Items -or $Items.Count -eq 0) { + return $null + } + $preferred = $Items | Where-Object { $_.FullName -match "(x64|amd64|win64)" } + if ($preferred -and $preferred.Count -gt 0) { + return $preferred[0] + } + return $Items[0] +} + +function Find-Header { + param( + [string]$BaseName, + [string[]]$Alternates = @() + ) + $names = @($BaseName) + $Alternates + foreach ($name in $names) { + $match = Get-ChildItem -Path $ExtractDir -Recurse -File | + Where-Object { $_.Name -ieq $name } | + Select-Object -First 1 + if ($match) { + return $match + } + } + return $null +} + +function Test-ZipValid { + param([string]$Path) + if (-not (Test-Path $Path)) { + return $false + } + $zip = $null + try { + $zip = [System.IO.Compression.ZipFile]::OpenRead($Path) + $null = $zip.Entries.Count + return $true + } catch { + return $false + } finally { + if ($zip) { + $zip.Dispose() + } + } +} + +function Download-With-Retry { + param( + [string]$Url, + [string]$Path, + [int]$Attempts = 3 + ) + for ($attempt = 1; $attempt -le $Attempts; $attempt++) { + if (Test-Path $Path) { + Remove-Item $Path -Force + } + try { + Invoke-WebRequest -Uri $Url -OutFile $Path -MaximumRedirection 5 + } catch { + if ($attempt -eq $Attempts) { + throw + } + Start-Sleep -Seconds (2 * $attempt) + continue + } + if (Test-ZipValid $Path) { + return + } + Write-Warning "Downloaded zip failed validation (attempt $attempt/$Attempts)." + Start-Sleep -Seconds (2 * $attempt) + } + throw "Failed to download a valid zip from $Url after $Attempts attempts." +} + +function Install-LibFT4222 { + Ensure-Dir $WorkDir + if (Test-Path $ExtractDir) { + Remove-Item $ExtractDir -Recurse -Force + } + + Download-With-Retry -Url $ZipUrl -Path $ZipPath -Attempts 3 + Expand-Archive -Path $ZipPath -DestinationPath $ExtractDir -Force + + $headers = @( + @{ Name = "libft4222.h"; Alternates = @() }, + @{ Name = "ftd2xx.h"; Alternates = @() }, + @{ Name = "WinTypes.h"; Alternates = @("wintypes.h") } + ) + foreach ($header in $headers) { + $match = Find-Header -BaseName $header.Name -Alternates $header.Alternates + if (-not $match) { + if ($header.Name -eq "WinTypes.h") { + $stubPath = Join-Path $WorkDir $header.Name + @" +#pragma once +#ifdef _WIN32 +#include +#endif +"@ | Set-Content -Path $stubPath -Encoding ASCII + continue + } + throw "Missing required header: $($header.Name)" + } + Copy-Item $match.FullName -Destination (Join-Path $WorkDir $header.Name) -Force + } + + $libftLib = Choose-Best (Get-ChildItem -Path $ExtractDir -Recurse -Filter "libft4222*.lib") + $ftdLib = Choose-Best (Get-ChildItem -Path $ExtractDir -Recurse -Filter "ftd2xx*.lib") + $libftDll = Choose-Best (Get-ChildItem -Path $ExtractDir -Recurse -Filter "libft4222*.dll") + $ftdDll = Choose-Best (Get-ChildItem -Path $ExtractDir -Recurse -Filter "ftd2xx*.dll") + + if (-not $libftLib -or -not $ftdLib) { + throw "Missing required .lib files in extracted package" + } + if (-not $libftDll -or -not $ftdDll) { + throw "Missing required .dll files in extracted package" + } + + Copy-Item $libftLib.FullName -Destination (Join-Path $WorkDir "libft4222.lib") -Force + Copy-Item $ftdLib.FullName -Destination (Join-Path $WorkDir "ftd2xx.lib") -Force + Copy-Item $libftDll.FullName -Destination (Join-Path $WorkDir "libft4222.dll") -Force + Copy-Item $ftdDll.FullName -Destination (Join-Path $WorkDir "ftd2xx.dll") -Force + + if (Test-Path $HwDir) { + Remove-Item $HwDir -Recurse -Force + } + Ensure-Dir $HwDir + + Copy-Item (Join-Path $WorkDir "*.h") -Destination $HwDir -Force + Copy-Item (Join-Path $WorkDir "*.lib") -Destination $HwDir -Force + Copy-Item (Join-Path $WorkDir "*.dll") -Destination $HwDir -Force +} + +function Clean-LibFT4222 { + if (Test-Path $WorkDir) { + Remove-Item $WorkDir -Recurse -Force + } + if (Test-Path $HwDir) { + Remove-Item $HwDir -Recurse -Force + } +} + +switch ($Action) { + "install" { Install-LibFT4222 } + "clean" { Clean-LibFT4222 } +} diff --git a/scripts/get-libft4222.sh b/scripts/get-libft4222.sh new file mode 100755 index 00000000..0bca8601 --- /dev/null +++ b/scripts/get-libft4222.sh @@ -0,0 +1,179 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +SRC_DIR="${ROOT_DIR}/src" +WORK_DIR="${SRC_DIR}/libft4222" +HW_DIR="${SRC_DIR}/hw/libft4222" + +# Upstream FT4222 driver downloads can be found at https://ftdichip.com/products/ft4222h/ +# on the Downloads tab in the middle of the page as of 2026-02-05. +# There is an updated Linux version available(v1.4.4.233) also as of 2026-02-05. +# We currently fetch known-good versions mirrored at download.chia.net. + +LINUX_URL="https://download.chia.net/vdf/libft4222-linux-1.4.4.170.tgz" +LINUX_ARCHIVE="${WORK_DIR}/libft4222-linux-1.4.4.170.tgz" +MAC_URL="https://download.chia.net/vdf/LibFT4222-mac-v1.4.4.190.zip" +MAC_ARCHIVE="${WORK_DIR}/LibFT4222-mac-v1.4.4.190.zip" +MAC_DMG="libft4222.1.4.4.190.dmg" + +usage() { + cat <<'EOF' +Usage: scripts/get-libft4222.sh + +install Download and place FT4222 driver headers/libs. +clean Remove downloaded artifacts and symlinks. +EOF +} + +need_cmd() { + if ! command -v "$1" >/dev/null 2>&1; then + echo "Missing required command: $1" >&2 + exit 1 + fi +} + +fetch_once() { + local url="$1" + local dest="$2" + if command -v curl >/dev/null 2>&1; then + curl -L -o "$dest" "$url" + elif command -v wget >/dev/null 2>&1; then + wget -O "$dest" "$url" + else + echo "Missing curl or wget for downloads" >&2 + exit 1 + fi +} + +validate_archive() { + local kind="$1" + local path="$2" + case "$kind" in + tgz) + tar -tzf "$path" >/dev/null 2>&1 + ;; + zip) + unzip -t "$path" >/dev/null 2>&1 + ;; + *) + echo "Unknown archive type: $kind" >&2 + return 1 + ;; + esac +} + +fetch_with_retry() { + local url="$1" + local dest="$2" + local kind="$3" + local attempts="${4:-3}" + local attempt=1 + + while [ "$attempt" -le "$attempts" ]; do + rm -f "$dest" + fetch_once "$url" "$dest" || true + if [ -f "$dest" ] && validate_archive "$kind" "$dest"; then + return 0 + fi + echo "Downloaded archive failed validation (attempt $attempt/$attempts)." >&2 + sleep $((2 * attempt)) + attempt=$((attempt + 1)) + done + + echo "Failed to download a valid archive from $url after $attempts attempts." >&2 + return 1 +} + +install_linux() { + need_cmd tar + + mkdir -p "$WORK_DIR" + fetch_with_retry "$LINUX_URL" "$LINUX_ARCHIVE" tgz 3 + tar -xzf "$LINUX_ARCHIVE" -C "$WORK_DIR" + + rm -rf "$HW_DIR" + ln -s "$WORK_DIR" "$HW_DIR" + ln -sf "$WORK_DIR/build-x86_64/libft4222.so.1.4.4.170" \ + "$WORK_DIR/build-x86_64/libft4222.so" +} + +install_macos() { + need_cmd unzip + need_cmd hdiutil + need_cmd install_name_tool + need_cmd codesign + + mkdir -p "$WORK_DIR" + fetch_with_retry "$MAC_URL" "$MAC_ARCHIVE" zip 3 + unzip -q "$MAC_ARCHIVE" -d "$WORK_DIR" + + local mount_dir="${WORK_DIR}/ft4222-mount" + local mounted=0 + cleanup_mount() { + if [ "$mounted" -eq 1 ]; then + hdiutil detach "$mount_dir" >/dev/null 2>&1 || true + mounted=0 + fi + } + trap cleanup_mount EXIT + + # If a previous run failed, try to detach before re-attaching. + hdiutil detach "$mount_dir" >/dev/null 2>&1 || true + mkdir -p "$mount_dir" + hdiutil attach -nobrowse -readonly -mountpoint "$mount_dir" "${WORK_DIR}/${MAC_DMG}" + mounted=1 + + cp "$mount_dir/ftd2xx.h" "$WORK_DIR/" + cp "$mount_dir/libft4222.h" "$WORK_DIR/" + cp "$mount_dir/WinTypes.h" "$WORK_DIR/" + cp "$mount_dir/build/libft4222.1.4.4.190.dylib" "$WORK_DIR/" + cp "$mount_dir/build/libftd2xx.dylib" "$WORK_DIR/" + cleanup_mount + trap - EXIT + + ln -sf "libft4222.1.4.4.190.dylib" "${WORK_DIR}/libft4222.dylib" + + install_name_tool -id "@rpath/libftd2xx.dylib" "${WORK_DIR}/libftd2xx.dylib" + install_name_tool -id "@rpath/libft4222.dylib" "${WORK_DIR}/libft4222.1.4.4.190.dylib" + install_name_tool -change "libftd2xx.dylib" "@rpath/libftd2xx.dylib" \ + "${WORK_DIR}/libft4222.1.4.4.190.dylib" + + # Clear provenance attributes and ad-hoc sign dylibs to avoid execution kills. + if command -v xattr >/dev/null 2>&1; then + xattr -dr com.apple.provenance "$WORK_DIR" || true + fi + codesign --force --sign - \ + "${WORK_DIR}/libftd2xx.dylib" \ + "${WORK_DIR}/libft4222.1.4.4.190.dylib" \ + "${WORK_DIR}/libft4222.dylib" + + rm -rf "$HW_DIR" + ln -s "$WORK_DIR" "$HW_DIR" +} + +clean_all() { + local mount_dir="${WORK_DIR}/ft4222-mount" + hdiutil detach "$mount_dir" >/dev/null 2>&1 || true + rm -rf "$WORK_DIR" + if [ -L "$HW_DIR" ] || [ -d "$HW_DIR" ]; then + rm -rf "$HW_DIR" + fi +} + +case "${1:-}" in + install) + case "$(uname -s)" in + Linux) install_linux ;; + Darwin) install_macos ;; + *) echo "Unsupported OS: $(uname -s)" >&2; exit 1 ;; + esac + ;; + clean) + clean_all + ;; + *) + usage + exit 1 + ;; +esac diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e054c818..7af16c8f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -42,7 +42,7 @@ if(APPLE) endif() if(WIN32) - add_compile_definitions(CHIA_WINDOWS) + add_compile_definitions(CHIA_WINDOWS NOMINMAX WIN32_LEAN_AND_MEAN) set(MPIR_LIBRARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../mpir_gc_x64") set(MPIR_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../mpir_gc_x64") include_directories( @@ -306,6 +306,7 @@ endif() if(BUILD_HW_TOOLS) set(HW_COMMON_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/vdf_base.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/prover_runtime.cpp ${CMAKE_CURRENT_SOURCE_DIR}/hw/hw_util.cpp ${CMAKE_CURRENT_SOURCE_DIR}/hw/hw_proof.cpp ${CMAKE_CURRENT_SOURCE_DIR}/hw/hw_interface.cpp @@ -318,61 +319,70 @@ if(BUILD_HW_TOOLS) ${CMAKE_CURRENT_SOURCE_DIR}/hw/emu_funcs.cpp ${CMAKE_CURRENT_SOURCE_DIR}/hw/emu_runner.cpp ) + set(HW_VDF_ASM_SOURCES ${VDF_ASM_SOURCES}) + if(WIN32) + # Generated asm currently collides with lzcnt symbols for HW tool links on Windows. + set(HW_VDF_ASM_SOURCES) + endif() - if(NOT WIN32) - if(APPLE) - set(HW_LIBS - ${CMAKE_CURRENT_SOURCE_DIR}/hw/libft4222/libft4222.dylib - ${CMAKE_CURRENT_SOURCE_DIR}/hw/libft4222/libftd2xx.dylib - ) - else() - set(HW_LIBS - ${CMAKE_CURRENT_SOURCE_DIR}/hw/libft4222/build-x86_64/libft4222.so - ) - endif() - - add_executable(hw_test - ${CMAKE_CURRENT_SOURCE_DIR}/hw/hw_test.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/hw/real_hw.cpp + if(WIN32) + set(HW_LIBS + ${CMAKE_CURRENT_SOURCE_DIR}/hw/libft4222/libft4222.lib + ${CMAKE_CURRENT_SOURCE_DIR}/hw/libft4222/ftd2xx.lib ) - target_sources(hw_test PRIVATE ${HW_COMMON_SOURCES} ${VDF_ASM_SOURCES} ${VDF_COMMON_SOURCES}) - target_compile_definitions(hw_test PRIVATE ${VDF_COMMON_DEFINITIONS}) - target_include_directories(hw_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/hw ${CMAKE_CURRENT_SOURCE_DIR}/hw/libft4222) - target_link_libraries(hw_test PRIVATE ${GMP_LIBRARIES} ${GMPXX_LIBRARIES} Threads::Threads ${HW_LIBS}) - if(APPLE) - target_link_options(hw_test PRIVATE "-Wl,-rpath,@executable_path/hw/libft4222") - endif() - - add_executable(hw_vdf_client - ${CMAKE_CURRENT_SOURCE_DIR}/hw/hw_vdf_client.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/hw/real_hw.cpp + set(HW_SOCKET_LIBS ws2_32) + elseif(APPLE) + set(HW_LIBS + ${CMAKE_CURRENT_SOURCE_DIR}/hw/libft4222/libft4222.dylib + ${CMAKE_CURRENT_SOURCE_DIR}/hw/libft4222/libftd2xx.dylib ) - target_sources(hw_vdf_client PRIVATE ${HW_COMMON_SOURCES} ${VDF_ASM_SOURCES} ${VDF_COMMON_SOURCES}) - target_compile_definitions(hw_vdf_client PRIVATE ${VDF_COMMON_DEFINITIONS}) - target_include_directories(hw_vdf_client PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/hw ${CMAKE_CURRENT_SOURCE_DIR}/hw/libft4222) - target_link_libraries(hw_vdf_client PRIVATE ${GMP_LIBRARIES} ${GMPXX_LIBRARIES} Threads::Threads ${HW_LIBS}) - if(APPLE) - target_link_options(hw_vdf_client PRIVATE "-Wl,-rpath,@executable_path/hw/libft4222") - endif() + set(HW_SOCKET_LIBS) + else() + set(HW_LIBS + ${CMAKE_CURRENT_SOURCE_DIR}/hw/libft4222/build-x86_64/libft4222.so + ) + set(HW_SOCKET_LIBS) endif() - if(NOT WIN32) - add_executable(emu_hw_test - ${CMAKE_CURRENT_SOURCE_DIR}/hw/hw_test.cpp - ) - target_sources(emu_hw_test PRIVATE ${HW_COMMON_SOURCES} ${EMU_SOURCES} ${VDF_ASM_SOURCES} ${VDF_COMMON_SOURCES}) - target_compile_definitions(emu_hw_test PRIVATE ${VDF_COMMON_DEFINITIONS}) - target_include_directories(emu_hw_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/hw ${CMAKE_CURRENT_SOURCE_DIR}/hw/libft4222) - target_link_libraries(emu_hw_test PRIVATE ${GMP_LIBRARIES} ${GMPXX_LIBRARIES} Threads::Threads) + add_executable(hw_test + ${CMAKE_CURRENT_SOURCE_DIR}/hw/hw_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/hw/real_hw.cpp + ) + target_sources(hw_test PRIVATE ${HW_COMMON_SOURCES} ${HW_VDF_ASM_SOURCES} ${VDF_COMMON_SOURCES}) + target_compile_definitions(hw_test PRIVATE ${VDF_COMMON_DEFINITIONS}) + target_include_directories(hw_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/hw ${CMAKE_CURRENT_SOURCE_DIR}/hw/libft4222) + target_link_libraries(hw_test PRIVATE ${GMP_LIBRARIES} ${GMPXX_LIBRARIES} Threads::Threads ${HW_LIBS}) + if(APPLE) + target_link_options(hw_test PRIVATE "-Wl,-rpath,@executable_path/hw/libft4222") + endif() - add_executable(emu_hw_vdf_client - ${CMAKE_CURRENT_SOURCE_DIR}/hw/hw_vdf_client.cpp - ) - target_sources(emu_hw_vdf_client PRIVATE ${HW_COMMON_SOURCES} ${EMU_SOURCES} ${VDF_ASM_SOURCES} ${VDF_COMMON_SOURCES}) - target_compile_definitions(emu_hw_vdf_client PRIVATE ${VDF_COMMON_DEFINITIONS}) - target_include_directories(emu_hw_vdf_client PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/hw ${CMAKE_CURRENT_SOURCE_DIR}/hw/libft4222) - target_link_libraries(emu_hw_vdf_client PRIVATE ${GMP_LIBRARIES} ${GMPXX_LIBRARIES} Threads::Threads) + add_executable(hw_vdf_client + ${CMAKE_CURRENT_SOURCE_DIR}/hw/hw_vdf_client.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/hw/real_hw.cpp + ) + target_sources(hw_vdf_client PRIVATE ${HW_COMMON_SOURCES} ${HW_VDF_ASM_SOURCES} ${VDF_COMMON_SOURCES}) + target_compile_definitions(hw_vdf_client PRIVATE ${VDF_COMMON_DEFINITIONS}) + target_include_directories(hw_vdf_client PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/hw ${CMAKE_CURRENT_SOURCE_DIR}/hw/libft4222) + target_link_libraries(hw_vdf_client PRIVATE ${GMP_LIBRARIES} ${GMPXX_LIBRARIES} Threads::Threads ${HW_LIBS} ${HW_SOCKET_LIBS}) + if(APPLE) + target_link_options(hw_vdf_client PRIVATE "-Wl,-rpath,@executable_path/hw/libft4222") endif() + + add_executable(emu_hw_test + ${CMAKE_CURRENT_SOURCE_DIR}/hw/hw_test.cpp + ) + target_sources(emu_hw_test PRIVATE ${HW_COMMON_SOURCES} ${EMU_SOURCES} ${HW_VDF_ASM_SOURCES} ${VDF_COMMON_SOURCES}) + target_compile_definitions(emu_hw_test PRIVATE ${VDF_COMMON_DEFINITIONS}) + target_include_directories(emu_hw_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/hw ${CMAKE_CURRENT_SOURCE_DIR}/hw/libft4222) + target_link_libraries(emu_hw_test PRIVATE ${GMP_LIBRARIES} ${GMPXX_LIBRARIES} Threads::Threads) + + add_executable(emu_hw_vdf_client + ${CMAKE_CURRENT_SOURCE_DIR}/hw/hw_vdf_client.cpp + ) + target_sources(emu_hw_vdf_client PRIVATE ${HW_COMMON_SOURCES} ${EMU_SOURCES} ${HW_VDF_ASM_SOURCES} ${VDF_COMMON_SOURCES}) + target_compile_definitions(emu_hw_vdf_client PRIVATE ${VDF_COMMON_DEFINITIONS}) + target_include_directories(emu_hw_vdf_client PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/hw ${CMAKE_CURRENT_SOURCE_DIR}/hw/libft4222) + target_link_libraries(emu_hw_vdf_client PRIVATE ${GMP_LIBRARIES} ${GMPXX_LIBRARIES} Threads::Threads ${HW_SOCKET_LIBS}) endif() add_executable(verifier_test diff --git a/src/Makefile.vdf-client b/src/Makefile.vdf-client index 9b0aa5a5..24138d96 100644 --- a/src/Makefile.vdf-client +++ b/src/Makefile.vdf-client @@ -3,8 +3,14 @@ ARCH := $(shell uname -m) ifneq (,$(findstring clang, $(shell $(CXX) --version))) NOPIE = -fno-PIE +LTO_FLAGS = -flto else NOPIE = -no-pie +LTO_FLAGS = -flto=auto +endif +# Windows builds don't use PIE flags. +ifeq ($(OS),Windows_NT) +NOPIE = endif # macOS arm64 ignores -no_pie and warns; omit to avoid deprecation warnings ifeq ($(UNAME),Darwin) @@ -13,18 +19,26 @@ NOPIE = endif endif -LDFLAGS += -flto $(NOPIE) -g +CFLAGS += $(LTO_FLAGS) $(NOPIE) +LDFLAGS += $(LTO_FLAGS) $(NOPIE) -g +ifeq ($(OS),Windows_NT) +LDLIBS += -lmpirxx -lmpir -lws2_32 +CXXFLAGS += $(LTO_FLAGS) -std=c++1z -D VDF_MODE=0 -D FAST_MACHINE=1 $(NOPIE) -fvisibility=hidden +else LDLIBS += -lgmpxx -lgmp -pthread -CXXFLAGS += -flto -std=c++1z -D VDF_MODE=0 -D FAST_MACHINE=1 -pthread $(NOPIE) -fvisibility=hidden +CXXFLAGS += $(LTO_FLAGS) -std=c++1z -D VDF_MODE=0 -D FAST_MACHINE=1 -pthread $(NOPIE) -fvisibility=hidden +endif ifeq ($(UNAME),Darwin) CXXFLAGS += -D CHIAOSX=1 # Homebrew (common on macOS) installs boost/gmp to /opt/homebrew or /usr/local ifneq ($(wildcard /opt/homebrew/include/boost/asio.hpp),) CXXFLAGS += -I/opt/homebrew/include +CFLAGS += -I/opt/homebrew/include LDFLAGS += -L/opt/homebrew/lib endif ifneq ($(wildcard /usr/local/include/boost/asio.hpp),) CXXFLAGS += -I/usr/local/include +CFLAGS += -I/usr/local/include LDFLAGS += -L/usr/local/lib endif endif @@ -62,7 +76,7 @@ $(BINS) avx512_test: %: %.o lzcnt.o $(ASM_OBJS) $(addsuffix .o,$(BINS)) avx512_test.o: CXXFLAGS += $(OPT_CFLAGS) lzcnt.o: refcode/lzcnt.c - $(CC) -c refcode/lzcnt.c + $(CC) $(CFLAGS) -c refcode/lzcnt.c asm_compiled.s: compile_asm ./compile_asm @@ -76,17 +90,35 @@ avx512_asm_compiled.s: compile_asm compile_asm: compile_asm.o $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) -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 +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_hw.o vdf_hw_symbol_anchors.o prover_runtime.o lzcnt.o EMU_OBJS = hw/emu_funcs.o hw/emu_runner.o +ifeq ($(OS),Windows_NT) +HW_LIB = hw/libft4222/libft4222.lib +HW_FTD2XX_LIB = hw/libft4222/ftd2xx.lib +HW_LIBS = $(HW_LIB) $(HW_FTD2XX_LIB) +else +ifeq ($(UNAME),Darwin) +HW_LIB = hw/libft4222/libft4222.dylib +HW_FTD2XX_LIB = hw/libft4222/libftd2xx.dylib +HW_LIBS = $(HW_LIB) $(HW_FTD2XX_LIB) +else HW_LIB = hw/libft4222/build-x86_64/libft4222.so +HW_LIBS = $(HW_LIB) +endif +endif + +ifeq ($(UNAME),Darwin) +# Ensure HW client binaries can locate USB driver dylibs bundled in-tree. +hw_test hw_vdf_client: LDFLAGS += -Wl,-rpath,@executable_path/hw/libft4222 +endif -hw_test: hw/hw_test.o $(HW_OBJS) $(HW_LIB) hw/real_hw.o +hw_test: hw/hw_test.o $(HW_OBJS) $(HW_LIBS) hw/real_hw.o $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) emu_hw_test: hw/hw_test.o $(HW_OBJS) $(EMU_OBJS) $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) -hw_vdf_client: hw/hw_vdf_client.o $(HW_OBJS) $(HW_LIB) hw/real_hw.o +hw_vdf_client: hw/hw_vdf_client.o $(HW_OBJS) $(HW_LIBS) hw/real_hw.o $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) emu_hw_vdf_client: hw/hw_vdf_client.o $(HW_OBJS) $(EMU_OBJS) diff --git a/src/Reducer.h b/src/Reducer.h index 913d747a..9fa7b5b5 100644 --- a/src/Reducer.h +++ b/src/Reducer.h @@ -29,7 +29,9 @@ limitations under the License. # define HAS_ATTRIBUTE(x) 0 #endif -#if HAS_ATTRIBUTE(weak) +#if defined(_WIN32) +# define WEAK +#elif HAS_ATTRIBUTE(weak) # define WEAK __attribute__((weak)) #else # define WEAK diff --git a/src/alloc.hpp b/src/alloc.hpp index a53d9f0c..f18377e7 100644 --- a/src/alloc.hpp +++ b/src/alloc.hpp @@ -52,7 +52,6 @@ inline void* mp_realloc_func(void* old_ptr, size_t old_size, size_t new_bytes) { //(the mpz class constructor does not call any gmp functions) inline void init_gmp() { mp_set_memory_functions(mp_alloc_func, mp_realloc_func, mp_free_func); - allow_integer_constructor=true; //make sure the old gmp allocator isn't used } #endif diff --git a/src/asm_base.h b/src/asm_base.h index 601c8a9e..0ac58aba 100644 --- a/src/asm_base.h +++ b/src/asm_base.h @@ -79,29 +79,29 @@ string track_asm(string comment, string jump_to = "") { assert(!enable_threads); //this code isn't atomic - #if defined(CHIAOSX) || defined(CHIA_WINDOWS) +#if defined(CHIAOSX) || defined(CHIA_WINDOWS) APPEND_M(str( "MOV [RIP+track_asm_rax], RAX" )); APPEND_M(str( "MOV RAX, [RIP+asm_tracking_data+#]", to_hex(8*(id-1)) )); APPEND_M(str( "LEA RAX, [RAX+1]" )); APPEND_M(str( "MOV [RIP+asm_tracking_data+#], RAX", to_hex(8*(id-1)) )); - #else +#else APPEND_M(str( "MOV [track_asm_rax], RAX" )); APPEND_M(str( "MOV RAX, [asm_tracking_data+#]", to_hex(8*(id-1)) )); APPEND_M(str( "LEA RAX, [RAX+1]" )); APPEND_M(str( "MOV [asm_tracking_data+#], RAX", to_hex(8*(id-1)) )); - #endif +#endif #if defined(CHIAOSX) || defined(CHIA_WINDOWS) APPEND_M(str( "LEA RAX, [RIP+#] ", comment_label )); #else APPEND_M(str( "MOV RAX, OFFSET FLAT:#", comment_label )); #endif - #if defined(CHIAOSX) || defined(CHIA_WINDOWS) +#if defined(CHIAOSX) || defined(CHIA_WINDOWS) APPEND_M(str( "MOV [RIP+asm_tracking_data_comments+#], RAX", to_hex(8*(id-1)) )); APPEND_M(str( "MOV RAX, [RIP+track_asm_rax]" )); - #else +#else APPEND_M(str( "MOV [asm_tracking_data_comments+#], RAX", to_hex(8*(id-1)) )); APPEND_M(str( "MOV RAX, [track_asm_rax]" )); - #endif +#endif if (!jump_to.empty()) { APPEND_M(str( "JMP #", jump_to )); diff --git a/src/asm_gcd_unsigned.h b/src/asm_gcd_unsigned.h index 53c54cd1..f6bc1019 100644 --- a/src/asm_gcd_unsigned.h +++ b/src/asm_gcd_unsigned.h @@ -452,27 +452,7 @@ void gcd_unsigned( APPEND_M(str( ".balign 8" )); APPEND_M(str( "#:", jump_table_label )); -#if defined(CHIA_WINDOWS) - APPEND_M(str( ".text" )); - - string bad_end_index_label = track_asm( "gcd_unsigned invalid a_end_index", m.alloc_error_label() ); - APPEND_M(str( "MOV `tmp, `spill_a_end_index" )); - APPEND_M(str( "CMP `tmp, #", to_hex(int_size-1) )); - APPEND_M(str( "JA #", bad_end_index_label )); - - for (int end_index=0;end_index 4) { + // For ABI entry points, pull args 5+ from the caller stack per Win64 ABI. + // Internal asm-to-asm calls may pass args 5/6 in R10/R11 and opt out. + if (d_windows_stack_args && num_args > 4) { APPEND_M(str( "MOV R10, [RSP+0x28]" )); } - if (num_args > 5) { + if (d_windows_stack_args && num_args > 5) { APPEND_M(str( "MOV R11, [RSP+0x30]" )); } #endif diff --git a/src/bqfc.c b/src/bqfc.c index 7e3ae35e..7d5782e6 100644 --- a/src/bqfc.c +++ b/src/bqfc.c @@ -3,6 +3,9 @@ #include #include +/* xgcd_partial.c is linked via C++ translation units; declare symbol for C++ compile path. */ +void mpz_xgcd_partial(mpz_t co2, mpz_t co1, mpz_t r2, mpz_t r1, const mpz_t L); + int bqfc_compr(struct qfb_c *out_c, mpz_t a, mpz_t b) { mpz_t a_sqrt, a_copy, b_copy, dummy; diff --git a/src/callback.h b/src/callback.h index d51626fb..ecf7fff8 100644 --- a/src/callback.h +++ b/src/callback.h @@ -94,10 +94,21 @@ class OneWesolowskiCallback: public WesolowskiCallback { k = 10; l = 1; } - kl = k * l; - uint64_t space_needed = wanted_iter / (k * l) + 100; + const uint64_t step = static_cast(k) * static_cast(l); + if (step == 0) { + throw std::overflow_error("OneWesolowskiCallback invalid checkpoint stride"); + } + if (step > static_cast(std::numeric_limits::max())) { + throw std::overflow_error("OneWesolowskiCallback checkpoint stride too large"); + } + kl = static_cast(step); + + const uint64_t space_needed = wanted_iter / step + 100; + if (space_needed > static_cast(std::numeric_limits::max())) { + throw std::overflow_error("OneWesolowskiCallback forms capacity overflow"); + } forms_capacity = static_cast(space_needed); - forms.reset(new form[space_needed]); + forms.reset(new form[forms_capacity]); forms[0] = f; } @@ -134,61 +145,108 @@ class TwoWesolowskiCallback: public WesolowskiCallback { forms_capacity = space_needed; forms.reset(new form[space_needed]); forms[0] = f; - kl = 10; - switch_iters = -1; + kl.store(10, std::memory_order_relaxed); + transition_state.store(EncodeTransitionState(/*switch_index=*/0, /*switch_iters=*/-1), std::memory_order_relaxed); } void IncreaseConstants(uint64_t num_iters) { std::lock_guard lk(forms_mutex); - kl = 100; - switch_iters = num_iters; - switch_index = num_iters / 10; + // Publish transition metadata first. `kl` is only a fast-path hint. + transition_state.store( + EncodeTransitionState(num_iters / 10, static_cast(num_iters)), + std::memory_order_release + ); + kl.store(100, std::memory_order_release); } - int GetPosition(uint64_t power) { + size_t GetPosition(uint64_t power) { std::lock_guard lk(forms_mutex); return GetPositionUnlocked(power); } - int GetPositionUnlocked(uint64_t power) const { - if (switch_iters == -1 || power < switch_iters) { - return power / 10; - } else { - return (switch_index + (power - switch_iters) / 100); - } + size_t GetPositionUnlocked(uint64_t power) const { + const uint64_t snapshot = transition_state.load(std::memory_order_acquire); + return GetPositionFromSnapshot(power, snapshot); } form GetFormCopy(uint64_t power) { std::lock_guard lk(forms_mutex); - const int pos = GetPositionUnlocked(power); - if (pos < 0 || static_cast(pos) >= forms_capacity) { + const size_t pos = GetPositionUnlocked(power); + if (pos >= forms_capacity) { throw std::runtime_error("TwoWesolowskiCallback::GetFormCopy out of bounds"); } - return forms[static_cast(pos)]; + return forms[pos]; } bool LargeConstants() { - std::lock_guard lk(forms_mutex); - return kl == 100; + const uint64_t snapshot = transition_state.load(std::memory_order_acquire); + return DecodeSwitchIters(snapshot) != -1; } void OnIteration(int type, void *data, uint64_t iteration) { iteration++; + // Most iterations are not checkpoints. Avoid mutex lock/unlock on that hot path. + const uint32_t current_kl = kl.load(std::memory_order_relaxed); + if (iteration % current_kl != 0) { + return; + } + std::lock_guard lk(forms_mutex); - if (iteration % kl == 0) { - const int pos = GetPositionUnlocked(iteration); - if (pos < 0 || static_cast(pos) >= forms_capacity) { - throw std::runtime_error("TwoWesolowskiCallback::OnIteration out of bounds"); - } - form* mulf = &forms[static_cast(pos)]; - SetForm(type, data, mulf); + const uint64_t snapshot = transition_state.load(std::memory_order_acquire); + const uint32_t effective_kl = GetEffectiveKl(iteration, snapshot); + if (iteration % effective_kl != 0) { + return; + } + const size_t pos = GetPositionFromSnapshot(iteration, snapshot); + if (pos >= forms_capacity) { + throw std::runtime_error("TwoWesolowskiCallback::OnIteration out of bounds"); } + form* mulf = &forms[pos]; + SetForm(type, data, mulf); } private: - uint64_t switch_index; - int64_t switch_iters; - uint32_t kl; + static uint64_t EncodeTransitionState(uint64_t switch_index_value, int64_t switch_iters_value) { + if (switch_index_value > static_cast(std::numeric_limits::max())) { + throw std::overflow_error("TwoWesolowskiCallback switch_index overflow"); + } + // Persist switch_iters in 32 bits with +1 encoding to represent -1 (unset) as 0. + if (switch_iters_value < -1 || + switch_iters_value > static_cast(std::numeric_limits::max() - 1)) { + throw std::overflow_error("TwoWesolowskiCallback switch_iters overflow"); + } + const uint32_t encoded_iters = static_cast(switch_iters_value + 1); + return (static_cast(encoded_iters) << 32) | + static_cast(static_cast(switch_index_value)); + } + + static uint64_t DecodeSwitchIndex(uint64_t state) { + return static_cast(state & 0xffffffffULL); + } + + static int64_t DecodeSwitchIters(uint64_t state) { + const uint32_t encoded_iters = static_cast(state >> 32); + return static_cast(encoded_iters) - 1; + } + + static uint32_t GetEffectiveKl(uint64_t power, uint64_t state) { + const int64_t current_switch_iters = DecodeSwitchIters(state); + return (current_switch_iters == -1 || power < static_cast(current_switch_iters)) ? 10U : 100U; + } + + static size_t GetPositionFromSnapshot(uint64_t power, uint64_t state) { + const int64_t current_switch_iters = DecodeSwitchIters(state); + if (current_switch_iters == -1 || power < static_cast(current_switch_iters)) { + return static_cast(power / 10); + } + const uint64_t current_switch_index = DecodeSwitchIndex(state); + return static_cast( + current_switch_index + (power - static_cast(current_switch_iters)) / 100 + ); + } + + std::atomic transition_state{0}; + std::atomic kl{10}; std::mutex forms_mutex; }; diff --git a/src/hw/emu_funcs.cpp b/src/hw/emu_funcs.cpp index 8f6cb0ca..bbb4e9a3 100644 --- a/src/hw/emu_funcs.cpp +++ b/src/hw/emu_funcs.cpp @@ -10,6 +10,19 @@ #include #include +#ifdef _WIN32 +// The vendor headers mark these APIs as dllimport; emulator builds provide +// local definitions, so drop import attributes for this translation unit. +#ifdef FTD2XX_API +#undef FTD2XX_API +#endif +#define FTD2XX_API +#ifdef LIBFT4222_API +#undef LIBFT4222_API +#endif +#define LIBFT4222_API +#endif + int chia_vdf_is_emu = 1; #define EMU_LOC_ID 0x88888 diff --git a/src/hw/emu_runner.cpp b/src/hw/emu_runner.cpp index 356365ac..4d04dae4 100644 --- a/src/hw/emu_runner.cpp +++ b/src/hw/emu_runner.cpp @@ -9,8 +9,24 @@ #include #include +#ifndef _WIN32 #include -#include +#endif + +#ifdef _WIN32 +static inline uint32_t bswap32_local(uint32_t x) +{ + return ((x & 0x000000FFu) << 24) | + ((x & 0x0000FF00u) << 8) | + ((x & 0x00FF0000u) >> 8) | + ((x & 0xFF000000u) >> 24); +} +static inline uint32_t htonl_local(uint32_t x) { return bswap32_local(x); } +static inline uint32_t ntohl_local(uint32_t x) { return bswap32_local(x); } +#else +static inline uint32_t htonl_local(uint32_t x) { return htonl(x); } +static inline uint32_t ntohl_local(uint32_t x) { return ntohl(x); } +#endif #define N_VDFS 3 @@ -92,7 +108,7 @@ void run_job(int i) reducer.reduce(qf2); if (!(st->cur_iter % 4096)) { - usleep(10); + vdf_usleep(10); } st->mtx.lock(); @@ -114,7 +130,7 @@ static void start_job(int i) while (states[i]->running) { states[i]->stopping = true; LOG_INFO("Emu %d: Waiting for the old thread to finish", i); - usleep(1000); + vdf_usleep(1000); } clear_state(states[i]); @@ -139,7 +155,11 @@ static void enable_engine(int i) states[i]->init_done = false; states[i]->stopping = true; states[i]->running = false; + #ifdef _WIN32 + srand(1); + #else srand48(1); + #endif } if (states[i]->stopping) { states[i]->stopping = false; @@ -155,7 +175,12 @@ void inject_error(struct job_status *stat, struct job_state *st) p = prob ? atoi(prob) : 0; g_error_prob = p; } - if (p != 0 && (st->error || (uint32_t)mrand48() % p == 0)) { + #ifdef _WIN32 + const uint32_t rand_val = static_cast(rand()); + #else + const uint32_t rand_val = static_cast(mrand48()); + #endif + if (p != 0 && (st->error || (rand_val % static_cast(p) == 0))) { // Inject error by messing up 'a' register stat->a[10] = ~stat->a[10]; st->error = true; @@ -269,7 +294,7 @@ int emu_do_io(uint8_t *buf_in, uint16_t size_in, uint8_t *buf_out, uint16_t size if (addr == job_control) { uint32_t data; memcpy(&data, buf_in, 4); - data = ntohl(data); + data = ntohl_local(data); if (data & (1U << CHIA_VDF_CONTROL_CLK_ENABLE_BIT)) { enable_engine(i); } else { @@ -287,7 +312,7 @@ int emu_do_io(uint8_t *buf_in, uint16_t size_in, uint8_t *buf_out, uint16_t size read_regs(addr, buf_out, size_out); } else if (!size_out && addr < sizeof(g_pll_regs)) { uint8_t *regs_addr = &((uint8_t *)g_pll_regs)[addr]; - uint32_t status_val = htonl((1U << CLOCK_STATUS_DIVACK_BIT) | + uint32_t status_val = htonl_local((1U << CLOCK_STATUS_DIVACK_BIT) | (1U << CLOCK_STATUS_LOCK_BIT)); copy_regs(regs_addr, buf_in, size_in, sizeof(g_pll_regs) - addr, 0); diff --git a/src/hw/ftdi_driver.hpp b/src/hw/ftdi_driver.hpp index e740c9fd..ad749288 100644 --- a/src/hw/ftdi_driver.hpp +++ b/src/hw/ftdi_driver.hpp @@ -13,7 +13,11 @@ void print_buf(size_t offset, uint8_t *buf, size_t size); class FtdiDriver { public: - typedef unsigned int DWORD; +#ifdef _WIN32 + using DWORD = ::DWORD; +#else + using DWORD = unsigned int; +#endif static const unsigned GPIO2 = 2; static const unsigned GPIO3 = 3; diff --git a/src/hw/hw_interface.cpp b/src/hw/hw_interface.cpp index d3cbf66f..129c6c63 100644 --- a/src/hw/hw_interface.cpp +++ b/src/hw/hw_interface.cpp @@ -9,7 +9,6 @@ #include #include -#include #define REG_BYTES 4 #define CHIA_VDF_JOB_SIZE (CHIA_VDF_CMD_START_REG_OFFSET - \ diff --git a/src/hw/hw_proof.cpp b/src/hw/hw_proof.cpp index 057a0b0f..4b510f49 100644 --- a/src/hw/hw_proof.cpp +++ b/src/hw/hw_proof.cpp @@ -4,7 +4,6 @@ #include #include -#include static const uint32_t g_chkp_thres = 1000000; static const uint32_t g_skip_thres = 10; @@ -474,13 +473,13 @@ void hw_proof_wait_values(struct vdf_state *vdf, bool finish_work) { if (finish_work) { while (!vdf->wq.empty()) { - usleep(100000); + vdf_usleep(100000); hw_proof_process_work(vdf); } } while (vdf->aux_threads_busy) { - usleep(10000); + vdf_usleep(10000); } if (!finish_work) { @@ -495,7 +494,7 @@ void hw_proof_wait_values(struct vdf_state *vdf, bool finish_work) int hw_proof_wait_value(struct vdf_state *vdf, size_t pos) { while (!(vdf->valid_values[pos / 8] & (1 << (pos % 8)))) { - usleep(100000); + vdf_usleep(100000); if (vdf->stopping) { return -1; } diff --git a/src/hw/hw_test.cpp b/src/hw/hw_test.cpp index 20df7694..4509a1ab 100644 --- a/src/hw/hw_test.cpp +++ b/src/hw/hw_test.cpp @@ -19,8 +19,6 @@ #include #include -#include - static const char *discrs[] = { "-0xac566497f63870a7b661f5482f47336cd1aa85ab43914828b7998f255916729c2ad965bcf7fe231721d96706ea7d823ed4adf663a0263714bb80144aebafcdd2915b6c7ef68c2d19447be83e7f39b4a7442640914053d2e7d6a561aa29b9449c815717af7da97a823798f402d073901a1f2bd8cd879b8b1afe2496649197021f", "-0xc3657f850b3f2b659d70273704564bc69b849fe1d8c70b096933efdcf7143931b393676f500d79624da783d73e0c5303ae48fb9543502c4161586d8fdaf03709d2115df21aeeee4a58614050cbdfe74024063b9620de084d8ef46f474fa57983c4bebfa7e8a69efeb523a167558fe1487a086c11337e20b773ad3d4710671417", @@ -100,7 +98,7 @@ int hw_test_main(int argc, char **argv) //break; //} if (chia_vdf_is_emu) { - usleep(50000); + vdf_usleep(50000); } read_cnt++; } diff --git a/src/hw/hw_util.cpp b/src/hw/hw_util.cpp index 77f7f7c2..a68ed474 100644 --- a/src/hw/hw_util.cpp +++ b/src/hw/hw_util.cpp @@ -8,12 +8,18 @@ void vdf_do_log(const char *msg, ...) va_list ap; struct tm cal; char time_str[25]; - struct timespec ts; + auto now = std::chrono::system_clock::now(); + auto secs = std::chrono::time_point_cast(now); + auto millis = std::chrono::duration_cast(now - secs).count(); + std::time_t tt = std::chrono::system_clock::to_time_t(now); - clock_gettime(CLOCK_REALTIME, &ts); - localtime_r(&ts.tv_sec, &cal); +#ifdef _WIN32 + localtime_s(&cal, &tt); +#else + localtime_r(&tt, &cal); +#endif strftime(time_str, sizeof(time_str), "%FT%T", &cal); - fprintf(stderr, "%s.%03ld ", time_str, ts.tv_nsec / 1000000); + fprintf(stderr, "%s.%03lld ", time_str, static_cast(millis)); va_start(ap, msg); vfprintf(stderr, msg, ap); diff --git a/src/hw/hw_util.hpp b/src/hw/hw_util.hpp index bb2c7805..daa05a54 100644 --- a/src/hw/hw_util.hpp +++ b/src/hw/hw_util.hpp @@ -3,6 +3,7 @@ #include #include +#include void vdf_do_log(const char *msg, ...); @@ -33,4 +34,14 @@ static inline uint64_t vdf_get_elapsed_us(timepoint_t &t1) return std::chrono::duration_cast(t2 - t1).count(); } +static inline void vdf_usleep(uint64_t usec) +{ + std::this_thread::sleep_for(std::chrono::microseconds(usec)); +} + +static inline void vdf_sleep(uint64_t sec) +{ + std::this_thread::sleep_for(std::chrono::seconds(sec)); +} + #endif /* HW_UTIL_H */ diff --git a/src/hw/hw_vdf_client.cpp b/src/hw/hw_vdf_client.cpp index 19d063dd..b71f01c4 100644 --- a/src/hw/hw_vdf_client.cpp +++ b/src/hw/hw_vdf_client.cpp @@ -6,15 +6,26 @@ #include "chia_driver.hpp" #include "pll_freqs.hpp" -#include #include -#include -#include +#include +#include #include + +#ifdef _WIN32 +#include +#include +using vdf_socket_t = SOCKET; +static constexpr vdf_socket_t kInvalidSocket = INVALID_SOCKET; +#else +#include +#include #include #include #include #include +using vdf_socket_t = int; +static constexpr vdf_socket_t kInvalidSocket = -1; +#endif enum conn_state { WAITING, @@ -26,7 +37,7 @@ enum conn_state { struct vdf_conn { struct vdf_state vdf; - int sock; + vdf_socket_t sock; char read_buf[512]; uint32_t buf_pos; enum conn_state state; @@ -63,6 +74,52 @@ void write_data(struct vdf_conn *conn, const char *buf, size_t size); static volatile bool g_stopping = false; +static int vdf_socket_set_nonblock(vdf_socket_t sock) +{ +#ifdef _WIN32 + u_long mode = 1; + return ioctlsocket(sock, FIONBIO, &mode); +#else + return fcntl(sock, F_SETFL, O_NONBLOCK); +#endif +} + +static int vdf_socket_read(vdf_socket_t sock, char *buf, size_t size) +{ +#ifdef _WIN32 + return recv(sock, buf, static_cast(size), 0); +#else + return read(sock, buf, size); +#endif +} + +static int vdf_socket_write(vdf_socket_t sock, const char *buf, size_t size) +{ +#ifdef _WIN32 + return send(sock, buf, static_cast(size), 0); +#else + return write(sock, buf, size); +#endif +} + +static int vdf_socket_close(vdf_socket_t sock) +{ +#ifdef _WIN32 + return closesocket(sock); +#else + return close(sock); +#endif +} + +static bool vdf_socket_would_block() +{ +#ifdef _WIN32 + return WSAGetLastError() == WSAEWOULDBLOCK; +#else + return errno == EAGAIN || errno == EWOULDBLOCK; +#endif +} + void signal_handler(int sig) { LOG_INFO("Interrupted"); @@ -72,21 +129,24 @@ void signal_handler(int sig) void init_conn(struct vdf_conn *conn, uint32_t ip, int port) { int ret; - struct sockaddr_in sa = { AF_INET, htons(port), { htonl(ip) } }; + struct sockaddr_in sa = {}; + sa.sin_family = AF_INET; + sa.sin_port = htons(static_cast(port)); + sa.sin_addr.s_addr = htonl(ip); conn->sock = socket(AF_INET, SOCK_STREAM, 0); LOG_INFO("Connecting to %s:%d", inet_ntoa(sa.sin_addr), port); ret = connect(conn->sock, (struct sockaddr *)&sa, sizeof(sa)); if (ret < 0) { perror("connect"); - sleep(1); + vdf_sleep(1); return; } - ret = fcntl(conn->sock, F_SETFL, O_NONBLOCK); + ret = vdf_socket_set_nonblock(conn->sock); if (ret < 0) { - perror("fcntl"); - close(conn->sock); - conn->sock = -1; + perror("set_nonblock"); + vdf_socket_close(conn->sock); + conn->sock = kInvalidSocket; return; } conn->state = WAITING; @@ -107,7 +167,7 @@ void init_vdf_client(struct vdf_client *client) continue; } client->conns[i].vdf.idx = i; - client->conns[i].sock = -1; + client->conns[i].sock = kInvalidSocket; memset(client->conns[i].read_buf, 0, sizeof(client->conns[i].read_buf)); client->conns[i].buf_pos = 0; @@ -126,7 +186,7 @@ void clear_vdf_client(struct vdf_client *client) void stop_conn(struct vdf_client *client, struct vdf_conn *conn) { - if (conn->sock >= 0) { + if (conn->sock != kInvalidSocket) { write_data(conn, "STOP", 4); } if (conn->vdf.init_done) { @@ -135,24 +195,24 @@ void stop_conn(struct vdf_client *client, struct vdf_conn *conn) stop_hw_vdf(client->drv, conn->vdf.idx); } conn->state = STOPPED; - LOG_INFO("VDF %d: Stopped at iters=%lu", conn->vdf.idx, conn->vdf.cur_iters); + LOG_INFO("VDF %d: Stopped at iters=%llu", conn->vdf.idx, (unsigned long long)conn->vdf.cur_iters); } void close_conn(struct vdf_conn *conn) { if (conn->state != CLOSED) { - close(conn->sock); - conn->sock = -1; + vdf_socket_close(conn->sock); + conn->sock = kInvalidSocket; conn->state = CLOSED; LOG_INFO("VDF %d: Connection closed", conn->vdf.idx); } } -ssize_t read_data(struct vdf_client *client, struct vdf_conn *conn) +int read_data(struct vdf_client *client, struct vdf_conn *conn) { - ssize_t bytes = read(conn->sock, conn->read_buf + conn->buf_pos, + int bytes = vdf_socket_read(conn->sock, conn->read_buf + conn->buf_pos, sizeof(conn->read_buf) - conn->buf_pos); - if ((bytes < 0 && errno != EAGAIN) || bytes == 0) { + if ((bytes < 0 && !vdf_socket_would_block()) || bytes == 0) { if (bytes == 0) { LOG_ERROR("VDF %d: Unexpected EOF", conn->vdf.idx); } else { @@ -169,7 +229,7 @@ ssize_t read_data(struct vdf_client *client, struct vdf_conn *conn) void write_data(struct vdf_conn *conn, const char *buf, size_t size) { - ssize_t bytes = write(conn->sock, buf, size); + int bytes = vdf_socket_write(conn->sock, buf, size); if (bytes < 0) { perror("write"); throw std::runtime_error("Write error"); @@ -196,7 +256,7 @@ void handle_iters(struct vdf_client *client, struct vdf_conn *conn) iters = strtoul(iters_buf, NULL, 10); if (iters) { - LOG_DEBUG("VDF %d: Requested proof for iters=%lu", conn->vdf.idx, iters); + LOG_DEBUG("VDF %d: Requested proof for iters=%llu", conn->vdf.idx, (unsigned long long)iters); hw_request_proof(&conn->vdf, iters, false); } else { LOG_INFO("VDF %d: Stop requested", conn->vdf.idx); @@ -217,8 +277,8 @@ void handle_iters(struct vdf_client *client, struct vdf_conn *conn) size_t pos = 0; for (size_t i = 0; i < n_proofs; i++) { - pos += snprintf(&iters_str[pos], sizeof(iters_str) - pos, "%s%lu", - i ? ", " : "", conn->vdf.req_proofs[i].iters); + pos += snprintf(&iters_str[pos], sizeof(iters_str) - pos, "%s%llu", + i ? ", " : "", (unsigned long long)conn->vdf.req_proofs[i].iters); if (pos >= sizeof(iters_str) - 1) { break; } @@ -243,7 +303,7 @@ void handle_proofs(struct vdf_client *client, struct vdf_conn *conn) uint8_t data[8 + 8 + 1 + BQFC_FORM_SIZE * 2]; char tl_data[sizeof(data) * 2 + 5] = {0}; - LOG_INFO("VDF %d: Proof retrieved for iters=%lu", conn->vdf.idx, proof->iters); + LOG_INFO("VDF %d: Proof retrieved for iters=%llu", conn->vdf.idx, (unsigned long long)proof->iters); Int64ToBytes(&data[0], proof->iters); Int64ToBytes(&data[8], BQFC_FORM_SIZE); @@ -272,7 +332,7 @@ void handle_proofs(struct vdf_client *client, struct vdf_conn *conn) void handle_conn(struct vdf_client *client, struct vdf_conn *conn) { - ssize_t bytes; + int bytes; char *buf = conn->read_buf; struct vdf_state *vdf = &conn->vdf; @@ -305,7 +365,7 @@ void handle_conn(struct vdf_client *client, struct vdf_conn *conn) memcpy(d_str, &buf[4], d_size); d_str[d_size] = '\0'; if ((uint64_t)bytes != 4 + d_size + 1 + buf[4 + d_size]) { - LOG_ERROR("Bad data size: %zd", bytes); + LOG_ERROR("Bad data size: %d", bytes); throw std::runtime_error("Bad data size"); } @@ -329,7 +389,7 @@ void handle_conn(struct vdf_client *client, struct vdf_conn *conn) if (conn->state == STOPPED) { bytes = read_data(client, conn); if (bytes != 3 || memcmp(buf, "ACK", 3)) { - LOG_ERROR("Bad data size after stop: %zd", bytes); + LOG_ERROR("Bad data size after stop: %d", bytes); } close_conn(conn); } else if (conn->state == CLOSED && !g_stopping) { @@ -386,8 +446,8 @@ void event_loop(struct vdf_client *client) adjust_hw_freq(client->drv, running_mask & ~(1 << i), -1); } - LOG_INFO("VDF %d: Restarting VDF at %lu iters", - vdf->idx, vdf->iters_offset); + LOG_INFO("VDF %d: Restarting VDF at %llu iters", + vdf->idx, (unsigned long long)vdf->iters_offset); start_hw_vdf(client->drv, vdf->D.impl, f->a.impl, f->b.impl, vdf->target_iters - vdf->iters_offset, vdf->idx); } @@ -413,7 +473,7 @@ void event_loop(struct vdf_client *client) } if (chia_vdf_is_emu) { - usleep(50000); + vdf_usleep(50000); } loop_cnt++; } @@ -421,20 +481,9 @@ void event_loop(struct vdf_client *client) int parse_opts(int argc, char **argv, struct vdf_client_opts *opts) { - const struct option long_opts[] = { - {"freq", required_argument, NULL, 1}, - {"voltage", required_argument, NULL, 1}, - {"ip", required_argument, NULL, 1}, - {"vdfs-mask", required_argument, NULL, 1}, - {"vdf-threads", required_argument, NULL, 1}, - {"proof-threads", required_argument, NULL, 1}, - {"list", no_argument, NULL, 1}, - {"auto-freq-period", required_argument, NULL, 1}, - {"max-freq", required_argument, NULL, 1}, - {0} - }; - int long_idx = -1; - int ret; + int argi = 1; + int positional_count = 0; + const char *positionals[2] = {nullptr, nullptr}; opts->voltage = HW_VDF_DEF_VOLTAGE; opts->freq = HW_VDF_DEF_FREQ; @@ -448,32 +497,78 @@ int parse_opts(int argc, char **argv, struct vdf_client_opts *opts) opts->vpo.max_proof_threads = 0; opts->vdfs_mask = 0; - while ((ret = getopt_long(argc, argv, "", long_opts, &long_idx)) == 1) { - if (long_idx == 0) { - opts->freq = strtod(optarg, NULL); - } else if (long_idx == 1) { - opts->voltage = strtod(optarg, NULL); - } else if (long_idx == 2) { - opts->ip = ntohl(inet_addr(optarg)); - } else if (long_idx == 3) { - opts->vdfs_mask = strtoul(optarg, NULL, 0); - } else if (long_idx == 4) { - opts->vpo.max_aux_threads = strtoul(optarg, NULL, 0); - } else if (long_idx == 5) { - opts->vpo.max_proof_threads = strtoul(optarg, NULL, 0); - } else if (long_idx == 6) { + while (argi < argc) { + if (strncmp(argv[argi], "--", 2) != 0) { + if (argv[argi][0] == '-') { + LOG_SIMPLE("Invalid option"); + return -1; + } + if (positional_count < 2) { + positionals[positional_count] = argv[argi]; + } + positional_count++; + argi++; + continue; + } + + const char *name = argv[argi] + 2; + const char *value = nullptr; + char name_buf[32] = {0}; + const char *eq = strchr(name, '='); + + if (eq) { + size_t len = static_cast(eq - name); + if (len == 0 || len >= sizeof(name_buf)) { + LOG_SIMPLE("Invalid option"); + return -1; + } + memcpy(name_buf, name, len); + name = name_buf; + value = eq + 1; + } + + if (!strcmp(name, "list")) { + if (value) { + LOG_SIMPLE("Invalid option"); + return -1; + } opts->do_list = true; - } else if (long_idx == 7) { + argi++; + continue; + } + + if (!value) { + if (argi + 1 >= argc) { + LOG_SIMPLE("Invalid option"); + return -1; + } + value = argv[++argi]; + } + + if (!strcmp(name, "freq")) { + opts->freq = strtod(value, NULL); + } else if (!strcmp(name, "voltage")) { + opts->voltage = strtod(value, NULL); + } else if (!strcmp(name, "ip")) { + opts->ip = ntohl(inet_addr(value)); + } else if (!strcmp(name, "vdfs-mask")) { + opts->vdfs_mask = strtoul(value, NULL, 0); + } else if (!strcmp(name, "vdf-threads")) { + opts->vpo.max_aux_threads = strtoul(value, NULL, 0); + } else if (!strcmp(name, "proof-threads")) { + opts->vpo.max_proof_threads = strtoul(value, NULL, 0); + } else if (!strcmp(name, "auto-freq-period")) { opts->auto_freq = true; - opts->auto_freq_period = strtoul(optarg, NULL, 0); - } else if (long_idx == 8) { - opts->max_freq = strtod(optarg, NULL); + opts->auto_freq_period = strtoul(value, NULL, 0); + } else if (!strcmp(name, "max-freq")) { + opts->max_freq = strtod(value, NULL); + } else { + LOG_SIMPLE("Invalid option"); + return -1; } + argi++; } - if (ret != -1) { - LOG_SIMPLE("Invalid option"); - return -1; - } + if (opts->do_list) { return 0; } @@ -511,14 +606,28 @@ int parse_opts(int argc, char **argv, struct vdf_client_opts *opts) return -1; } - if (optind == argc) { + if (positional_count == 0) { return -1; } - opts->port = atoi(argv[optind]); - if (argc > optind + 1) { - opts->n_vdfs = atoi(argv[optind + 1]); + { + char *end = nullptr; + long parsed_port = strtol(positionals[0], &end, 10); + if (!positionals[0][0] || (end && *end) || parsed_port < 1 || parsed_port > 65535) { + LOG_SIMPLE("Invalid port or VDF count"); + return -1; + } + opts->port = static_cast(parsed_port); + } + if (positional_count > 1) { + char *end = nullptr; + long parsed_n_vdfs = strtol(positionals[1], &end, 10); + if (!positionals[1][0] || (end && *end) || parsed_n_vdfs < 1 || parsed_n_vdfs > 3) { + LOG_SIMPLE("Invalid port or VDF count"); + return -1; + } + opts->n_vdfs = static_cast(parsed_n_vdfs); } - if (!opts->port || opts->n_vdfs < 1 || opts->n_vdfs > 3) { + if (opts->n_vdfs < 1 || opts->n_vdfs > 3) { LOG_SIMPLE("Invalid port or VDF count"); return -1; } @@ -529,7 +638,28 @@ int parse_opts(int argc, char **argv, struct vdf_client_opts *opts) int hw_vdf_client_main(int argc, char **argv) { struct vdf_client client; +#ifndef _WIN32 struct sigaction sa = {0}; +#endif + +#ifdef _WIN32 + struct WinsockGuard { + bool started = false; + + ~WinsockGuard() { + if (started) { + WSACleanup(); + } + } + } winsock_guard; + + WSADATA wsa_data; + if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) { + LOG_SIMPLE("Failed to initialize Winsock"); + return 1; + } + winsock_guard.started = true; +#endif if (parse_opts(argc, argv, &client.opts) < 0) { LOG_SIMPLE("\nUsage: %s [OPTIONS] PORT [N_VDFS]\n" @@ -548,7 +678,8 @@ int hw_vdf_client_main(int argc, char **argv) if (client.opts.do_list) { LOG_SIMPLE("List of available devices:"); - return list_hw() ? 1 : 0; + const int ret = list_hw() ? 1 : 0; + return ret; } client.drv = init_hw(client.opts.freq, client.opts.voltage); @@ -558,9 +689,14 @@ int hw_vdf_client_main(int argc, char **argv) init_vdf_client(&client); +#ifdef _WIN32 + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); +#else sa.sa_handler = signal_handler; sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); +#endif event_loop(&client); diff --git a/src/hw/vdf_driver.cpp b/src/hw/vdf_driver.cpp index 15109165..9709b200 100644 --- a/src/hw/vdf_driver.cpp +++ b/src/hw/vdf_driver.cpp @@ -2,9 +2,9 @@ #include #include #include -#include #include "vdf_driver.hpp" #include "pll_freqs.hpp" +#include "hw_util.hpp" #define VR_I2C_ADDR 0x38 #define CS_I2C_ADDR 0x70 @@ -372,7 +372,7 @@ bool VdfDriver::SetPLLFrequency(double frequency, uint32_t entry_index) { while (((pll_status >> CLOCK_STATUS_DIVACK_BIT) & 0x1) == 0) { ret_val = RegRead(CLOCK_STATUS_REG_OFFSET, pll_status); read_attempts++; - usleep(1000); + vdf_usleep(1000); if ((read_attempts > 4) || (ret_val != 0)) { fprintf(stderr, "SetPLLFrequency pll div never ack'd\n"); return false; @@ -388,7 +388,7 @@ bool VdfDriver::SetPLLFrequency(double frequency, uint32_t entry_index) { while (((pll_status >> CLOCK_STATUS_LOCK_BIT) & 0x1) == 0) { ret_val = RegRead(CLOCK_STATUS_REG_OFFSET, pll_status); read_attempts++; - usleep(1000); + vdf_usleep(1000); if ((read_attempts > 4) || (ret_val != 0)) { fprintf(stderr, "SetPLLFrequency pll never locked\n"); return false; @@ -449,7 +449,7 @@ int VdfDriver::Reset(uint32_t sleep_duration = 1000) { fprintf(stderr, "Reset failed to set gpio, %d\n", ret_val); return ret_val; } - usleep(sleep_duration); + vdf_usleep(sleep_duration); // Some boards will contain FT4222H chips with the OTP programmed such // that GPIO2 (VDF_RST_N) is configured as an open drain output. In // this case, GPIO2 powers up actively driving out low. In order to @@ -479,7 +479,7 @@ int VdfDriver::Reset(uint32_t sleep_duration = 1000) { if (ret_val != 0) { fprintf(stderr, "Reset failed to tri-state gpio, %d\n", ret_val); } - usleep(100000); + vdf_usleep(100000); return ret_val; } @@ -604,7 +604,7 @@ double VdfDriver::GetBoardCurrent() { return 0.0; } - usleep(10000); + vdf_usleep(10000); ret_val = I2CReadReg(CS_I2C_ADDR, 0x0, 2, (uint8_t*)(&cs)); if (ret_val != 0) { diff --git a/src/parameters.h b/src/parameters.h index 64bcea4e..bf700350 100644 --- a/src/parameters.h +++ b/src/parameters.h @@ -32,10 +32,10 @@ extern bool enable_all_instructions; #include #include #include -#include #include +#include #if defined(_MSC_VER) -#include +#include #endif inline std::atomic bAVX2{false}; @@ -113,66 +113,82 @@ inline void init_avx_flags() ); #endif #endif + uint64_t xcr0 = 0; + bool osxsave_enabled = false; +#if defined(_MSC_VER) + const int OSXSAVE = 1 << 27; + osxsave_enabled = ((info1[2] & OSXSAVE) == OSXSAVE); + if (osxsave_enabled) { + xcr0 = static_cast(_xgetbv(0)); + } +#elif defined(__GNUC__) || defined(__clang__) + const int OSXSAVE = 1 << 27; + osxsave_enabled = ((info1[2] & OSXSAVE) == OSXSAVE); + if (osxsave_enabled) { + uint32_t eax = 0; + uint32_t edx = 0; + __asm__ __volatile__ ( + ".byte 0x0f, 0x01, 0xd0" + : "=a"(eax), "=d"(edx) + : "c"(0) + ); + xcr0 = (static_cast(edx) << 32) | eax; + } +#endif + + constexpr uint64_t XCR0_XMM_MASK = (1ULL << 1); + constexpr uint64_t XCR0_YMM_MASK = (1ULL << 2); + constexpr uint64_t XCR0_AVX_MASK = XCR0_XMM_MASK | XCR0_YMM_MASK; + constexpr uint64_t XCR0_AVX512_MASK = XCR0_AVX_MASK | (1ULL << 5) | (1ULL << 6) | (1ULL << 7); + + const int AVX = 1 << 28; const int AVX2 = 1<<5; const int ADX = 1<<19; const int AVX512F = 1<<16; const int AVX512IFMA = 1<<21; - const int XSAVE = 1<<26; - const int OSXSAVE = 1<<27; - const int AVX = 1<<28; + bool avxbit = ((info1[2] & AVX) == AVX); bool avx2bit = ((info[1] & AVX2) == AVX2); bool adxbit = ((info[1] & ADX) == ADX); bool avx512fbit = ((info[1] & AVX512F) == AVX512F); bool avx512ifmabit = ((info[1] & AVX512IFMA) == AVX512IFMA); - bool xsavebit = ((info1[2] & XSAVE) == XSAVE); - bool osxsavebit = ((info1[2] & OSXSAVE) == OSXSAVE); - bool avxbit = ((info1[2] & AVX) == AVX); - bool os_avx2_state = false; - bool os_avx512_state = false; - if (xsavebit && osxsavebit) { -#if defined(_MSC_VER) - unsigned long long xcr0 = _xgetbv(0); -#elif defined(__GNUC__) || defined(__clang__) - uint32_t eax = 0; - uint32_t edx = 0; - __asm__ __volatile__ ( - ".byte 0x0f, 0x01, 0xd0" - : "=a"(eax), "=d"(edx) - : "c"(0) - ); - uint64_t xcr0 = (uint64_t(eax) | (uint64_t(edx) << 32)); -#else - uint64_t xcr0 = 0; -#endif - const uint64_t xcr0_avx = (uint64_t(1) << 1) | (uint64_t(1) << 2); - const uint64_t xcr0_avx512 = xcr0_avx | (uint64_t(1) << 5) | (uint64_t(1) << 6) | (uint64_t(1) << 7); - os_avx2_state = (xcr0 & xcr0_avx) == xcr0_avx; - os_avx512_state = (xcr0 & xcr0_avx512) == xcr0_avx512; - } + bool avx_os_state = osxsave_enabled && ((xcr0 & XCR0_AVX_MASK) == XCR0_AVX_MASK); + bool avx512_os_state = osxsave_enabled && ((xcr0 & XCR0_AVX512_MASK) == XCR0_AVX512_MASK); if (disable_avx2) { bAVX2.store(false, std::memory_order_relaxed); } else if (force_avx2) { - bAVX2.store(true, std::memory_order_relaxed); + // Force mode bypasses CPUID feature gating but still must respect OS xstate. + bAVX2.store(avx_os_state, std::memory_order_relaxed); } else { - bAVX2.store(avx2bit && adxbit && avxbit && os_avx2_state, std::memory_order_relaxed); + bAVX2.store(avxbit && avx2bit && adxbit && avx_os_state, std::memory_order_relaxed); } if (bAVX2.load(std::memory_order_relaxed) && should_log_avx()) { - std::fprintf(stderr, "AVX2 enabled (avx2=%d adx=%d avx=%d os_avx2=%d)\n", avx2bit ? 1 : 0, adxbit ? 1 : 0, avxbit ? 1 : 0, os_avx2_state ? 1 : 0); + std::fprintf(stderr, "AVX2 enabled (avx=%d avx2=%d adx=%d osxsave=%d xcr0=0x%llx)\n", + avxbit ? 1 : 0, + avx2bit ? 1 : 0, + adxbit ? 1 : 0, + osxsave_enabled ? 1 : 0, + static_cast(xcr0)); } if (disable_avx512) { enable_avx512_ifma.store(false, std::memory_order_relaxed); } else if (force_avx512) { - enable_avx512_ifma.store(true, std::memory_order_relaxed); + // Force mode bypasses CPUID feature gating but still must respect OS xstate. + enable_avx512_ifma.store(avx512_os_state, std::memory_order_relaxed); } else if (enable_avx512) { - enable_avx512_ifma.store(avx512fbit && avx512ifmabit && avxbit && os_avx512_state, std::memory_order_relaxed); + enable_avx512_ifma.store(avxbit && avx512fbit && avx512ifmabit && avx512_os_state, std::memory_order_relaxed); } else { enable_avx512_ifma.store(false, std::memory_order_relaxed); } if (enable_avx512_ifma.load(std::memory_order_relaxed) && should_log_avx()) { - std::fprintf(stderr, "AVX512 IFMA enabled (f=%d ifma=%d avx=%d os_avx512=%d)\n", avx512fbit ? 1 : 0, avx512ifmabit ? 1 : 0, avxbit ? 1 : 0, os_avx512_state ? 1 : 0); + std::fprintf(stderr, "AVX512 IFMA enabled (avx=%d f=%d ifma=%d osxsave=%d xcr0=0x%llx)\n", + avxbit ? 1 : 0, + avx512fbit ? 1 : 0, + avx512ifmabit ? 1 : 0, + osxsave_enabled ? 1 : 0, + static_cast(xcr0)); } #elif defined(ARCH_ARM) bAVX2.store(false, std::memory_order_relaxed); diff --git a/src/prover_runtime.cpp b/src/prover_runtime.cpp new file mode 100644 index 00000000..79ed06ab --- /dev/null +++ b/src/prover_runtime.cpp @@ -0,0 +1,18 @@ +#include "vdf_base.hpp" + +Prover::Prover(Segment segm, integer D) { + this->segm = segm; + this->D = D; + this->num_iterations = segm.length; + is_finished = false; +} + +bool Prover::IsFinished() { + return is_finished; +} + +form Prover::GetProof() { + return proof; +} + +ParallelProver::ParallelProver(Segment segm, integer D) : Prover(segm, D) {} diff --git a/src/vdf_base.hpp b/src/vdf_base.hpp index c69131e4..fde85d13 100644 --- a/src/vdf_base.hpp +++ b/src/vdf_base.hpp @@ -278,5 +278,4 @@ void VerifyWesolowskiProof(integer &D, form x, form y, form proof, uint64_t iter void Int64ToBytes(uint8_t *result, uint64_t input); void Int32ToBytes(uint8_t *result, uint32_t input); -#include "prover_impl.hpp" #endif // VDF_BASE_H diff --git a/src/vdf_base_hw.cpp b/src/vdf_base_hw.cpp new file mode 100644 index 00000000..3f2a82af --- /dev/null +++ b/src/vdf_base_hw.cpp @@ -0,0 +1,10 @@ +#include "vdf_base.hpp" +#include "alloc.hpp" + +#include + +void VdfBaseInit(void) +{ + init_gmp(); + fesetround(FE_TOWARDZERO); +} diff --git a/src/vdf_hw_symbol_anchors.cpp b/src/vdf_hw_symbol_anchors.cpp new file mode 100644 index 00000000..0ae53260 --- /dev/null +++ b/src/vdf_hw_symbol_anchors.cpp @@ -0,0 +1,24 @@ +#include "verifier.h" +#include "prover_base.hpp" +#include "prover_parallel.hpp" + +bool hw_dummy_form_check_valid(form& f, integer& d) +{ + return f.check_valid(d); +} + +form hw_dummy_get_proof(ParallelProver& p) +{ + p.GenerateProof(); + return p.GetProof(); +} + +void hw_dummy_verify_wesolowski(integer& D, form x, form y, form proof, uint64_t iters, bool& is_valid) +{ + VerifyWesolowskiProof(D, x, y, proof, iters, is_valid); +} + +integer hw_dummy_get_B(const integer& D, form& x, form& y) +{ + return GetB(D, x, y); +}