├── .github └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── bin └── .gitkeep ├── build.ps1 ├── get-miner.sh ├── make.sh ├── run-miner.ps1 ├── run-miner.sh ├── src ├── blake3.cu ├── blake3 │ ├── blake3-common.hpp │ ├── inlined-blake.hpp │ └── original-blake.hpp ├── constants.h ├── getopt.h ├── log.h ├── main.cu ├── messages.h ├── mining.h ├── pow.h ├── template.h └── worker.h └── test └── main.c /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build artifacts 2 | on: [push] 3 | 4 | jobs: 5 | build-linux-artifact: 6 | name: build-linux-artifact 7 | runs-on: ubuntu-18.04 8 | steps: 9 | - uses: actions/checkout@v2 10 | 11 | - uses: Jimver/cuda-toolkit@v0.2.5 12 | id: cuda-toolkit 13 | with: 14 | cuda: '11.3.0' 15 | sub-packages: '["nvcc"]' 16 | method: 'network' 17 | 18 | - name: Install Dependency 19 | run: | 20 | temp_file=$(mktemp --suffix=.deb) 21 | curl -L https://github.com/conan-io/conan/releases/latest/download/conan-ubuntu-64.deb -o $temp_file 22 | sudo apt install $temp_file 23 | 24 | - name: Build miner 25 | run: ./make.sh 26 | 27 | - name: Rename miner 28 | run: mv bin/gpu-miner bin/gpu-miner_$(git rev-parse --short "$GITHUB_SHA") 29 | 30 | - uses: actions/upload-artifact@v2 31 | with: 32 | name: linux-binary 33 | path: bin/gpu-miner_* 34 | 35 | build-windows-artifact: 36 | name: build-windows-artifact 37 | runs-on: windows-latest 38 | steps: 39 | - uses: actions/checkout@v2 40 | with: 41 | submodules: recursive 42 | 43 | - uses: ilammy/msvc-dev-cmd@v1 44 | with: 45 | arch: amd64 46 | 47 | - uses: Jimver/cuda-toolkit@v0.2.5 48 | id: cuda-toolkit 49 | with: 50 | cuda: '11.3.0' 51 | sub-packages: '["nvcc", "cudart", "visual_studio_integration"]' 52 | method: 'network' 53 | 54 | - run: echo "conan==1.42.2" > requirements.txt 55 | 56 | - uses: actions/setup-python@v2 57 | with: 58 | python-version: '3.x' 59 | architecture: 'x64' 60 | cache: 'pip' 61 | 62 | - name: Install Dependency 63 | run: pip install -r requirements.txt 64 | - name: Build Miner 65 | run: .\build.ps1 66 | 67 | - name: Rename Miner 68 | run: | 69 | $fileName = git rev-parse --short HEAD 70 | cp bin/gpu-miner.exe "bin/gpu-miner_$fileName.exe" 71 | 72 | - uses: actions/upload-artifact@v2 73 | with: 74 | name: windows-binary 75 | path: bin/gpu-miner_*.exe 76 | 77 | release: 78 | name: release 79 | runs-on: ubuntu-latest 80 | # If both artifacts were built properly and this is a tag 81 | if: ${{ needs.build-linux-artifact.result == 'success' && needs.build-linux-artifact.result == 'success' && startsWith(github.ref, 'refs/tags/') }} 82 | needs: [build-linux-artifact, build-windows-artifact] 83 | steps: 84 | - uses: actions/checkout@v2 85 | 86 | - name: Get linux artifact 87 | uses: actions/download-artifact@v2 88 | with: 89 | name: linux-binary 90 | 91 | - name: Get Windows artifact 92 | uses: actions/download-artifact@v2 93 | with: 94 | name: windows-binary 95 | 96 | - name: Get the version (Release prep) 97 | id: get_version 98 | run: | 99 | version=$(echo ${GITHUB_REF/refs\/tags\//} | cut -c 2-) 100 | echo ::set-output name=VERSION::$version 101 | shell: bash 102 | 103 | - name: Generate miners checksums (Release prep) 104 | run: | 105 | filename=$(git rev-parse --short HEAD) 106 | mv "gpu-miner_$filename" "alephium-${{ steps.get_version.outputs.VERSION }}-cuda-miner-linux" 107 | mv "gpu-miner_$filename.exe" "alephium-${{ steps.get_version.outputs.VERSION }}-cuda-miner-windows.exe" 108 | sha256sum "alephium-${{ steps.get_version.outputs.VERSION }}-cuda-miner-linux" > "alephium-${{ steps.get_version.outputs.VERSION }}-cuda-miner-linux.checksum" 109 | sha256sum "alephium-${{ steps.get_version.outputs.VERSION }}-cuda-miner-windows.exe" > "alephium-${{ steps.get_version.outputs.VERSION }}-cuda-miner-windows.exe.checksum" 110 | ls -la 111 | 112 | - name: Release 113 | uses: softprops/action-gh-release@v1 114 | with: 115 | files: | 116 | alephium-${{ steps.get_version.outputs.VERSION }}-cuda-miner-linux 117 | alephium-${{ steps.get_version.outputs.VERSION }}-cuda-miner-linux.checksum 118 | alephium-${{ steps.get_version.outputs.VERSION }}-cuda-miner-windows.exe 119 | alephium-${{ steps.get_version.outputs.VERSION }}-cuda-miner-windows.exe.checksum 120 | 121 | buildx_and_push_to_registry: 122 | name: Push Docker image to Docker Hub 123 | runs-on: ubuntu-latest 124 | if: ${{ needs.release.result == 'success' }} 125 | needs: release 126 | steps: 127 | - name: Check out the repo 128 | uses: actions/checkout@v2 129 | 130 | - uses: docker/setup-qemu-action@v1 131 | - uses: docker/setup-buildx-action@v1 132 | 133 | - name: Get the version 134 | id: get_version 135 | run: | 136 | version=$(git describe --tags --abbrev=0) 137 | echo $version 138 | echo ${version:1} 139 | echo ::set-output name=VERSION::$version 140 | echo ::set-output name=VERSION-NO-V::${version:1} 141 | shell: bash 142 | 143 | - name: Log in to Docker Hub 144 | uses: docker/login-action@v1 145 | with: 146 | username: ${{ secrets.DOCKER_USERNAME }} 147 | password: ${{ secrets.DOCKER_PASSWORD }} 148 | 149 | - name: Build and publish docker image 150 | uses: docker/build-push-action@v2 151 | with: 152 | context: . 153 | file: ./Dockerfile 154 | build-args: RELEASE=${{ steps.get_version.outputs.VERSION-NO-V }} 155 | platforms: linux/amd64 156 | tags: | 157 | docker.io/alephium/gpu-miner:${{ steps.get_version.outputs.VERSION }} 158 | docker.io/alephium/gpu-miner:latest 159 | push: true 160 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | lib/ 3 | .vscode/ 4 | build/ 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alephium/gpu-miner/d5919b7f419e00593cfdc71b034ebffb017362b3/.gitmodules -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10 FATAL_ERROR) 2 | project(gpu-miner LANGUAGES CXX CUDA) 3 | set(CMAKE_VERBOSE_MAKEFILE TRUE) 4 | 5 | # Add miner version at compile time, priority is tag, short hash and finally unknown 6 | find_package(Git) 7 | if(Git_FOUND) 8 | # Find current tag name : 9 | execute_process(COMMAND 10 | "${GIT_EXECUTABLE}" describe --tags --abbrev=0 --exact-match 11 | WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" 12 | OUTPUT_VARIABLE miner-version 13 | ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) 14 | # If no tag was found, set to hash abbreviation 15 | if(NOT miner-version) 16 | execute_process(COMMAND 17 | "${GIT_EXECUTABLE}" describe --match=NeVeRmAtCh --always --abbrev=7 18 | WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" 19 | OUTPUT_VARIABLE miner-version 20 | ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) 21 | set(miner-version canary-${miner-version}) 22 | else() 23 | set(miner-version release-${miner-version}) 24 | endif() 25 | else() 26 | set(miner-version unknown) 27 | endif() 28 | 29 | message(miner version : ${miner-version}) 30 | 31 | #Setup conan 32 | if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") 33 | message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan") 34 | file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/master/conan.cmake" 35 | "${CMAKE_BINARY_DIR}/conan.cmake") 36 | endif() 37 | include(${CMAKE_BINARY_DIR}/conan.cmake) 38 | conan_cmake_run(REQUIRES libuv/1.42.0 39 | BASIC_SETUP) 40 | 41 | # Target definitions 42 | add_executable(gpu-miner src/main.cu) 43 | set_property(TARGET gpu-miner PROPERTY CXX_STANDARD 11) 44 | set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} --ptxas-options=-v") 45 | target_compile_definitions(gpu-miner PUBLIC MINER_VERSION="${miner-version}") 46 | target_link_libraries(gpu-miner PRIVATE ${CONAN_LIBS}) 47 | #Copy binary to bin directory 48 | add_custom_command(TARGET gpu-miner 49 | POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_SOURCE_DIR}/bin) 50 | 51 | add_executable(blake3-test src/blake3.cu src/blake3/blake3-common.hpp) 52 | set_property(TARGET blake3-test PROPERTY CXX_STANDARD 11) 53 | target_compile_definitions(blake3-test PUBLIC BLAKE3_TEST) 54 | target_link_libraries(blake3-test PRIVATE ${CONAN_LIBS}) 55 | #Copy binary to bin directory 56 | add_custom_command(TARGET blake3-test 57 | POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_SOURCE_DIR}/bin) 58 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nvidia/cuda:11.0.3-devel-ubuntu20.04 AS builder 2 | 3 | WORKDIR /src 4 | 5 | RUN apt update && \ 6 | DEBIAN_FRONTEND="noninteractive" apt-get -y install cmake tzdata curl 7 | 8 | RUN curl -L https://github.com/conan-io/conan/releases/download/1.65.0/conan-ubuntu-64.deb -o out.deb && \ 9 | DEBIAN_FRONTEND=sudo apt-get -y install ./out.deb 10 | 11 | COPY ./ ./ 12 | RUN ./make.sh 13 | 14 | FROM nvidia/cuda:11.0.3-base-ubuntu20.04 15 | 16 | RUN apt update && \ 17 | DEBIAN_FRONTEND="noninteractive" apt-get -y install tzdata 18 | 19 | COPY --from=builder /src/bin/gpu-miner /gpu-miner 20 | 21 | USER root 22 | 23 | ENTRYPOINT ["/gpu-miner"] 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(OS),Windows_NT) # is Windows_NT on XP, 2000, 7, Vista, 10... 2 | target := windows-gpu 3 | else 4 | target := linux-gpu # same as "uname -s" 5 | endif 6 | 7 | gpu: $(target) 8 | 9 | windows-gpu: 10 | @powershell ./build.ps1 11 | 12 | linux-gpu: 13 | ./make.sh 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gpu-miner 2 | 3 | CUDA capable PoW miner for Alephium. 4 | 5 | Please make sure that you have installed Nvidia driver for you GPU. You could verify that by running the `nvidia-smi` command. 6 | 7 | ### Ubuntu miner from source code 8 | 9 | 1. Build the miner by running 10 | 11 | ```shell 12 | curl -L https://github.com/alephium/gpu-miner/raw/master/get-miner.sh | bash 13 | ``` 14 | 15 | 2. Start the miner 16 | 17 | ```shell 18 | gpu-miner/run-miner.sh 19 | ``` 20 | 21 | You could specify the miner api with `-a broker_ip` and `-p broker_port` parameters, GPU indexes with `-g 1 2`. 22 | 23 | ### Windows miner from source code 24 | 25 | 1. Install [Visual Studio Build Tools 2019](https://visualstudio.microsoft.com/vs/older-downloads/#visual-studio-2019-and-other-products), making sure to select [C++ CMake tools for Windows](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=msvc-170#installation) during the installation. 26 | 2. Install [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads?target_os=Windows&target_arch=x86_64) (11.5 was tested and working) 27 | 3. Install [conan](https://docs.conan.io/en/latest/installation.html) 28 | 4. Build gpu-miner: 29 | 1. Clone gpu-miner to local 30 | 31 | ``` shell 32 | git clone https://github.com/alephium/gpu-miner.git 33 | ``` 34 | 2. Open a powershell window, and launch the build script: 35 | 36 | ```shell 37 | cd your-gpu-miner-dir 38 | .\build.ps1 39 | ``` 40 | Executable file will be generated in `your-gpu-miner-dir/bin/` directory. 41 | 42 | 5. Start the miner in a powershell window : 43 | ```shell 44 | .\run-miner.ps1 45 | ``` 46 | 47 | If you have any questions, please reach out to us on Discord. 48 | 49 | ### Pre-built miner 50 | 51 | You could also download and run the pre-built miner from [Github release page](https://github.com/alephium/gpu-miner/releases). Note that your anti-virus might warn about the pre-built miner. 52 | -------------------------------------------------------------------------------- /bin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alephium/gpu-miner/d5919b7f419e00593cfdc71b034ebffb017362b3/bin/.gitkeep -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | # requires powershell3 or later 2 | 3 | cd $PSScriptRoot 4 | 5 | mkdir -p build 6 | cd build >$null 2>&1 7 | # Specify the x64 architecture, otherwise can't find cuda 8 | cmake .. -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 16 2019" -A x64 9 | cmake --build . --config Release --target gpu-miner 10 | cd .. 11 | -------------------------------------------------------------------------------- /get-miner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | if ! command -v nvidia-smi &> /dev/null 6 | then 7 | echo "Please install nvidia driver first" 8 | exit 1 9 | fi 10 | 11 | echo "Installing build-essential, python3-pip and nvidia-cuda-toolkit" 12 | sudo apt install -y build-essential nvidia-cuda-toolkit cmake 13 | 14 | echo "Installing conan" 15 | temp_file=$(mktemp --suffix=.deb) 16 | curl -L https://github.com/conan-io/conan/releases/latest/download/conan-ubuntu-64.deb -o $temp_file 17 | sudo apt install $temp_file 18 | rm -f $temp_file 19 | 20 | echo "Git cloning gpu-miner" 21 | git clone https://github.com/alephium/gpu-miner.git 22 | 23 | echo "Building the gpu miner" 24 | ./gpu-miner/make.sh 25 | 26 | echo "Your miner is built, you could run it with: gpu-miner/run-miner.sh" 27 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Bash script to build miner 4 | 5 | cd $(dirname "$0") 6 | 7 | echoerr() { echo "$@" 1>&2; } 8 | 9 | # cmake dep 10 | if ! command -v cmake &> /dev/null 11 | then 12 | echo -n "CMake could not be found, trying to install automatically..." 13 | # Try installing 14 | (sudo apt-get update >/dev/null && sudo apt-get -y install cmake >/dev/null) || ((echoerr "Could not install cmake, exiting") && exit) 15 | # Die if not found 16 | (command -v cmake &>/dev/null) || ((echoerr "Installed CMake not found, exiting") && exit) 17 | echo "Success !" 18 | fi 19 | 20 | # conan dep 21 | if ! command -v conan &> /dev/null 22 | then 23 | echo -n "conan could not be found, trying to install automatically..." 24 | # Try installing 25 | (temp_file=$(mktemp --suffix=.deb) && \ 26 | curl -s -L https://github.com/conan-io/conan/releases/latest/download/conan-ubuntu-64.deb -o $temp_file && \ 27 | sudo apt-get -y install $temp_file >/dev/null) || ((echoerr "Could not install conan, exiting") && exit) 28 | # Die if not found 29 | (command -v cmake &>/dev/null) || ((echoerr "Installed conan not found, exiting") && exit) 30 | echo "Success !" 31 | fi 32 | 33 | mkdir -p build 34 | cd build 35 | cmake .. -DCMAKE_BUILD_TYPE=Release 36 | cmake --build . --config Release --target gpu-miner 37 | -------------------------------------------------------------------------------- /run-miner.ps1: -------------------------------------------------------------------------------- 1 |  2 | $API_HOST="127.0.0.1" 3 | $API_KEY="0000000000000000000000000000000000000000000000000000000000000000" 4 | 5 | $KEY_HEADER=@{ 6 | 'X-API-KEY' = $API_KEY 7 | } 8 | 9 | $node=Invoke-RestMethod -Uri "http://$($API_HOST):12973/infos/self-clique" -Method GET -Headers $KEY_HEADER -ErrorAction SilentlyContinue 10 | 11 | if ($node -eq $null) { 12 | Write-Host "Your full node is not running" 13 | Exit 1 14 | } 15 | 16 | if ( ( -Not [bool]($node.PSobject.Properties.name -match "synced")) -or $node.synced -ne "True"){ 17 | Write-Host "Your full node is not synced" 18 | Exit 1 19 | } 20 | 21 | $addresses=$(Invoke-RestMethod -Uri "http://$($API_HOST):12973/miners/addresses" -Method GET -Headers $KEY_HEADER -ErrorAction SilentlyContinue).addresses 22 | 23 | if ($addresses -eq $null) { 24 | Write-Host "Miner addresses are not set" 25 | Exit 1 26 | } 27 | 28 | $miner_pid=$null 29 | # Use try-finally to kill the miner automatically when the script stops (ctrl+c only) 30 | try{ 31 | while($true){ 32 | # if we don't have an associated PID, spawn the miner and wait 10 seconds to ensure it started mining properly 33 | if ($miner_pid -eq $null){ 34 | $miner_pid = (Start-Process "$PSScriptRoot\bin\gpu-miner.exe" "-a $($API_HOST)" -PassThru).ID 35 | Start-Sleep -Seconds 10 36 | } 37 | 38 | # Check if the process died 39 | if (-not (Get-Process -Id $miner_pid -ErrorAction SilentlyContinue)) { 40 | Write-Host "Miner died, restarting it..." 41 | $miner_pid=$null 42 | continue 43 | } 44 | 45 | # Check if GPU usage stalled (in some cases of error -4095, the process doesn't die, but GPU usage stops) 46 | $gpu_usage=0 47 | $usage_threshold=75 # Expect at least 75% gpu usage at all times 48 | ((Get-Counter "\GPU Engine(pid_$($miner_pid)*engtype_Cuda)\Utilization Percentage").CounterSamples | where CookedValue).CookedValue | 49 | foreach { $gpu_usage = 0 } { $gpu_usage += [math]::Round($_,2) } 50 | 51 | Write-Output "Process $($miner_pid) GPU Engine Usage $($gpu_usage)%" 52 | 53 | if ($gpu_usage -lt $usage_threshold -and $miner_pid -ne $null){ 54 | Write-Host "Miner stalled, restarting it" 55 | Stop-Process -Id $miner_pid -ErrorAction SilentlyContinue 56 | $miner_pid=$null 57 | continue 58 | } 59 | # Sleep 10 seconds before trying again 60 | Start-Sleep -Seconds 10 61 | } 62 | } 63 | finally 64 | { 65 | if ($miner_pid -ne $null) { 66 | Stop-Process -Id $miner_pid -ErrorAction SilentlyContinue 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /run-miner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | API_KEY="0000000000000000000000000000000000000000000000000000000000000000" 6 | 7 | if ! command -v jq &> /dev/null 8 | then 9 | echo "Installing jq" 10 | sudo apt install -y jq 11 | fi 12 | 13 | node=$(curl --silent -X 'GET' "http://${2:-127.0.0.1}:12973/infos/self-clique" -H "X-API-KEY: $API_KEY") 14 | if [ -z "$node" ]; then 15 | echo "Your full node is not running" 16 | exit 1 17 | fi 18 | 19 | synced=$(echo $node | jq '.synced') 20 | if [ -z "$synced" ] || [ "$synced" != "true" ]; then 21 | echo "Your full node is not synced or API key is not correct: $node" 22 | exit 1 23 | fi 24 | 25 | addresses=$(curl --silent -X 'GET' "http://${2:-127.0.0.1}:12973/miners/addresses" -H "X-API-KEY: $API_KEY" | jq '.addresses') 26 | if [ -z "$addresses" ]; then 27 | echo "Miner addresses are not set" 28 | exit 1 29 | fi 30 | 31 | SCRIPT_DIR=`dirname "$BASH_SOURCE"` 32 | 33 | echo "Launching the miner and restart automatically if it crashes" 34 | until $SCRIPT_DIR/bin/gpu-miner $*; do 35 | echo "Miner crashed with exit code $?. Respawning.." >&2 36 | sleep 1 37 | done 38 | -------------------------------------------------------------------------------- /src/blake3.cu: -------------------------------------------------------------------------------- 1 | #ifndef ALEPHIUM_BLAKE3_CU 2 | #define ALEPHIUM_BLAKE3_CU 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "constants.h" 10 | #include "messages.h" 11 | 12 | 13 | // Include both blake implementations 14 | #include "blake3/inlined-blake.hpp" 15 | #include "blake3/original-blake.hpp" 16 | 17 | #ifdef BLAKE3_TEST 18 | #include 19 | int main() 20 | { 21 | cudaProfilerStart(); 22 | blob_t target; 23 | hex_to_bytes("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", &target); 24 | 25 | inline_blake::blake3_hasher *hasher; 26 | inline_blake::blake3_hasher *device_hasher; 27 | TRY(cudaMallocHost(&hasher, sizeof(inline_blake::blake3_hasher))); 28 | TRY(cudaMalloc(&device_hasher, sizeof(inline_blake::blake3_hasher))); 29 | 30 | bzero(hasher->buf, BLAKE3_BUF_CAP); 31 | memcpy(hasher->target, target.blob, target.len); 32 | hasher->from_group = 0; 33 | hasher->to_group = 3; 34 | 35 | cudaStream_t stream; 36 | TRY(cudaStreamCreate(&stream)); 37 | TRY(cudaMemcpyAsync(device_hasher, hasher, sizeof(blake3_hasher), cudaMemcpyHostToDevice, stream)); 38 | inline_blake::blake3_hasher_mine<<<10, 1024, 0, stream>>>(device_hasher); 39 | TRY(cudaStreamSynchronize(stream)); 40 | 41 | TRY(cudaMemcpy(hasher, device_hasher, sizeof(blake3_hasher), cudaMemcpyDeviceToHost)); 42 | char *hash_string1 = bytes_to_hex(hasher->hash, 32); 43 | printf("good: %d\n", hasher->found_good_hash); 44 | printf("nonce: %d\n", hasher->buf[0]); 45 | printf("count: %d\n", hasher->hash_count); 46 | printf("%s\n", hash_string1); // 0003119e5bf02115e1c8496008fbbcec4884e0be7f9dc372cd4316a51d065283 47 | cudaProfilerStop(); 48 | } 49 | #endif // BLAKE3_TEST 50 | 51 | // Beginning of GPU Architecture definitions 52 | inline int get_sm_cores(int major, int minor) 53 | { 54 | // Defines for GPU Architecture types (using the SM version to determine 55 | // the # of cores per SM 56 | typedef struct 57 | { 58 | int SM; // 0xMm (hexidecimal notation), M = SM Major version, 59 | // and m = SM minor version 60 | int Cores; 61 | } sSMtoCores; 62 | 63 | sSMtoCores nGpuArchCoresPerSM[] = { 64 | {0x30, 192}, 65 | {0x32, 192}, 66 | {0x35, 192}, 67 | {0x37, 192}, 68 | {0x50, 128}, 69 | {0x52, 128}, 70 | {0x53, 128}, 71 | {0x60, 64}, 72 | {0x61, 128}, 73 | {0x62, 128}, 74 | {0x70, 64}, 75 | {0x72, 64}, 76 | {0x75, 64}, 77 | {0x80, 64}, 78 | {0x86, 128}, 79 | {-1, -1}}; 80 | 81 | int index = 0; 82 | 83 | while (nGpuArchCoresPerSM[index].SM != -1) 84 | { 85 | if (nGpuArchCoresPerSM[index].SM == ((major << 4) + minor)) 86 | { 87 | return nGpuArchCoresPerSM[index].Cores; 88 | } 89 | 90 | index++; 91 | } 92 | 93 | // If we don't find the values, we default use the previous one 94 | // to run properly 95 | printf( 96 | "MapSMtoCores for SM %d.%d is undefined." 97 | " Default to use %d Cores/SM\n", 98 | major, minor, nGpuArchCoresPerSM[index - 1].Cores); 99 | return nGpuArchCoresPerSM[index - 1].Cores; 100 | } 101 | 102 | int get_device_cores(int device_id) 103 | { 104 | cudaDeviceProp props; 105 | cudaGetDeviceProperties(&props, device_id); 106 | 107 | int cores_size = get_sm_cores(props.major, props.minor) * props.multiProcessorCount; 108 | return cores_size; 109 | } 110 | 111 | void config_cuda(int device_id, int *grid_size, int *block_size, bool* is_inline_miner) 112 | { 113 | cudaSetDevice(device_id); 114 | 115 | cudaDeviceProp props; 116 | cudaGetDeviceProperties(&props, device_id); 117 | 118 | // If using a 2xxx or 3xxx card, use the new grid calc 119 | bool use_rtx_grid_bloc = ((props.major << 4) + props.minor) >= 0x75; 120 | 121 | // If compiling for windows, override the test and force the new calc 122 | #ifdef _WIN32 123 | use_rtx_grid_bloc = true; 124 | #endif 125 | 126 | // If compiling for linux, and we're not using the RTX grid block, force the original miner, otherwise use the inlined one 127 | #ifdef __linux__ 128 | *is_inline_miner = use_rtx_grid_bloc; 129 | #else 130 | *is_inline_miner = true; 131 | #endif 132 | if(*is_inline_miner) { 133 | cudaOccupancyMaxPotentialBlockSize(grid_size, block_size, inline_blake::blake3_hasher_mine); 134 | } else { 135 | cudaOccupancyMaxPotentialBlockSize(grid_size, block_size, ref_blake::blake3_hasher_mine); 136 | } 137 | 138 | int cores_size = get_device_cores(device_id); 139 | if (use_rtx_grid_bloc) { 140 | *grid_size = props.multiProcessorCount * 2; 141 | *block_size = cores_size / *grid_size * 4; 142 | } else { 143 | *grid_size = cores_size / *block_size * 3 / 2; 144 | } 145 | } 146 | 147 | #endif // ALEPHIUM_BLAKE3_CU 148 | -------------------------------------------------------------------------------- /src/blake3/blake3-common.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ALEPHIUM_BLAKE3_COMMON_H 2 | #define ALEPHIUM_BLAKE3_COMMON_H 3 | 4 | #include "../log.h" 5 | 6 | #define INLINE __forceinline__ 7 | #define TRY(x) \ 8 | { \ 9 | cudaGetLastError(); \ 10 | x; \ 11 | cudaError_t err = cudaGetLastError(); \ 12 | if (err != cudaSuccess) \ 13 | { \ 14 | LOGERR("cudaError %d (%s) calling '%s' (%s line %d)\n", err, cudaGetErrorString(err), #x, __FILE__, __LINE__); \ 15 | exit(1); \ 16 | } \ 17 | } 18 | 19 | #define BLAKE3_KEY_LEN 32 20 | #define BLAKE3_OUT_LEN 32 21 | #define BLAKE3_BLOCK_LEN 64 22 | #define BLAKE3_CHUNK_LEN 1024 23 | #define BLAKE3_BUF_CAP 384 24 | #define BLAKE3_BUF_LEN 326 25 | 26 | #define IV_0 0x6A09E667UL 27 | #define IV_1 0xBB67AE85UL 28 | #define IV_2 0x3C6EF372UL 29 | #define IV_3 0xA54FF53AUL 30 | #define IV_4 0x510E527FUL 31 | #define IV_5 0x9B05688CUL 32 | #define IV_6 0x1F83D9ABUL 33 | #define IV_7 0x5BE0CD19UL 34 | 35 | #define CHUNK_START (1 << 0) 36 | #define CHUNK_END (1 << 1) 37 | #define ROOT (1 << 3) 38 | 39 | 40 | 41 | #endif //ALEPHIUM_BLAKE3_COMMON_H 42 | -------------------------------------------------------------------------------- /src/blake3/inlined-blake.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ALEPHIUM_INLINED_BLAKE_H 2 | #define ALEPHIUM_INLINED_BLAKE_H 3 | 4 | #include "blake3-common.hpp" 5 | 6 | namespace inline_blake{ 7 | 8 | #define Z00 0 9 | #define Z01 1 10 | #define Z02 2 11 | #define Z03 3 12 | #define Z04 4 13 | #define Z05 5 14 | #define Z06 6 15 | #define Z07 7 16 | #define Z08 8 17 | #define Z09 9 18 | #define Z0A A 19 | #define Z0B B 20 | #define Z0C C 21 | #define Z0D D 22 | #define Z0E E 23 | #define Z0F F 24 | #define Z10 2 25 | #define Z11 6 26 | #define Z12 3 27 | #define Z13 A 28 | #define Z14 7 29 | #define Z15 0 30 | #define Z16 4 31 | #define Z17 D 32 | #define Z18 1 33 | #define Z19 B 34 | #define Z1A C 35 | #define Z1B 5 36 | #define Z1C 9 37 | #define Z1D E 38 | #define Z1E F 39 | #define Z1F 8 40 | #define Z20 3 41 | #define Z21 4 42 | #define Z22 A 43 | #define Z23 C 44 | #define Z24 D 45 | #define Z25 2 46 | #define Z26 7 47 | #define Z27 E 48 | #define Z28 6 49 | #define Z29 5 50 | #define Z2A 9 51 | #define Z2B 0 52 | #define Z2C B 53 | #define Z2D F 54 | #define Z2E 8 55 | #define Z2F 1 56 | #define Z30 A 57 | #define Z31 7 58 | #define Z32 C 59 | #define Z33 9 60 | #define Z34 E 61 | #define Z35 3 62 | #define Z36 D 63 | #define Z37 F 64 | #define Z38 4 65 | #define Z39 0 66 | #define Z3A B 67 | #define Z3B 2 68 | #define Z3C 5 69 | #define Z3D 8 70 | #define Z3E 1 71 | #define Z3F 6 72 | #define Z40 C 73 | #define Z41 D 74 | #define Z42 9 75 | #define Z43 B 76 | #define Z44 F 77 | #define Z45 A 78 | #define Z46 E 79 | #define Z47 8 80 | #define Z48 7 81 | #define Z49 2 82 | #define Z4A 5 83 | #define Z4B 3 84 | #define Z4C 0 85 | #define Z4D 1 86 | #define Z4E 6 87 | #define Z4F 4 88 | #define Z50 9 89 | #define Z51 E 90 | #define Z52 B 91 | #define Z53 5 92 | #define Z54 8 93 | #define Z55 C 94 | #define Z56 F 95 | #define Z57 1 96 | #define Z58 D 97 | #define Z59 3 98 | #define Z5A 0 99 | #define Z5B A 100 | #define Z5C 2 101 | #define Z5D 6 102 | #define Z5E 4 103 | #define Z5F 7 104 | #define Z60 B 105 | #define Z61 F 106 | #define Z62 5 107 | #define Z63 0 108 | #define Z64 1 109 | #define Z65 9 110 | #define Z66 8 111 | #define Z67 6 112 | #define Z68 E 113 | #define Z69 A 114 | #define Z6A 2 115 | #define Z6B C 116 | #define Z6C 3 117 | #define Z6D 4 118 | #define Z6E 7 119 | #define Z6F D 120 | 121 | INLINE __device__ uint32_t ROTR32(uint32_t w, uint32_t c) 122 | { 123 | return (w >> c) | (w << (32 - c)); 124 | } 125 | 126 | #define G(a, b, c, d, x, y) \ 127 | if (1) \ 128 | { \ 129 | a = a + b + x; \ 130 | d = ROTR32(d ^ a, 16); \ 131 | c = c + d; \ 132 | b = ROTR32(b ^ c, 12); \ 133 | a = a + b + y; \ 134 | d = ROTR32(d ^ a, 8); \ 135 | c = c + d; \ 136 | b = ROTR32(b ^ c, 7); \ 137 | } \ 138 | else \ 139 | ((void)0) 140 | 141 | #define Mx(r, i) Mx_(Z##r##i) 142 | #define Mx_(n) Mx__(n) 143 | #define Mx__(n) M##n 144 | 145 | #define ROUND(r) \ 146 | if (1) \ 147 | { \ 148 | G(V0, V4, V8, VC, Mx(r, 0), Mx(r, 1)); \ 149 | G(V1, V5, V9, VD, Mx(r, 2), Mx(r, 3)); \ 150 | G(V2, V6, VA, VE, Mx(r, 4), Mx(r, 5)); \ 151 | G(V3, V7, VB, VF, Mx(r, 6), Mx(r, 7)); \ 152 | G(V0, V5, VA, VF, Mx(r, 8), Mx(r, 9)); \ 153 | G(V1, V6, VB, VC, Mx(r, A), Mx(r, B)); \ 154 | G(V2, V7, V8, VD, Mx(r, C), Mx(r, D)); \ 155 | G(V3, V4, V9, VE, Mx(r, E), Mx(r, F)); \ 156 | } \ 157 | else \ 158 | ((void)0) 159 | 160 | #define COMPRESS_PRE \ 161 | if (1) \ 162 | { \ 163 | V0 = H0; \ 164 | V1 = H1; \ 165 | V2 = H2; \ 166 | V3 = H3; \ 167 | V4 = H4; \ 168 | V5 = H5; \ 169 | V6 = H6; \ 170 | V7 = H7; \ 171 | V8 = IV_0; \ 172 | V9 = IV_1; \ 173 | VA = IV_2; \ 174 | VB = IV_3; \ 175 | VC = 0; \ 176 | VD = 0; \ 177 | VE = BLEN; \ 178 | VF = FLAGS; \ 179 | \ 180 | ROUND(0); \ 181 | ROUND(1); \ 182 | ROUND(2); \ 183 | ROUND(3); \ 184 | ROUND(4); \ 185 | ROUND(5); \ 186 | ROUND(6); \ 187 | } \ 188 | else \ 189 | ((void)0) 190 | 191 | #define COMPRESS \ 192 | if (1) \ 193 | { \ 194 | COMPRESS_PRE; \ 195 | H0 = V0 ^ V8; \ 196 | H1 = V1 ^ V9; \ 197 | H2 = V2 ^ VA; \ 198 | H3 = V3 ^ VB; \ 199 | H4 = V4 ^ VC; \ 200 | H5 = V5 ^ VD; \ 201 | H6 = V6 ^ VE; \ 202 | H7 = V7 ^ VF; \ 203 | } \ 204 | else \ 205 | ((void)0) 206 | 207 | #define HASH_BLOCK(r, blen, flags) \ 208 | if (1) \ 209 | { \ 210 | M0 = input[0x##r##0]; \ 211 | M1 = input[0x##r##1]; \ 212 | M2 = input[0x##r##2]; \ 213 | M3 = input[0x##r##3]; \ 214 | M4 = input[0x##r##4]; \ 215 | M5 = input[0x##r##5]; \ 216 | M6 = input[0x##r##6]; \ 217 | M7 = input[0x##r##7]; \ 218 | M8 = input[0x##r##8]; \ 219 | M9 = input[0x##r##9]; \ 220 | MA = input[0x##r##A]; \ 221 | MB = input[0x##r##B]; \ 222 | MC = input[0x##r##C]; \ 223 | MD = input[0x##r##D]; \ 224 | ME = input[0x##r##E]; \ 225 | MF = input[0x##r##F]; \ 226 | BLEN = (blen); \ 227 | FLAGS = (flags); \ 228 | COMPRESS; \ 229 | } \ 230 | else \ 231 | ((void)0) 232 | 233 | typedef struct 234 | { 235 | uint8_t buf[BLAKE3_BUF_CAP]; 236 | 237 | uint8_t hash[32]; // 64 bytes needed as hash will used as block words as well 238 | 239 | uint8_t target[32]; 240 | uint32_t from_group; 241 | uint32_t to_group; 242 | 243 | uint32_t hash_count; 244 | int found_good_hash; 245 | } blake3_hasher; 246 | 247 | #define DOUBLE_HASH \ 248 | if (1) \ 249 | { \ 250 | H1 = IV_1; \ 251 | H0 = IV_0; \ 252 | H2 = IV_2; \ 253 | H3 = IV_3; \ 254 | H4 = IV_4; \ 255 | H5 = IV_5; \ 256 | H6 = IV_6; \ 257 | H7 = IV_7; \ 258 | HASH_BLOCK(0, 64, CHUNK_START); \ 259 | HASH_BLOCK(1, 64, 0); \ 260 | HASH_BLOCK(2, 64, 0); \ 261 | HASH_BLOCK(3, 64, 0); \ 262 | HASH_BLOCK(4, 64, 0); \ 263 | HASH_BLOCK(5, 6, CHUNK_END | ROOT); \ 264 | \ 265 | M0 = H0; \ 266 | M1 = H1; \ 267 | M2 = H2; \ 268 | M3 = H3; \ 269 | M4 = H4; \ 270 | M5 = H5; \ 271 | M6 = H6; \ 272 | M7 = H7; \ 273 | M8 = 0; \ 274 | M9 = 0; \ 275 | MA = 0; \ 276 | MB = 0; \ 277 | MC = 0; \ 278 | MD = 0; \ 279 | ME = 0; \ 280 | MF = 0; \ 281 | H0 = IV_0; \ 282 | H1 = IV_1; \ 283 | H2 = IV_2; \ 284 | H3 = IV_3; \ 285 | H4 = IV_4; \ 286 | H5 = IV_5; \ 287 | H6 = IV_6; \ 288 | H7 = IV_7; \ 289 | BLEN = 32; \ 290 | FLAGS = CHUNK_START | CHUNK_END | ROOT; \ 291 | COMPRESS; \ 292 | } \ 293 | else \ 294 | ((void)0) 295 | 296 | #define UPDATE_NONCE \ 297 | if (1) \ 298 | { \ 299 | if (atomicCAS(&reinterpret_cast(global_hasher)->found_good_hash, 0, 1) == 0) \ 300 | { \ 301 | uint32_t *nonce = (uint32_t *)reinterpret_cast(global_hasher)->buf; \ 302 | nonce[0] = input[0x00]; \ 303 | nonce[1] = input[0x01]; \ 304 | nonce[2] = input[0x02]; \ 305 | nonce[3] = input[0x03]; \ 306 | nonce[4] = input[0x04]; \ 307 | nonce[5] = input[0x05]; \ 308 | uint32_t *output = (uint32_t *)reinterpret_cast(global_hasher)->hash; \ 309 | output[0] = H0; \ 310 | output[1] = H1; \ 311 | output[2] = H2; \ 312 | output[3] = H3; \ 313 | output[4] = H4; \ 314 | output[5] = H5; \ 315 | output[6] = H6; \ 316 | output[7] = H7; \ 317 | } \ 318 | atomicAdd(&reinterpret_cast(global_hasher)->hash_count, hash_count); \ 319 | return; \ 320 | } \ 321 | else \ 322 | ((void)0) 323 | 324 | #define CHECK_INDEX \ 325 | if (1) \ 326 | { \ 327 | uint32_t big_index = (H7 & 0x0F000000) >> 24; \ 328 | if ((big_index / group_nums == from_group) && (big_index % group_nums == to_group)) \ 329 | { \ 330 | UPDATE_NONCE; \ 331 | } \ 332 | else \ 333 | { \ 334 | goto cnt; \ 335 | } \ 336 | } \ 337 | else \ 338 | ((void)0) 339 | 340 | #define MASK0(n) (n & 0x000000FF) 341 | #define MASK1(n) (n & 0x0000FF00) 342 | #define MASK2(n) (n & 0x00FF0000) 343 | #define MASK3(n) (n & 0xFF000000) 344 | #define CHECK_TARGET(m, n) \ 345 | if (1) \ 346 | { \ 347 | m0 = MASK##n(H##m); \ 348 | m1 = MASK##n(target##m); \ 349 | if (m0 > m1) \ 350 | { \ 351 | goto cnt; \ 352 | } \ 353 | else if (m0 < m1) \ 354 | { \ 355 | CHECK_INDEX; \ 356 | } \ 357 | } \ 358 | else \ 359 | ((void)0) 360 | 361 | #define CHECK_POW \ 362 | if (1) \ 363 | { \ 364 | uint32_t m0, m1; \ 365 | CHECK_TARGET(0, 0); \ 366 | CHECK_TARGET(0, 1); \ 367 | CHECK_TARGET(0, 2); \ 368 | CHECK_TARGET(0, 3); \ 369 | CHECK_TARGET(1, 0); \ 370 | CHECK_TARGET(1, 1); \ 371 | CHECK_TARGET(1, 2); \ 372 | CHECK_TARGET(1, 3); \ 373 | CHECK_TARGET(2, 0); \ 374 | CHECK_TARGET(2, 1); \ 375 | CHECK_TARGET(2, 2); \ 376 | CHECK_TARGET(2, 3); \ 377 | } \ 378 | else \ 379 | ((void)0) 380 | 381 | __global__ void blake3_hasher_mine(void *global_hasher) 382 | { 383 | blake3_hasher hasher = *reinterpret_cast(global_hasher); 384 | uint32_t *input = (uint32_t *)hasher.buf; 385 | uint32_t *target = (uint32_t *)hasher.target; 386 | uint32_t target0 = target[0], target1 = target[1], target2 = target[2]; //, target3 = target[3], target4 = target[4], target5 = target[5], target6 = target[6], target7 = target[7]; 387 | uint32_t from_group = hasher.from_group, to_group = hasher.to_group; 388 | uint32_t hash_count = 0; 389 | 390 | uint32_t M0, M1, M2, M3, M4, M5, M6, M7, M8, M9, MA, MB, MC, MD, ME, MF; // message block 391 | uint32_t V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, VA, VB, VC, VD, VE, VF; // internal state 392 | uint32_t H0, H1, H2, H3, H4, H5, H6, H7; // chain value 393 | uint32_t BLEN, FLAGS; // block len, flags 394 | 395 | int stride = blockDim.x * gridDim.x; 396 | int tid = threadIdx.x + blockIdx.x * blockDim.x; 397 | uint32_t *short_nonce = &input[0x00]; 398 | *short_nonce = (*short_nonce) / stride * stride + tid; 399 | 400 | while (hash_count < mining_steps) 401 | { 402 | hash_count += 1; 403 | // printf("count: %u\n", hash_count); 404 | *short_nonce += stride; 405 | DOUBLE_HASH; 406 | CHECK_POW; 407 | cnt:; 408 | } 409 | atomicAdd(&reinterpret_cast(global_hasher)->hash_count, hash_count); 410 | } 411 | 412 | } 413 | 414 | #endif //ALEPHIUM_INLINED_BLAKE_H 415 | -------------------------------------------------------------------------------- /src/blake3/original-blake.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ALEPHIUM_ORIGINAL_BLAKE_H 2 | #define ALEPHIUM_ORIGINAL_BLAKE_H 3 | 4 | #include "blake3-common.hpp" 5 | 6 | namespace ref_blake{ 7 | 8 | #define REF_Z00 0 9 | #define REF_Z01 1 10 | #define REF_Z02 2 11 | #define REF_Z03 3 12 | #define REF_Z04 4 13 | #define REF_Z05 5 14 | #define REF_Z06 6 15 | #define REF_Z07 7 16 | #define REF_Z08 8 17 | #define REF_Z09 9 18 | #define REF_Z0A 10 19 | #define REF_Z0B 11 20 | #define REF_Z0C 12 21 | #define REF_Z0D 13 22 | #define REF_Z0E 14 23 | #define REF_Z0F 15 24 | #define REF_Z10 2 25 | #define REF_Z11 6 26 | #define REF_Z12 3 27 | #define REF_Z13 10 28 | #define REF_Z14 7 29 | #define REF_Z15 0 30 | #define REF_Z16 4 31 | #define REF_Z17 13 32 | #define REF_Z18 1 33 | #define REF_Z19 11 34 | #define REF_Z1A 12 35 | #define REF_Z1B 5 36 | #define REF_Z1C 9 37 | #define REF_Z1D 14 38 | #define REF_Z1E 15 39 | #define REF_Z1F 8 40 | #define REF_Z20 3 41 | #define REF_Z21 4 42 | #define REF_Z22 10 43 | #define REF_Z23 12 44 | #define REF_Z24 13 45 | #define REF_Z25 2 46 | #define REF_Z26 7 47 | #define REF_Z27 14 48 | #define REF_Z28 6 49 | #define REF_Z29 5 50 | #define REF_Z2A 9 51 | #define REF_Z2B 0 52 | #define REF_Z2C 11 53 | #define REF_Z2D 15 54 | #define REF_Z2E 8 55 | #define REF_Z2F 1 56 | #define REF_Z30 10 57 | #define REF_Z31 7 58 | #define REF_Z32 12 59 | #define REF_Z33 9 60 | #define REF_Z34 14 61 | #define REF_Z35 3 62 | #define REF_Z36 13 63 | #define REF_Z37 15 64 | #define REF_Z38 4 65 | #define REF_Z39 0 66 | #define REF_Z3A 11 67 | #define REF_Z3B 2 68 | #define REF_Z3C 5 69 | #define REF_Z3D 8 70 | #define REF_Z3E 1 71 | #define REF_Z3F 6 72 | #define REF_Z40 12 73 | #define REF_Z41 13 74 | #define REF_Z42 9 75 | #define REF_Z43 11 76 | #define REF_Z44 15 77 | #define REF_Z45 10 78 | #define REF_Z46 14 79 | #define REF_Z47 8 80 | #define REF_Z48 7 81 | #define REF_Z49 2 82 | #define REF_Z4A 5 83 | #define REF_Z4B 3 84 | #define REF_Z4C 0 85 | #define REF_Z4D 1 86 | #define REF_Z4E 6 87 | #define REF_Z4F 4 88 | #define REF_Z50 9 89 | #define REF_Z51 14 90 | #define REF_Z52 11 91 | #define REF_Z53 5 92 | #define REF_Z54 8 93 | #define REF_Z55 12 94 | #define REF_Z56 15 95 | #define REF_Z57 1 96 | #define REF_Z58 13 97 | #define REF_Z59 3 98 | #define REF_Z5A 0 99 | #define REF_Z5B 10 100 | #define REF_Z5C 2 101 | #define REF_Z5D 6 102 | #define REF_Z5E 4 103 | #define REF_Z5F 7 104 | #define REF_Z60 11 105 | #define REF_Z61 15 106 | #define REF_Z62 5 107 | #define REF_Z63 0 108 | #define REF_Z64 1 109 | #define REF_Z65 9 110 | #define REF_Z66 8 111 | #define REF_Z67 6 112 | #define REF_Z68 14 113 | #define REF_Z69 10 114 | #define REF_Z6A 2 115 | #define REF_Z6B 12 116 | #define REF_Z6C 3 117 | #define REF_Z6D 4 118 | #define REF_Z6E 7 119 | #define REF_Z6F 13 120 | 121 | INLINE __device__ void cv_state_init(uint32_t *cv) 122 | { 123 | cv[0] = IV_0; 124 | cv[1] = IV_1; 125 | cv[2] = IV_2; 126 | cv[3] = IV_3; 127 | cv[4] = IV_4; 128 | cv[5] = IV_5; 129 | cv[6] = IV_6; 130 | cv[7] = IV_7; 131 | } 132 | 133 | INLINE __device__ void blake3_compress_in_place(uint32_t cv[8], 134 | const uint8_t block[BLAKE3_BLOCK_LEN], 135 | uint8_t block_len, 136 | uint8_t flags); 137 | 138 | INLINE __device__ void chunk_state_update(uint32_t cv[8], uint8_t *input, size_t initial_len) 139 | { 140 | ssize_t input_len = initial_len; 141 | assert(input_len > 0 && input_len <= BLAKE3_CHUNK_LEN); 142 | 143 | while (input_len > 0) 144 | { 145 | ssize_t take = input_len >= BLAKE3_BLOCK_LEN ? BLAKE3_BLOCK_LEN : input_len; 146 | 147 | uint8_t maybe_start_flag = input_len == initial_len ? CHUNK_START : 0; 148 | input_len -= take; 149 | uint8_t maybe_end_flag = 0; 150 | if (input_len == 0) 151 | { 152 | maybe_end_flag = CHUNK_END | ROOT; 153 | memset(input + take, 0, BLAKE3_BLOCK_LEN - take); 154 | } 155 | 156 | blake3_compress_in_place(cv, input, take, maybe_start_flag | maybe_end_flag); 157 | input += take; 158 | } 159 | } 160 | 161 | INLINE __device__ uint32_t rotr32(uint32_t w, uint32_t c) 162 | { 163 | return (w >> c) | (w << (32 - c)); 164 | } 165 | 166 | #define REF_G(a, b, c, d, x, y) \ 167 | do \ 168 | { \ 169 | state[a] = state[a] + state[b] + x; \ 170 | state[d] = rotr32(state[d] ^ state[a], 16); \ 171 | state[c] = state[c] + state[d]; \ 172 | state[b] = rotr32(state[b] ^ state[c], 12); \ 173 | state[a] = state[a] + state[b] + y; \ 174 | state[d] = rotr32(state[d] ^ state[a], 8); \ 175 | state[c] = state[c] + state[d]; \ 176 | state[b] = rotr32(state[b] ^ state[c], 7); \ 177 | } while (0) 178 | 179 | #define REF_Mx(r, i) (block_words[REF_Z##r##i]) 180 | 181 | #define ROUND_S(r) \ 182 | do \ 183 | { \ 184 | REF_G(0x0, 0x4, 0x8, 0xC, REF_Mx(r, 0), REF_Mx(r, 1)); \ 185 | REF_G(0x1, 0x5, 0x9, 0xD, REF_Mx(r, 2), REF_Mx(r, 3)); \ 186 | REF_G(0x2, 0x6, 0xA, 0xE, REF_Mx(r, 4), REF_Mx(r, 5)); \ 187 | REF_G(0x3, 0x7, 0xB, 0xF, REF_Mx(r, 6), REF_Mx(r, 7)); \ 188 | REF_G(0x0, 0x5, 0xA, 0xF, REF_Mx(r, 8), REF_Mx(r, 9)); \ 189 | REF_G(0x1, 0x6, 0xB, 0xC, REF_Mx(r, A), REF_Mx(r, B)); \ 190 | REF_G(0x2, 0x7, 0x8, 0xD, REF_Mx(r, C), REF_Mx(r, D)); \ 191 | REF_G(0x3, 0x4, 0x9, 0xE, REF_Mx(r, E), REF_Mx(r, F)); \ 192 | } while (0) 193 | 194 | INLINE __device__ void compress_pre(uint32_t state[16], const uint32_t cv[8], 195 | const uint8_t block[BLAKE3_BLOCK_LEN], 196 | uint8_t block_len, uint8_t flags) 197 | { 198 | uint32_t *block_words = (uint32_t *)block; 199 | 200 | state[0] = cv[0]; 201 | state[1] = cv[1]; 202 | state[2] = cv[2]; 203 | state[3] = cv[3]; 204 | state[4] = cv[4]; 205 | state[5] = cv[5]; 206 | state[6] = cv[6]; 207 | state[7] = cv[7]; 208 | state[8] = IV_0; 209 | state[9] = IV_1; 210 | state[10] = IV_2; 211 | state[11] = IV_3; 212 | state[12] = 0; 213 | state[13] = 0; 214 | state[14] = (uint32_t)block_len; 215 | state[15] = (uint32_t)flags; 216 | 217 | ROUND_S(0); 218 | ROUND_S(1); 219 | ROUND_S(2); 220 | ROUND_S(3); 221 | ROUND_S(4); 222 | ROUND_S(5); 223 | ROUND_S(6); 224 | } 225 | 226 | INLINE __device__ void blake3_compress_in_place(uint32_t cv[8], 227 | const uint8_t block[BLAKE3_BLOCK_LEN], 228 | uint8_t block_len, 229 | uint8_t flags) 230 | { 231 | uint32_t state[16]; 232 | compress_pre(state, cv, block, block_len, flags); 233 | cv[0] = state[0] ^ state[8]; 234 | cv[1] = state[1] ^ state[9]; 235 | cv[2] = state[2] ^ state[10]; 236 | cv[3] = state[3] ^ state[11]; 237 | cv[4] = state[4] ^ state[12]; 238 | cv[5] = state[5] ^ state[13]; 239 | cv[6] = state[6] ^ state[14]; 240 | cv[7] = state[7] ^ state[15]; 241 | 242 | // printf("== final state: "); 243 | // for (int i = 0; i < 16; i++) { 244 | // printf("%d, ", state[i]); 245 | // } 246 | // printf("\n"); 247 | // printf("== final cv: "); 248 | // for (int i = 0; i < 16; i++) { 249 | // printf("%d, ", cv[i]); 250 | // } 251 | // printf("\n\n"); 252 | } 253 | 254 | typedef struct 255 | { 256 | uint8_t buf[BLAKE3_BUF_CAP]; 257 | 258 | uint32_t cv[8]; 259 | 260 | uint8_t hash[64]; // 64 bytes needed as hash will used as block words as well 261 | 262 | uint8_t target[32]; 263 | uint32_t from_group; 264 | uint32_t to_group; 265 | 266 | uint32_t hash_count; 267 | int found_good_hash; 268 | } blake3_hasher; 269 | 270 | INLINE __device__ void blake3_hasher_hash(const blake3_hasher *self, uint8_t *input, size_t input_len, uint8_t *out) 271 | { 272 | cv_state_init((uint32_t *)self->cv); 273 | chunk_state_update((uint32_t *)&self->cv, input, input_len); 274 | #pragma unroll 275 | for (int i = 0; i < 8; i ++) { 276 | ((uint32_t *) out)[i] = self->cv[i]; 277 | } 278 | } 279 | 280 | INLINE __device__ void blake3_hasher_double_hash(blake3_hasher *hasher) 281 | { 282 | blake3_hasher_hash(hasher, hasher->buf, BLAKE3_BUF_LEN, hasher->hash); 283 | blake3_hasher_hash(hasher, hasher->hash, BLAKE3_OUT_LEN, hasher->hash); 284 | } 285 | 286 | INLINE __device__ bool check_target(uint8_t *hash, uint8_t *target) 287 | { 288 | #pragma unroll 289 | for (ssize_t i = 0; i < 32; i++) 290 | { 291 | if (hash[i] > target[i]) 292 | { 293 | return false; 294 | } 295 | else if (hash[i] < target[i]) 296 | { 297 | return true; 298 | } 299 | } 300 | return true; 301 | } 302 | 303 | INLINE __device__ bool check_index(uint8_t *hash, uint32_t from_group, uint32_t to_group) 304 | { 305 | uint8_t big_index = hash[31] % chain_nums; 306 | return (big_index / group_nums == from_group) && (big_index % group_nums == to_group); 307 | } 308 | 309 | INLINE __device__ bool check_hash(uint8_t *hash, uint8_t *target, uint32_t from_group, uint32_t to_group) 310 | { 311 | return check_target(hash, target) && check_index(hash, from_group, to_group); 312 | } 313 | 314 | INLINE __device__ void update_nonce(blake3_hasher *hasher, uint64_t delta) 315 | { 316 | uint64_t *short_nonce = (uint64_t *)hasher->buf; 317 | *short_nonce += delta; 318 | } 319 | 320 | INLINE __device__ void copy_good_nonce(blake3_hasher *thread_hasher, blake3_hasher *global_hasher) 321 | { 322 | #pragma unroll 323 | for (int i = 0; i < 24; i++) 324 | { 325 | global_hasher->buf[i] = thread_hasher->buf[i]; 326 | } 327 | #pragma unroll 328 | for (int i = 0; i < 32; i++) 329 | { 330 | global_hasher->hash[i] = thread_hasher->hash[i]; 331 | } 332 | } 333 | 334 | __global__ void blake3_hasher_mine(void *global_hasher) 335 | { 336 | blake3_hasher local_hasher = *reinterpret_cast(global_hasher); 337 | blake3_hasher *hasher = &local_hasher; 338 | 339 | hasher->hash_count = 0; 340 | 341 | int stride = blockDim.x * gridDim.x; 342 | int tid = threadIdx.x + blockIdx.x * blockDim.x; 343 | uint64_t *short_nonce = (uint64_t *)hasher->buf; 344 | *short_nonce = (*short_nonce) / stride * stride + tid; 345 | 346 | while (hasher->hash_count < mining_steps) 347 | { 348 | hasher->hash_count += 1; 349 | 350 | *short_nonce += stride; 351 | blake3_hasher_double_hash(hasher); 352 | 353 | if (check_hash(hasher->hash, hasher->target, hasher->from_group, hasher->to_group)) 354 | { 355 | if (atomicCAS(&reinterpret_cast(global_hasher)->found_good_hash, 0, 1) == 0) 356 | { 357 | copy_good_nonce(hasher, reinterpret_cast(global_hasher)); 358 | } 359 | atomicAdd(&reinterpret_cast(global_hasher)->hash_count, hasher->hash_count); 360 | return; 361 | } 362 | } 363 | atomicAdd(&reinterpret_cast(global_hasher)->hash_count, hasher->hash_count); 364 | } 365 | } 366 | 367 | #endif //ALEPHIUM_ORIGINAL_BLAKE_H 368 | -------------------------------------------------------------------------------- /src/constants.h: -------------------------------------------------------------------------------- 1 | #ifndef ALEPHIUM_CONSTANTS_H 2 | #define ALEPHIUM_CONSTANTS_H 3 | 4 | #define group_nums 4 5 | #define chain_nums 16 6 | #define max_gpu_num 1024 7 | #define parallel_mining_works_per_gpu 4 8 | #define max_worker_num (max_gpu_num * parallel_mining_works_per_gpu) 9 | #define mining_steps 5000 10 | #define mining_protocol_version 1 11 | 12 | #endif // ALEPHIUM_CONSTANTS_H 13 | -------------------------------------------------------------------------------- /src/getopt.h: -------------------------------------------------------------------------------- 1 | #ifndef GETOPT_H 2 | #define GETOPT_H 3 | 4 | /* 5 | * Copyright (c) 1987, 1993, 1994 6 | * The Regents of the University of California. All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. All advertising materials mentioning features or use of this software 17 | * must display the following acknowledgement: 18 | * This product includes software developed by the University of 19 | * California, Berkeley and its contributors. 20 | * 4. Neither the name of the University nor the names of its contributors 21 | * may be used to endorse or promote products derived from this software 22 | * without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 | * SUCH DAMAGE. 35 | */ 36 | 37 | #include 38 | #include 39 | 40 | int opterr = 1, /* if error message should be printed */ 41 | optind = 1, /* index into parent argv vector */ 42 | optopt, /* character checked for validity */ 43 | optreset; /* reset getopt */ 44 | char *optarg; /* argument associated with option */ 45 | 46 | #define BADCH (int)'?' 47 | #define BADARG (int)':' 48 | #define EMSG "" 49 | 50 | /* 51 | * getopt -- 52 | * Parse argc/argv argument vector. 53 | */ 54 | int getopt(int nargc, char * const nargv[], const char *ostr) 55 | { 56 | static char *place = (char *)EMSG; /* option letter processing */ 57 | const char *oli; /* option letter list index */ 58 | 59 | if (optreset || !*place) { /* update scanning pointer */ 60 | optreset = 0; 61 | if (optind >= nargc || *(place = nargv[optind]) != '-') { 62 | place = (char *)EMSG; 63 | return (-1); 64 | } 65 | if (place[1] && *++place == '-') { /* found "--" */ 66 | ++optind; 67 | place = (char *)EMSG; 68 | return (-1); 69 | } 70 | } /* option letter okay? */ 71 | if ((optopt = (int)*place++) == (int)':' || 72 | !(oli = strchr(ostr, optopt))) { 73 | /* 74 | * if the user didn't specify '-' as an option, 75 | * assume it means -1. 76 | */ 77 | if (optopt == (int)'-') 78 | return (-1); 79 | if (!*place) 80 | ++optind; 81 | if (opterr && *ostr != ':') 82 | (void)printf("illegal option -- %c\n", optopt); 83 | return (BADCH); 84 | } 85 | if (*++oli != ':') { /* don't need argument */ 86 | optarg = NULL; 87 | if (!*place) 88 | ++optind; 89 | } 90 | else { /* need an argument */ 91 | if (*place) /* no white space */ 92 | optarg = place; 93 | else if (nargc <= ++optind) { /* no arg */ 94 | place = (char *)EMSG; 95 | if (*ostr == ':') 96 | return (BADARG); 97 | if (opterr) 98 | (void)printf("option requires an argument -- %c\n", optopt); 99 | return (BADCH); 100 | } 101 | else /* white space */ 102 | optarg = nargv[optind]; 103 | place = (char *)EMSG; 104 | ++optind; 105 | } 106 | return (optopt); /* dump back option letter */ 107 | } 108 | 109 | #endif -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | #ifndef ALEPHIUM_LOG_H 2 | #define ALEPHIUM_LOG_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef _WIN32 8 | #define localtime_r(a, b) localtime_s(b, a) 9 | #endif 10 | 11 | #define LOG_TIMESTAMP(stream) \ 12 | { \ 13 | time_t now = time(NULL); \ 14 | struct tm time; \ 15 | localtime_r(&now, &time); \ 16 | char str[24]; \ 17 | strftime(str, 24, "%Y-%m-%d %H:%M:%S", &time); \ 18 | fprintf(stream, "%s | ", str); \ 19 | } 20 | 21 | #define LOG_WITH_TS(stream, format, ...) \ 22 | { \ 23 | LOG_TIMESTAMP(stream); \ 24 | fprintf(stream, format, ##__VA_ARGS__); \ 25 | } 26 | 27 | #define LOG_WITHOUT_TS(format, ...) fprintf(stdout, format, ##__VA_ARGS__); 28 | 29 | #define LOG(format, ...) LOG_WITH_TS(stdout, format, ##__VA_ARGS__); 30 | 31 | #define LOGERR(format, ...) LOG_WITH_TS(stderr, format, ##__VA_ARGS__); 32 | 33 | #endif // ALEPHIUM_LOG_H 34 | -------------------------------------------------------------------------------- /src/main.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "constants.h" 11 | #include "uv.h" 12 | #include "messages.h" 13 | #include "blake3.cu" 14 | #include "pow.h" 15 | #include "worker.h" 16 | #include "template.h" 17 | #include "mining.h" 18 | #include "getopt.h" 19 | #include "log.h" 20 | 21 | std::atomic found_solutions{0}; 22 | 23 | typedef std::chrono::high_resolution_clock Time; 24 | typedef std::chrono::duration duration_t; 25 | typedef std::chrono::time_point time_point_t; 26 | 27 | uv_loop_t *loop; 28 | uv_stream_t *tcp; 29 | 30 | time_point_t start_time = Time::now(); 31 | 32 | std::atomic gpu_count; 33 | std::atomic worker_count; 34 | std::atomic total_mining_count; 35 | std::atomic device_mining_count[max_gpu_num]; 36 | bool use_device[max_gpu_num]; 37 | 38 | int port = 10973; 39 | char broker_ip[16]; 40 | uv_timer_t reconnect_timer; 41 | uv_tcp_t *uv_socket; 42 | uv_connect_t *uv_connect; 43 | 44 | void setup_gpu_worker_count(int _gpu_count, int _worker_count) 45 | { 46 | gpu_count.store(_gpu_count); 47 | worker_count.store(_worker_count); 48 | } 49 | 50 | void on_write_end(uv_write_t *req, int status) 51 | { 52 | if (status < 0) 53 | { 54 | LOGERR("error on_write_end %d\n", status); 55 | } 56 | free(req); 57 | } 58 | 59 | std::mutex write_mutex; 60 | uint8_t write_buffer[4096 * 1024]; 61 | void submit_new_block(mining_worker_t *worker) 62 | { 63 | expire_template_for_new_block(load_worker__template(worker)); 64 | 65 | const std::lock_guard lock(write_mutex); 66 | 67 | ssize_t buf_size = write_new_block(worker, write_buffer); 68 | uv_buf_t buf = uv_buf_init((char *)write_buffer, buf_size); 69 | print_hex("new solution", (uint8_t *) hasher_buf(worker, true), 32); 70 | 71 | uv_write_t *write_req = (uv_write_t *)malloc(sizeof(uv_write_t)); 72 | uint32_t buf_count = 1; 73 | 74 | uv_write(write_req, tcp, &buf, buf_count, on_write_end); 75 | found_solutions.fetch_add(1, std::memory_order_relaxed); 76 | } 77 | 78 | void mine_with_timer(uv_timer_t *timer); 79 | 80 | void mine(mining_worker_t *worker) 81 | { 82 | time_point_t start = Time::now(); 83 | 84 | int32_t to_mine_index = next_chain_to_mine(); 85 | if (to_mine_index == -1) 86 | { 87 | LOG("waiting for new tasks\n"); 88 | worker->timer.data = worker; 89 | uv_timer_start(&worker->timer, mine_with_timer, 500, 0); 90 | } else { 91 | mining_counts[to_mine_index].fetch_add(mining_steps); 92 | setup_template(worker, load_template(to_mine_index)); 93 | 94 | start_worker_mining(worker); 95 | 96 | duration_t elapsed = Time::now() - start; 97 | // LOG("=== mining time: %fs\n", elapsed.count()); 98 | } 99 | } 100 | 101 | void mine_with_req(uv_work_t *req) 102 | { 103 | mining_worker_t *worker = load_req_worker(req); 104 | mine(worker); 105 | } 106 | 107 | void mine_with_async(uv_async_t *handle) 108 | { 109 | mining_worker_t *worker = (mining_worker_t *)handle->data; 110 | mine(worker); 111 | } 112 | 113 | void mine_with_timer(uv_timer_t *timer) 114 | { 115 | mining_worker_t *worker = (mining_worker_t *)timer->data; 116 | mine(worker); 117 | } 118 | 119 | void after_mine(uv_work_t *req, int status) 120 | { 121 | return; 122 | } 123 | 124 | void worker_stream_callback(cudaStream_t stream, cudaError_t status, void *data) 125 | { 126 | mining_worker_t *worker = (mining_worker_t *)data; 127 | if (hasher_found_good_hash(worker, true)) 128 | { 129 | store_worker_found_good_hash(worker, true); 130 | submit_new_block(worker); 131 | } 132 | 133 | mining_template_t *template_ptr = load_worker__template(worker); 134 | job_t *job = template_ptr->job; 135 | uint32_t chain_index = job->from_group * group_nums + job->to_group; 136 | mining_counts[chain_index].fetch_sub(mining_steps); 137 | mining_counts[chain_index].fetch_add(hasher_hash_count(worker, true)); 138 | total_mining_count.fetch_add(hasher_hash_count(worker, true)); 139 | device_mining_count[worker->device_id].fetch_add(hasher_hash_count(worker, true)); 140 | free_template(template_ptr); 141 | worker->async.data = worker; 142 | uv_async_send(&(worker->async)); 143 | } 144 | 145 | void start_mining() 146 | { 147 | assert(mining_templates_initialized == true); 148 | 149 | start_time = Time::now(); 150 | 151 | for (uint32_t i = 0; i < worker_count.load(); i++) 152 | { 153 | if (use_device[mining_workers[i].device_id]) 154 | { 155 | uv_queue_work(loop, &req[i], mine_with_req, after_mine); 156 | } 157 | } 158 | } 159 | 160 | void start_mining_if_needed() 161 | { 162 | if (!mining_templates_initialized) 163 | { 164 | bool all_initialized = true; 165 | for (int i = 0; i < chain_nums; i++) 166 | { 167 | if (load_template(i) == NULL) 168 | { 169 | all_initialized = false; 170 | break; 171 | } 172 | } 173 | if (all_initialized) 174 | { 175 | mining_templates_initialized = true; 176 | start_mining(); 177 | } 178 | } 179 | } 180 | 181 | void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) 182 | { 183 | buf->base = (char *)malloc(suggested_size); 184 | buf->len = suggested_size; 185 | } 186 | 187 | void log_hashrate(uv_timer_t *timer) 188 | { 189 | time_point_t current_time = Time::now(); 190 | if (current_time > start_time) 191 | { 192 | duration_t eplased = current_time - start_time; 193 | LOG("hashrate: %.0f MH/s ", total_mining_count.load() / eplased.count() / 1000000); 194 | for (int i = 0; i < gpu_count; i++) 195 | { 196 | LOG_WITHOUT_TS("gpu%d: %.0f MH/s ", i, device_mining_count[i].load() / eplased.count() / 1000000); 197 | } 198 | LOG_WITHOUT_TS("solutions: %u\n", found_solutions.load(std::memory_order_relaxed)); 199 | } 200 | } 201 | 202 | uint8_t read_buf[2048 * 1024 * chain_nums]; 203 | blob_t read_blob = {read_buf, 0}; 204 | server_message_t *decode_buf(const uv_buf_t *buf, ssize_t nread) 205 | { 206 | if (read_blob.len == 0) 207 | { 208 | read_blob.blob = (uint8_t *)buf->base; 209 | read_blob.len = nread; 210 | server_message_t *message = decode_server_message(&read_blob); 211 | if (message) 212 | { 213 | // some bytes left 214 | if (read_blob.len > 0) 215 | { 216 | memcpy(read_buf, read_blob.blob, read_blob.len); 217 | read_blob.blob = read_buf; 218 | } 219 | return message; 220 | } 221 | else 222 | { // no bytes consumed 223 | memcpy(read_buf, buf->base, nread); 224 | read_blob.blob = read_buf; 225 | read_blob.len = nread; 226 | return NULL; 227 | } 228 | } 229 | else 230 | { 231 | assert(read_blob.blob == read_buf); 232 | memcpy(read_buf + read_blob.len, buf->base, nread); 233 | read_blob.len += nread; 234 | return decode_server_message(&read_blob); 235 | } 236 | } 237 | 238 | void connect_to_broker(); 239 | 240 | void try_to_reconnect(uv_timer_t *timer){ 241 | read_blob.len = 0; 242 | free(uv_socket); 243 | free(uv_connect); 244 | connect_to_broker(); 245 | uv_timer_stop(timer); 246 | } 247 | 248 | void on_read(uv_stream_t *server, ssize_t nread, const uv_buf_t *buf) 249 | { 250 | if (nread < 0) 251 | { 252 | LOGERR("error on_read %ld: might be that the full node is not synced, or miner wallets are not setup, try to reconnect\n", nread); 253 | uv_timer_start(&reconnect_timer, try_to_reconnect, 5000, 0); 254 | return; 255 | } 256 | 257 | if (nread == 0) 258 | { 259 | return; 260 | } 261 | 262 | server_message_t *message = decode_buf(buf, nread); 263 | if (message) 264 | { 265 | switch (message->kind) 266 | { 267 | case JOBS: 268 | for (int i = 0; i < message->jobs->len; i++) 269 | { 270 | update_templates(message->jobs->jobs[i]); 271 | } 272 | start_mining_if_needed(); 273 | break; 274 | 275 | case SUBMIT_RESULT: 276 | char *block_hash_hex = bytes_to_hex(message->submit_result->block_hash, 32); 277 | LOG( 278 | "submitted: %d -> %d, %s: %d \n", 279 | message->submit_result->from_group, 280 | message->submit_result->to_group, 281 | block_hash_hex, 282 | message->submit_result->status 283 | ); 284 | free(block_hash_hex); 285 | break; 286 | } 287 | free_server_message_except_jobs(message); 288 | } 289 | 290 | free(buf->base); 291 | // uv_close((uv_handle_t *) server, free_close_cb); 292 | } 293 | 294 | void on_connect(uv_connect_t *req, int status) 295 | { 296 | if (status < 0) 297 | { 298 | LOGERR("connection error %d: might be that the full node is not reachable, try to reconnect\n", status); 299 | uv_timer_start(&reconnect_timer, try_to_reconnect, 5000, 0); 300 | return; 301 | } 302 | LOG("the server is connected %d %p\n", status, req); 303 | 304 | tcp = req->handle; 305 | uv_read_start(req->handle, alloc_buffer, on_read); 306 | } 307 | 308 | void connect_to_broker(){ 309 | uv_socket = (uv_tcp_t *)malloc(sizeof(uv_tcp_t)); 310 | uv_tcp_init(loop, uv_socket); 311 | uv_tcp_nodelay(uv_socket, 1); 312 | uv_connect = (uv_connect_t *)malloc(sizeof(uv_connect_t)); 313 | struct sockaddr_in dest; 314 | uv_ip4_addr(broker_ip, port, &dest); 315 | uv_tcp_connect(uv_connect, uv_socket, (const struct sockaddr *)&dest, on_connect); 316 | } 317 | 318 | bool is_valid_ip_address(char *ip_address) 319 | { 320 | struct sockaddr_in sa; 321 | int result = inet_pton(AF_INET, ip_address, &(sa.sin_addr)); 322 | return result != 0; 323 | } 324 | 325 | int hostname_to_ip(char *ip_address, char *hostname) 326 | { 327 | struct addrinfo hints, *servinfo; 328 | memset(&hints, 0, sizeof hints); 329 | hints.ai_family = AF_INET; 330 | hints.ai_socktype = SOCK_STREAM; 331 | 332 | int res = getaddrinfo(hostname, NULL, &hints, &servinfo); 333 | if (res != 0) 334 | { 335 | LOGERR("getaddrinfo: %s\n", gai_strerror(res)); 336 | return 1; 337 | } 338 | 339 | struct sockaddr_in *h = (struct sockaddr_in *)servinfo->ai_addr; 340 | strcpy(ip_address, inet_ntoa(h->sin_addr)); 341 | 342 | freeaddrinfo(servinfo); 343 | return 0; 344 | } 345 | #ifndef MINER_VERSION 346 | #define MINER_VERSION "unknown" 347 | #endif 348 | 349 | int main(int argc, char **argv) 350 | { 351 | setbuf(stdout, NULL); 352 | 353 | #ifdef _WIN32 354 | WSADATA wsa; 355 | // current winsocket version is 2.2 356 | int rc = WSAStartup(MAKEWORD(2, 2), &wsa); 357 | if (rc != 0) 358 | { 359 | LOGERR("Initialize winsock failed: %d\n", rc); 360 | exit(1); 361 | } 362 | #endif 363 | 364 | LOG("Running gpu-miner version : %s\n", MINER_VERSION); 365 | 366 | int gpu_count = 0; 367 | cudaGetDeviceCount(&gpu_count); 368 | LOG("GPU count: %d\n", gpu_count); 369 | for (int i = 0; i < gpu_count; i++) 370 | { 371 | cudaDeviceProp prop; 372 | cudaGetDeviceProperties(&prop, i); 373 | LOG("GPU #%d - %s has #%d cores\n", i, prop.name, get_device_cores(i)); 374 | use_device[i] = true; 375 | } 376 | 377 | strcpy(broker_ip, "127.0.0.1"); 378 | 379 | int command; 380 | while ((command = getopt(argc, argv, "p:g:a:")) != -1) 381 | { 382 | switch (command) 383 | { 384 | case 'p': 385 | port = atoi(optarg); 386 | break; 387 | case 'a': 388 | if (is_valid_ip_address(optarg)) 389 | { 390 | strcpy(broker_ip, optarg); 391 | } 392 | else 393 | { 394 | hostname_to_ip(broker_ip, optarg); 395 | } 396 | break; 397 | 398 | case 'g': 399 | for (int i = 0; i < gpu_count; i++) 400 | { 401 | use_device[i] = false; 402 | } 403 | optind--; 404 | for (; optind < argc && *argv[optind] != '-'; optind++) 405 | { 406 | int device = atoi(argv[optind]); 407 | if (device < 0 || device >= gpu_count) { 408 | LOGERR("Invalid gpu index %d\n", device); 409 | exit(1); 410 | } 411 | use_device[device] = true; 412 | } 413 | break; 414 | default: 415 | LOGERR("Invalid command %c\n", command); 416 | exit(1); 417 | } 418 | } 419 | LOG("will connect to broker @%s:%d\n", broker_ip, port); 420 | 421 | #ifdef __linux__ 422 | signal(SIGPIPE, SIG_IGN); 423 | #endif 424 | 425 | mining_workers_init(gpu_count); 426 | setup_gpu_worker_count(gpu_count, gpu_count * parallel_mining_works_per_gpu); 427 | 428 | loop = uv_default_loop(); 429 | uv_timer_init(loop, &reconnect_timer); 430 | connect_to_broker(); 431 | 432 | for (int i = 0; i < worker_count; i++) 433 | { 434 | uv_async_init(loop, &(mining_workers[i].async), mine_with_async); 435 | uv_timer_init(loop, &(mining_workers[i].timer)); 436 | } 437 | 438 | uv_timer_t log_timer; 439 | uv_timer_init(loop, &log_timer); 440 | uv_timer_start(&log_timer, log_hashrate, 5000, 20000); 441 | 442 | uv_run(loop, UV_RUN_DEFAULT); 443 | 444 | return (0); 445 | } 446 | -------------------------------------------------------------------------------- /src/messages.h: -------------------------------------------------------------------------------- 1 | #ifndef ALEPHIUM_MESSAGE_H 2 | #define ALEPHIUM_MESSAGE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "log.h" 12 | #include "constants.h" 13 | 14 | typedef struct blob_t { 15 | uint8_t *blob; 16 | ssize_t len; 17 | } blob_t; 18 | 19 | void free_blob(blob_t *blob) 20 | { 21 | free(blob->blob); 22 | } 23 | 24 | char *bytes_to_hex(uint8_t *bytes, ssize_t len) 25 | { 26 | ssize_t hex_len = 2 * len + 1; 27 | char *hex_string = (char *)malloc(hex_len); 28 | memset(hex_string, 0, hex_len); 29 | 30 | uint8_t *byte_cursor = bytes; 31 | char *hex_cursor = hex_string; 32 | ssize_t count = 0; 33 | while (count < len) { 34 | sprintf(hex_cursor, "%02x", *byte_cursor); 35 | byte_cursor++; 36 | count++; 37 | hex_cursor += 2; 38 | } 39 | 40 | return hex_string; 41 | } 42 | 43 | void print_hex(const char* prefix, uint8_t *data, ssize_t nread) 44 | { 45 | char *hex_string = bytes_to_hex(data, nread); 46 | LOG("%s: %s\n", prefix, hex_string); 47 | free(hex_string); 48 | } 49 | 50 | char hex_to_byte(char hex) 51 | { 52 | if (hex >= '0' && hex <= '9') { 53 | return hex - '0'; 54 | } else if (hex >= 'a' && hex <= 'f') { 55 | return hex - 'a' + 10; 56 | } else { 57 | exit(1); 58 | } 59 | } 60 | 61 | void hex_to_bytes(const char *hex_data, blob_t *buf) 62 | { 63 | size_t hex_len = strlen(hex_data); 64 | assert(hex_len % 2 == 0); 65 | 66 | buf->len = hex_len / 2; 67 | buf->blob = (uint8_t *)malloc(buf->len); 68 | memset(buf->blob, 0, buf->len); 69 | 70 | for (size_t pos = 0; pos < hex_len; pos += 2) { 71 | char left = hex_to_byte(hex_data[pos]); 72 | char right = hex_to_byte(hex_data[pos + 1]); 73 | buf->blob[pos / 2] = (left << 4) + right; 74 | } 75 | } 76 | 77 | typedef struct job_t { 78 | int from_group; 79 | int to_group; 80 | blob_t header_blob; 81 | blob_t txs_blob; 82 | blob_t target; 83 | int height; 84 | } job_t; 85 | 86 | void free_job(job_t *job) { 87 | free_blob(&job->header_blob); 88 | free_blob(&job->txs_blob); 89 | free_blob(&job->target); 90 | free(job); 91 | } 92 | 93 | typedef struct jobs_t { 94 | job_t **jobs; 95 | size_t len; 96 | } jobs_t; 97 | 98 | void free_jobs(jobs_t *jobs) 99 | { 100 | for (size_t i = 0; i < jobs->len; i++) { 101 | free_job(jobs->jobs[i]); 102 | } 103 | free(jobs->jobs); 104 | } 105 | 106 | typedef struct submit_result_t { 107 | int from_group; 108 | int to_group; 109 | uint8_t block_hash[32]; 110 | bool status; 111 | } submit_result_t; 112 | 113 | typedef enum server_message_kind { 114 | JOBS, 115 | SUBMIT_RESULT, 116 | } server_message_kind; 117 | 118 | typedef struct server_message_t { 119 | server_message_kind kind; 120 | union { 121 | jobs_t *jobs; 122 | submit_result_t *submit_result; 123 | }; 124 | } server_message_t; 125 | 126 | void free_server_message_except_jobs(server_message_t *message) 127 | { 128 | switch (message->kind) 129 | { 130 | case JOBS: 131 | free(message->jobs->jobs); 132 | free(message->jobs); 133 | break; 134 | 135 | case SUBMIT_RESULT: 136 | free(message->submit_result); 137 | break; 138 | } 139 | 140 | free(message); 141 | } 142 | 143 | void write_size(uint8_t **bytes, ssize_t size) 144 | { 145 | (*bytes)[0] = (size >> 24) & 0xFF; 146 | (*bytes)[1] = (size >> 16) & 0xFF; 147 | (*bytes)[2] = (size >> 8) & 0xFF; 148 | (*bytes)[3] = size & 0xFF; 149 | *bytes = *bytes + 4; 150 | return; 151 | } 152 | 153 | ssize_t decode_size(uint8_t *bytes) 154 | { 155 | return bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3]; 156 | } 157 | 158 | ssize_t extract_size(uint8_t **bytes) 159 | { 160 | ssize_t size = decode_size(*bytes); 161 | *bytes = *bytes + 4; 162 | return size; 163 | } 164 | 165 | void write_byte(uint8_t **bytes, uint8_t byte) 166 | { 167 | (*bytes)[0] = byte; 168 | *bytes = *bytes + 1; 169 | } 170 | 171 | uint8_t extract_byte(uint8_t **bytes) 172 | { 173 | uint8_t byte = **bytes; 174 | *bytes = *bytes + 1; 175 | return byte; 176 | } 177 | 178 | bool extract_bool(uint8_t **bytes) 179 | { 180 | uint8_t byte = extract_byte(bytes); 181 | switch (byte) 182 | { 183 | case 0: 184 | return false; 185 | case 1: 186 | return true; 187 | default: 188 | LOGERR("Invaid bool value\n"); 189 | exit(1); 190 | } 191 | } 192 | 193 | void write_bytes(uint8_t **bytes, uint8_t *data, ssize_t len) 194 | { 195 | memcpy(*bytes, data, len); 196 | *bytes = *bytes + len; 197 | } 198 | 199 | void write_blob(uint8_t **bytes, blob_t *blob) 200 | { 201 | write_bytes(bytes, blob->blob, blob->len); 202 | } 203 | 204 | void extract_blob(uint8_t **bytes, blob_t *blob) 205 | { 206 | ssize_t size = extract_size(bytes); 207 | blob->len = size; 208 | blob->blob = (uint8_t *)malloc(size * sizeof(uint8_t)); 209 | memcpy(blob->blob, *bytes, size); 210 | *bytes = *bytes + size; 211 | 212 | // LOG("blob: %ld\n", blob->len); 213 | // LOG("%s\n", bytes_to_hex(blob->blob, blob->len)); 214 | } 215 | 216 | void extract_job(uint8_t **bytes, job_t *job) 217 | { 218 | job->from_group = extract_size(bytes); 219 | job->to_group = extract_size(bytes); 220 | // LOG("group: %d, %d\n", job->from_group, job->to_group); 221 | extract_blob(bytes, &job->header_blob); 222 | extract_blob(bytes, &job->txs_blob); 223 | extract_blob(bytes, &job->target); 224 | job->height = extract_size(bytes); 225 | } 226 | 227 | void extract_jobs(uint8_t **bytes, jobs_t *jobs) 228 | { 229 | ssize_t jobs_size = extract_size(bytes); 230 | 231 | // LOG("jobs: %ld\n", jobs_size); 232 | 233 | jobs->len = jobs_size; 234 | jobs->jobs = (job_t **)malloc(jobs_size * sizeof(job_t*)); 235 | for(ssize_t i = 0; i < jobs_size; i++) { 236 | jobs->jobs[i] = (job_t *)malloc(sizeof(job_t)); 237 | extract_job(bytes, (jobs->jobs[i])); 238 | } 239 | } 240 | 241 | void extract_block_hash(uint8_t **bytes, uint8_t *block_hash) 242 | { 243 | memcpy(block_hash, *bytes, 32); 244 | *bytes = *bytes + 32; 245 | } 246 | 247 | void extract_submit_result(uint8_t **bytes, submit_result_t *result) 248 | { 249 | result->from_group = extract_size(bytes); 250 | result->to_group = extract_size(bytes); 251 | extract_block_hash(bytes, result->block_hash); 252 | result->status = extract_bool(bytes); 253 | } 254 | 255 | server_message_t *decode_server_message(blob_t *blob) 256 | { 257 | uint8_t *bytes = blob->blob; 258 | ssize_t len = blob->len; 259 | 260 | if (len <= 4) { 261 | return NULL; // not enough bytes for decoding 262 | } 263 | 264 | uint8_t *pos = bytes; 265 | ssize_t message_size = extract_size(&pos); 266 | assert(pos == bytes + 4); 267 | 268 | ssize_t message_byte_size = message_size + 4; 269 | if (len < message_byte_size) { 270 | return NULL; // not enough bytes for decoding 271 | } 272 | 273 | uint8_t version = extract_byte(&pos); 274 | if (version != mining_protocol_version) { 275 | LOG("Invalid protocol version %d, expect %d\n", version, mining_protocol_version); 276 | exit(1); 277 | } 278 | 279 | server_message_t *server_message = (server_message_t *)malloc(sizeof(server_message_t)); 280 | switch (extract_byte(&pos)) 281 | { 282 | case 0: 283 | server_message->kind = JOBS; 284 | server_message->jobs = (jobs_t *)malloc(sizeof(jobs_t)); 285 | extract_jobs(&pos, server_message->jobs); 286 | 287 | // LOG("%p, %p, %p\n", bytes, pos, bytes + len); 288 | break; 289 | 290 | case 1: 291 | server_message->kind = SUBMIT_RESULT; 292 | server_message->submit_result = (submit_result_t *)malloc(sizeof(submit_result_t)); 293 | extract_submit_result(&pos, server_message->submit_result); 294 | break; 295 | 296 | default: 297 | LOGERR("Invalid server message kind\n"); 298 | exit(1); 299 | } 300 | 301 | assert(pos == (bytes + message_byte_size)); 302 | if (message_byte_size < len) { 303 | blob->len = len - message_byte_size; 304 | memmove(blob->blob, pos, blob->len); 305 | } else { 306 | blob->len = 0; 307 | } 308 | 309 | return server_message; 310 | } 311 | 312 | #endif // ALEPHIUM_MESSAGE_H 313 | -------------------------------------------------------------------------------- /src/mining.h: -------------------------------------------------------------------------------- 1 | #ifndef ALEPHIUM_MINING_H 2 | #define ALEPHIUM_MINING_H 3 | 4 | #include "blake3.cu" 5 | #include "log.h" 6 | 7 | //#define SHOW_MINING_TIME 1 8 | 9 | void worker_stream_callback(cudaStream_t stream, cudaError_t status, void *data); 10 | 11 | void start_worker_mining(mining_worker_t *worker) 12 | { 13 | cudaSetDevice(worker->device_id); 14 | 15 | reset_worker(worker); 16 | TRY( cudaMemcpyAsync(hasher(worker, false), hasher(worker, true), hasher_len(worker), cudaMemcpyHostToDevice, worker->stream) ); 17 | 18 | #ifdef SHOW_MINING_TIME 19 | cudaEvent_t startEvent, stopEvent; 20 | TRY( cudaEventCreate(&startEvent) ); 21 | TRY( cudaEventCreate(&stopEvent) ); 22 | TRY( cudaEventRecord(startEvent, worker->stream) ); 23 | #endif 24 | 25 | // blake3_hasher_mine<<grid_size, worker->block_size, 0, worker->stream>>>(worker->device_hasher); 26 | MINER_IMPL(worker)<<grid_size, worker->block_size, 0, worker->stream>>>(worker->device_hasher.inline_hasher); 27 | 28 | #ifdef SHOW_MINING_TIME 29 | TRY( cudaEventRecord(stopEvent, worker->stream) ); 30 | #endif 31 | 32 | TRY(cudaMemcpyAsync(hasher(worker, true), hasher(worker, false), hasher_len(worker), cudaMemcpyDeviceToHost, worker->stream)); 33 | 34 | TRY( cudaStreamAddCallback(worker->stream, worker_stream_callback, worker, 0) ); 35 | 36 | #ifdef SHOW_MINING_TIME 37 | float time; 38 | TRY( cudaEventElapsedTime(&time, startEvent, stopEvent) ); 39 | TRY( cudaEventDestroy(&startEvent) ); 40 | TRY( cudaEventDestroy(&stopEvent) ); 41 | LOG(" === mining time: %f\n", time); 42 | #endif 43 | } 44 | 45 | #endif // ALEPHIUM_MINING_H 46 | -------------------------------------------------------------------------------- /src/pow.h: -------------------------------------------------------------------------------- 1 | #ifndef ALEPHIUM_POW_H 2 | #define ALEPHIUM_POW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "constants.h" 11 | #include "messages.h" 12 | 13 | // bool check_target(uint8_t *hash, uint8_t *target_bytes, size_t target_len) 14 | // { 15 | // assert(target_len <= 32); 16 | 17 | // ssize_t zero_len = 32 - target_len; 18 | // for (ssize_t i = 0; i < zero_len; i++) { 19 | // if (hash[i] != 0) { 20 | // return false; 21 | // } 22 | // } 23 | // uint8_t *non_zero_hash = hash + zero_len; 24 | // for (ssize_t i = 0; i < target_len; i++) { 25 | // if (non_zero_hash[i] > target_bytes[i]) { 26 | // return false; 27 | // } else if (non_zero_hash[i] < target_bytes[i]) { 28 | // return true; 29 | // } 30 | // } 31 | // return true; 32 | // } 33 | 34 | // bool check_index(uint8_t *hash, uint32_t from_group, uint32_t to_group) 35 | // { 36 | // uint8_t big_index = hash[31] % chain_nums; 37 | // return (big_index / group_nums == from_group) && (big_index % group_nums == to_group); 38 | // } 39 | 40 | // bool check_hash(uint8_t *hash, uint8_t *target, size_t target_len, uint32_t from_group, uint32_t to_group) 41 | // { 42 | // return check_target(hash, target, target_len) && check_index(hash, from_group, to_group); 43 | // } 44 | 45 | #endif // ALEPHIUM_POW_H 46 | -------------------------------------------------------------------------------- /src/template.h: -------------------------------------------------------------------------------- 1 | #ifndef ALEPHIUM_TEMPLATE_H 2 | #define ALEPHIUM_TEMPLATE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "messages.h" 12 | // #include "blake3.cu" 13 | #include "uv.h" 14 | #include "constants.h" 15 | 16 | typedef struct mining_template_t { 17 | job_t *job; 18 | std::atomic ref_count; 19 | 20 | uint64_t chain_task_count; // increase this by one everytime the template for the chain is updated 21 | } mining_template_t; 22 | 23 | void store_template__ref_count(mining_template_t *template_ptr, uint32_t value) 24 | { 25 | atomic_store(&(template_ptr->ref_count), value); 26 | } 27 | 28 | uint32_t add_template__ref_count(mining_template_t *template_ptr, uint32_t value) 29 | { 30 | return atomic_fetch_add(&(template_ptr->ref_count), value); 31 | } 32 | 33 | uint32_t sub_template__ref_count(mining_template_t *template_ptr, uint32_t value) 34 | { 35 | return atomic_fetch_sub(&(template_ptr->ref_count), value); 36 | } 37 | 38 | void free_template(mining_template_t *template_ptr) 39 | { 40 | uint32_t old_count = sub_template__ref_count(template_ptr, 1); 41 | if (old_count == 1) { // fetch_sub returns original value 42 | free_job(template_ptr->job); 43 | free(template_ptr); 44 | } 45 | } 46 | 47 | std::atomic mining_templates[chain_nums] = {}; 48 | std::atomic mining_counts[chain_nums] = {}; 49 | uint64_t task_counts[chain_nums] = { 0 }; 50 | bool mining_templates_initialized = false; 51 | 52 | mining_template_t* load_template(ssize_t chain_index) 53 | { 54 | return atomic_load(&(mining_templates[chain_index])); 55 | } 56 | 57 | void store_template(ssize_t chain_index, mining_template_t* new_template) 58 | { 59 | atomic_store(&(mining_templates[chain_index]), new_template); 60 | } 61 | 62 | void update_templates(job_t *job) 63 | { 64 | mining_template_t *new_template = (mining_template_t *)malloc(sizeof(mining_template_t)); 65 | new_template->job = job; 66 | store_template__ref_count(new_template, 1); // referred by mining_templates 67 | 68 | ssize_t chain_index = job->from_group * group_nums + job->to_group; 69 | task_counts[chain_index] += 1; 70 | new_template->chain_task_count = task_counts[chain_index]; 71 | 72 | // TODO: optimize with atomic_exchange 73 | mining_template_t *last_template = load_template(chain_index); 74 | if (last_template) { 75 | free_template(last_template); 76 | } 77 | store_template(chain_index, new_template); 78 | } 79 | 80 | bool expire_template_for_new_block(mining_template_t *template_ptr) 81 | { 82 | job_t *job = template_ptr->job; 83 | ssize_t chain_index = job->from_group * group_nums + job->to_group; 84 | 85 | mining_template_t *latest_template = load_template(chain_index); 86 | if (latest_template) { 87 | store_template(chain_index, NULL); 88 | free_template(latest_template); 89 | return true; 90 | } else { 91 | return false; 92 | } 93 | } 94 | 95 | int32_t next_chain_to_mine() 96 | { 97 | int32_t to_mine_index = -1; 98 | uint64_t least_hash_count = UINT64_MAX; 99 | for (int32_t i = 0; i < chain_nums; i ++) { 100 | uint64_t i_hash_count = mining_counts[i].load(); 101 | if (load_template(i) && (i_hash_count < least_hash_count)) { 102 | to_mine_index = i; 103 | least_hash_count = i_hash_count; 104 | } 105 | } 106 | 107 | return to_mine_index; 108 | } 109 | 110 | #endif // ALEPHIUM_TEMPLATE_H 111 | -------------------------------------------------------------------------------- /src/worker.h: -------------------------------------------------------------------------------- 1 | #ifndef ALEPHIUM_WORKER_H 2 | #define ALEPHIUM_WORKER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "messages.h" 13 | #include "blake3.cu" 14 | #include "uv.h" 15 | #include "template.h" 16 | #include "log.h" 17 | 18 | typedef struct mining_worker_t { 19 | uint32_t id; 20 | 21 | int device_id; 22 | cudaStream_t stream; 23 | int grid_size; 24 | int block_size; 25 | union hasher { 26 | inline_blake::blake3_hasher *inline_hasher; 27 | ref_blake::blake3_hasher *ref_hasher; 28 | }; 29 | 30 | hasher host_hasher; 31 | hasher device_hasher; 32 | 33 | bool is_inline_miner; 34 | 35 | std::atomic found_good_hash; 36 | std::atomic template_ptr; 37 | 38 | std::mt19937 random_gen; 39 | 40 | uv_async_t async; 41 | uv_timer_t timer; 42 | } mining_worker_t; 43 | 44 | 45 | #define MINER_IMPL(worker) ((worker)->is_inline_miner ? inline_blake::blake3_hasher_mine:ref_blake::blake3_hasher_mine) 46 | #define HASHER(worker, host) ((host) ? (worker)->host_hasher:(worker)->device_hasher) 47 | #define HASHER_ELEM(hasher, is_inline, elem) ((is_inline) ? (hasher).inline_hasher->elem:(hasher).ref_hasher->elem) 48 | 49 | 50 | // Helper methods 51 | void *hasher(mining_worker_t *self, bool is_host) { 52 | return self->is_inline_miner ? reinterpret_cast(HASHER(self, is_host).inline_hasher) 53 | : reinterpret_cast(HASHER(self, is_host).ref_hasher); 54 | } 55 | 56 | void **hasher_ptr(mining_worker_t *self, bool is_host) { 57 | return self->is_inline_miner ? reinterpret_cast(&HASHER(self, is_host).inline_hasher) 58 | : reinterpret_cast(&HASHER(self, is_host).ref_hasher); 59 | } 60 | 61 | size_t hasher_len(mining_worker_t *self) { 62 | return self->is_inline_miner ? sizeof(inline_blake::blake3_hasher) 63 | : sizeof(ref_blake::blake3_hasher); 64 | } 65 | 66 | uint8_t *hasher_buf(mining_worker_t *self, bool is_host) { 67 | return self->is_inline_miner ? HASHER(self, is_host).inline_hasher->buf 68 | : HASHER(self, is_host).ref_hasher->buf; 69 | } 70 | 71 | uint8_t *hasher_hash(mining_worker_t *self, bool is_host) { 72 | return self->is_inline_miner ? HASHER(self, is_host).inline_hasher->hash 73 | : HASHER(self, is_host).ref_hasher->hash; 74 | } 75 | 76 | size_t hasher_hash_len(mining_worker_t *self) { 77 | return self->is_inline_miner ? sizeof(self->host_hasher.inline_hasher->hash) 78 | : sizeof(self->host_hasher.ref_hasher->hash); 79 | } 80 | 81 | uint32_t hasher_hash_count(mining_worker_t *self, bool is_host) { 82 | return self->is_inline_miner ? HASHER(self, is_host).inline_hasher->hash_count 83 | : HASHER(self, is_host).ref_hasher->hash_count; 84 | } 85 | 86 | int hasher_found_good_hash(mining_worker_t *self, bool is_host) { 87 | return self->is_inline_miner ? HASHER(self, is_host).inline_hasher->found_good_hash 88 | : HASHER(self, is_host).ref_hasher->found_good_hash; 89 | } 90 | 91 | 92 | void mining_worker_init(mining_worker_t *self, uint32_t id, int device_id) { 93 | self->id = id; 94 | 95 | self->device_id = device_id; 96 | cudaSetDevice(device_id); 97 | TRY(cudaStreamCreate(&(self->stream))); 98 | config_cuda(device_id, &self->grid_size, &self->block_size, &self->is_inline_miner); 99 | LOG("Worker %d: device id %d, grid size %d, block size %d. Using %s kernel\n", self->id, self->device_id, 100 | self->grid_size, self->block_size, self->is_inline_miner ? "inline" : "reference"); 101 | 102 | TRY(cudaMallocHost(hasher_ptr(self, true), hasher_len(self))); 103 | TRY(cudaMalloc(hasher_ptr(self, false), hasher_len(self))); 104 | memset(hasher_buf(self, true), 0, BLAKE3_BUF_CAP); 105 | memset(hasher_hash(self, true), 0, hasher_hash_len(self)); 106 | self->random_gen.seed(self->id + (uint64_t) self + (uint64_t) hasher(self, true) + rand()); 107 | 108 | } 109 | 110 | bool load_worker__found_good_hash(mining_worker_t *worker) { 111 | return atomic_load(&(worker->found_good_hash)); 112 | } 113 | 114 | void store_worker_found_good_hash(mining_worker_t *worker, bool value) { 115 | atomic_store(&(worker->found_good_hash), value); 116 | } 117 | 118 | mining_template_t *load_worker__template(mining_worker_t *worker) { 119 | return atomic_load(&(worker->template_ptr)); 120 | } 121 | 122 | void store_worker__template(mining_worker_t *worker, mining_template_t *template_ptr) { 123 | atomic_store(&(worker->template_ptr), template_ptr); 124 | } 125 | 126 | void reset_worker(mining_worker_t *worker) { 127 | std::uniform_int_distribution<> distrib(0, UINT8_MAX); 128 | mining_template_t *template_ptr = worker->template_ptr.load(); 129 | job_t *job = template_ptr->job; 130 | 131 | for (int i = 0; i < 24; i++) { 132 | hasher_buf(worker, true)[i] = distrib(worker->random_gen); 133 | } 134 | 135 | memcpy(hasher_buf(worker, true) + 24, job->header_blob.blob, job->header_blob.len); 136 | assert((24 + job->header_blob.len) == BLAKE3_BUF_LEN); 137 | assert((24 + job->header_blob.len + 63) / 64 * 64 == BLAKE3_BUF_CAP); 138 | 139 | size_t target_zero_len = 32 - job->target.len; 140 | 141 | memset(HASHER_ELEM(worker->host_hasher, worker->is_inline_miner, target), 0, target_zero_len); 142 | memcpy(HASHER_ELEM(worker->host_hasher, worker->is_inline_miner, target) + target_zero_len, job->target.blob, 143 | job->target.len); 144 | HASHER_ELEM(worker->host_hasher, worker->is_inline_miner, from_group) = job->from_group; 145 | HASHER_ELEM(worker->host_hasher, worker->is_inline_miner, to_group) = job->to_group; 146 | HASHER_ELEM(worker->host_hasher, worker->is_inline_miner, hash_count) = 0; 147 | HASHER_ELEM(worker->host_hasher, worker->is_inline_miner, found_good_hash) = false; 148 | 149 | store_worker_found_good_hash(worker, false); 150 | } 151 | 152 | typedef struct mining_req { 153 | std::atomic worker; 154 | } mining_req_t; 155 | 156 | uv_work_t req[max_worker_num] = {NULL}; 157 | mining_worker_t mining_workers[max_worker_num]; 158 | 159 | mining_worker_t *load_req_worker(uv_work_t *req) { 160 | mining_req_t *mining_req = (mining_req_t *) req->data; 161 | return atomic_load(&(mining_req->worker)); 162 | } 163 | 164 | void store_req_data(ssize_t worker_id, mining_worker_t *worker) { 165 | if (!req[worker_id].data) { 166 | req[worker_id].data = malloc(sizeof(mining_req_t)); 167 | } 168 | mining_req_t *mining_req = (mining_req_t *) (req[worker_id].data); 169 | atomic_store(&(mining_req->worker), worker); 170 | } 171 | 172 | void mining_workers_init(int gpu_count) { 173 | for (size_t i = 0; i < gpu_count * parallel_mining_works_per_gpu; i++) { 174 | mining_worker_t *worker = mining_workers + i; 175 | mining_worker_init(worker, (uint32_t) i, i % gpu_count); 176 | store_req_data(i, worker); 177 | } 178 | } 179 | 180 | ssize_t write_new_block(mining_worker_t *worker, uint8_t *write_buf) { 181 | job_t *job = load_worker__template(worker)->job; 182 | uint8_t *nonce = worker->is_inline_miner ? worker->host_hasher.inline_hasher->buf 183 | : worker->host_hasher.ref_hasher->buf; 184 | uint8_t *write_pos = write_buf; 185 | 186 | ssize_t block_size = 24 + job->header_blob.len + job->txs_blob.len; 187 | ssize_t message_size = 1 + 1 + 4 + block_size; 188 | 189 | write_size(&write_pos, message_size); 190 | write_byte(&write_pos, mining_protocol_version); // version 191 | write_byte(&write_pos, 0); // message type 192 | write_size(&write_pos, block_size); 193 | write_bytes(&write_pos, nonce, 24); 194 | write_blob(&write_pos, &job->header_blob); 195 | write_blob(&write_pos, &job->txs_blob); 196 | 197 | return message_size + 4; 198 | } 199 | 200 | void setup_template(mining_worker_t *worker, mining_template_t *template_ptr) { 201 | add_template__ref_count(template_ptr, 1); 202 | store_worker__template(worker, template_ptr); 203 | } 204 | 205 | #endif // ALEPHIUM_WORKER_H 206 | -------------------------------------------------------------------------------- /test/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "messages.h" 7 | #include "pow.h" 8 | #include "blake3.h" 9 | 10 | static void convert_hex(void **state) 11 | { 12 | char *hex_string = "1234abcd"; 13 | 14 | blob_t blob; 15 | hex_to_bytes(hex_string, &blob); 16 | assert_int_equal(blob.len, 4); 17 | 18 | char *hex_decoded = bytes_to_hex((uint8_t *)blob.blob, blob.len); 19 | assert_string_equal(hex_string, hex_decoded); 20 | } 21 | 22 | static void decode_size_test(void **state) 23 | { 24 | uint8_t bytes[] = {0, 1, 2, 3}; 25 | assert_int_equal(decode_size(bytes), 0x00010203); 26 | } 27 | 28 | static void extract_blob_test(void **state) 29 | { 30 | uint8_t *bytes = (uint8_t[]){0, 0, 0, 2, 3, 4}; 31 | blob_t blob; 32 | 33 | uint8_t *old_pos = bytes; 34 | extract_blob(&bytes, &blob); 35 | assert_ptr_equal(bytes, old_pos + 6); 36 | assert_int_equal(blob.len, 2); 37 | assert_int_equal(blob.blob[0], 3); 38 | assert_int_equal(blob.blob[1], 4); 39 | } 40 | 41 | char *hex_jobs = "0000001000000000000000000000012e00070000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec411500001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f00001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c000002349b6af9967de46fc5e7cce9858fb8a7b7ef334e194ab2a040b21e511b100000225e862ddfed99cda3f859a01758a58c90876c703676e0dccb35951f97200001832f64f200d39da63f58d059d062ba6ce7df1218a0af6a1ad6cb859a1334a454d47afc7672488d906f7699d6a2f73fae33c265afc6d9eda3b60351cd2fa60cd823ebdd1605cd2ff4a80c3540dd93ad20a9957519d4dc9612052dc4ed11b0000017bf884c7a71e2456710000004f01010080004e20bb9aca000001c41a055690d9db800000627ae790cbf98235030992faf3496d94da8900ba1dae0ee3458b10a20b633e500000017bf88def67000a00000000017bf884c7a7000000000000001e24567100000000000000000000000000000000000000000000000000000000000000000000010000012e00070000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec411500001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f00001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c000002349b6af9967de46fc5e7cce9858fb8a7b7ef334e194ab2a040b21e511b100000225e862ddfed99cda3f859a01758a58c90876c703676e0dccb35951f97200001832f64f200d39da63f58d059d062ba6ce7df1218a0af6a1ad6cb859a1334a454d47afc7672488d906f7699d6a2f73fae33c265afc6d9eda3b60351cd2fadc52ede9d0afacaddbecff9f1925e08e7bba59556dabfcb93f60aa9d218d6a340000017bf884c7b11e25f54d0000004f01010080004e20bb9aca000001c41a055690d9db800000303b30e2c4379f0bd813e60f86b412f21ac92c8ba9012e79da61ff714fe166fd0000017bf88def71000a00010000017bf884c7b1000000000000001e25f54d00000000000000000000000000000000000000000000000000000000000000000000020000012e00070000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec411500001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f00001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c000002349b6af9967de46fc5e7cce9858fb8a7b7ef334e194ab2a040b21e511b100000225e862ddfed99cda3f859a01758a58c90876c703676e0dccb35951f97200001832f64f200d39da63f58d059d062ba6ce7df1218a0af6a1ad6cb859a1334a454d47afc7672488d906f7699d6a2f73fae33c265afc6d9eda3b60351cd2faa93b9a5754e2ce79b5bc3bc34fa54e12ed01293eebb11090a859d401ae93c5a70000017bf884c7b81e27eca80000004f01010080004e20bb9aca000001c41a055690d9db80000047d375949b85b11ffd5385518183926ac12df8a77b5e768a11b4d7ca44aa71e20000017bf88def78000a00020000017bf884c7b8000000000000001e27eca800000000000000000000000000000000000000000000000000000000000000000000030000012e00070000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec411500001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f00001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c000002349b6af9967de46fc5e7cce9858fb8a7b7ef334e194ab2a040b21e511b100000225e862ddfed99cda3f859a01758a58c90876c703676e0dccb35951f97200001832f64f200d39da63f58d059d062ba6ce7df1218a0af6a1ad6cb859a1334a454d47afc7672488d906f7699d6a2f73fae33c265afc6d9eda3b60351cd2fa86188b14528c7b14eb989c0b4b39c32b0db8c63a31702096c261bc7ebc02225d0000017bf884c7bb1e25531e0000004f01010080004e20bb9aca000001c41a055690d9db80000097e542bd01a14e12b1a8076e55acdcdfe4704eb297ace191402b5a58cb81016f0000017bf88def7b000a00030000017bf884c7bb000000000000001e25531e00000000000000000000000000000000000000000000000000000000000001000000000000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c000001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f00001503034b6a8a3906abe80b33dab6f7311339fd2130df61f01e2e43319cc40000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec41150000111035bb3104c52f7886fc297522dc434a0898ea3c89a0fced320b45c3160000170a4d2327a15f77e5ed8b17f422b711376d09969282bb98065af15d26576c2d43c7d78c8f904c9a89b433cddec61874044cb95591a112c15511b487a7bf88a12da4b0617df1ce801186250f0b7ac213334b1e258ce25c8a40d9ac48f6c80000017bf884c7c01e21af500000004f01010080004e20bb9aca000001c41a055690d9db800000627ae790cbf98235030992faf3496d94da8900ba1dae0ee3458b10a20b633e500000017bf88def80000a01000000017bf884c7c0000000000000001e21af5000000000000000000000000000000000000000000000000000000000000001000000010000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c000001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f00001503034b6a8a3906abe80b33dab6f7311339fd2130df61f01e2e43319cc40000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec41150000111035bb3104c52f7886fc297522dc434a0898ea3c89a0fced320b45c3160000170a4d2327a15f77e5ed8b17f422b711376d09969282bb98065af15d26576c2d43c7d78c8f904c9a89b433cddec61874044cb95591a112c15511b487a7bff35a7b57892451169c449661b99dd9d81b2090511c17f74d13b59e6575ef5a260000017bf884c7c11e2330de0000004f01010080004e20bb9aca000001c41a055690d9db800000303b30e2c4379f0bd813e60f86b412f21ac92c8ba9012e79da61ff714fe166fd0000017bf88def81000a01010000017bf884c7c1000000000000001e2330de00000000000000000000000000000000000000000000000000000000000001000000020000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c000001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f00001503034b6a8a3906abe80b33dab6f7311339fd2130df61f01e2e43319cc40000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec41150000111035bb3104c52f7886fc297522dc434a0898ea3c89a0fced320b45c3160000170a4d2327a15f77e5ed8b17f422b711376d09969282bb98065af15d26576c2d43c7d78c8f904c9a89b433cddec61874044cb95591a112c15511b487a7bffe7810d753243282941e09b840edb58a34072b71a1bc9c3676976e75d59580490000017bf884c7c31e241e5d0000004f01010080004e20bb9aca000001c41a055690d9db80000047d375949b85b11ffd5385518183926ac12df8a77b5e768a11b4d7ca44aa71e20000017bf88def83000a01020000017bf884c7c3000000000000001e241e5d00000000000000000000000000000000000000000000000000000000000001000000030000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c000001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f00001503034b6a8a3906abe80b33dab6f7311339fd2130df61f01e2e43319cc40000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec41150000111035bb3104c52f7886fc297522dc434a0898ea3c89a0fced320b45c3160000170a4d2327a15f77e5ed8b17f422b711376d09969282bb98065af15d26576c2d43c7d78c8f904c9a89b433cddec61874044cb95591a112c15511b487a7bff903273a7106fd99f20a3ba9124a36caa71297b8a9e5d318eb3fbff66ec90fb60000017bf884c7c41e2100610000004f01010080004e20bb9aca000001c41a055690d9db80000097e542bd01a14e12b1a8076e55acdcdfe4704eb297ace191402b5a58cb81016f0000017bf88def84000a01030000017bf884c7c4000000000000001e21006100000000000000000000000000000000000000000000000000000000000002000000000000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c00000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec4115000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f0000179daff6fb7468cdf1ee72dd94f3912e33786490dcc550c3e77e644fa22800001c16be82a2a19e26def1300afe7b19881463c255664f0ce1ec8a982eb6f900001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000010f1715760c2be3563a5e34bd3b6155e6f6f13e39a8056f46f3bf34ab24b0c23cfe2589f2ece18830f0c9dd6e07db135efc1f3adf88c2111e90a3ef7c08b9e9ff0d009a864419f6c56449d1be704f3f7ffd0909303db506844ec5d763b610000017bf884c7c51e26da660000004f01010080004e20bb9aca000001c41a055690d9db800000627ae790cbf98235030992faf3496d94da8900ba1dae0ee3458b10a20b633e500000017bf88def85000a02000000017bf884c7c5000000000000001e26da6600000000000000000000000000000000000000000000000000000000000002000000010000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c00000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec4115000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f0000179daff6fb7468cdf1ee72dd94f3912e33786490dcc550c3e77e644fa22800001c16be82a2a19e26def1300afe7b19881463c255664f0ce1ec8a982eb6f900001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000010f1715760c2be3563a5e34bd3b6155e6f6f13e39a8056f46f3bf34ab24b0c23cfe2589f2ece18830f0c9dd6e07db135efc1f3adf88c2111e90a3ef7c08b41812cddacd14fe1dd2fba9bffb4e1d89e90ef5ec3b5eb5b5ed1a45e4bad90880000017bf884c7c51e22dd680000004f01010080004e20bb9aca000001c41a055690d9db800000303b30e2c4379f0bd813e60f86b412f21ac92c8ba9012e79da61ff714fe166fd0000017bf88def85000a02010000017bf884c7c5000000000000001e22dd6800000000000000000000000000000000000000000000000000000000000002000000020000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c00000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec4115000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f0000179daff6fb7468cdf1ee72dd94f3912e33786490dcc550c3e77e644fa22800001c16be82a2a19e26def1300afe7b19881463c255664f0ce1ec8a982eb6f900001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000010f1715760c2be3563a5e34bd3b6155e6f6f13e39a8056f46f3bf34ab24b0c23cfe2589f2ece18830f0c9dd6e07db135efc1f3adf88c2111e90a3ef7c08b7f1080cba230811d2a0b28149534cc0f62ff18f4b8c56e62e7b55a06a36c1c070000017bf884c7ce1e2322e10000004f01010080004e20bb9aca000001c41a055690d9db80000047d375949b85b11ffd5385518183926ac12df8a77b5e768a11b4d7ca44aa71e20000017bf88def8e000a02020000017bf884c7ce000000000000001e2322e100000000000000000000000000000000000000000000000000000000000002000000030000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c00000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec4115000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f0000179daff6fb7468cdf1ee72dd94f3912e33786490dcc550c3e77e644fa22800001c16be82a2a19e26def1300afe7b19881463c255664f0ce1ec8a982eb6f900001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000010f1715760c2be3563a5e34bd3b6155e6f6f13e39a8056f46f3bf34ab24b0c23cfe2589f2ece18830f0c9dd6e07db135efc1f3adf88c2111e90a3ef7c08b14bca80748f00984d765e578b1fdc20312cb5d26eba70cc279c64fe3e096b43f0000017bf884c7d01e22e1700000004f01010080004e20bb9aca000001c41a055690d9db80000097e542bd01a14e12b1a8076e55acdcdfe4704eb297ace191402b5a58cb81016f0000017bf88def90000a02030000017bf884c7d0000000000000001e22e17000000000000000000000000000000000000000000000000000000000000003000000000000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c00000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec411500001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000007f5ce0dcddcfd21ba899e4988227315aec1b303b0a68653c3c6f7b843ac00001a557a4e95d0ce810bef592d9b81596da5b43220699a0339c0d0cf21958d000018d15bf8dc5f105110e2a8053d9ce0b681a62f61e742f2f71680c05a105e000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f9518f6ccc3e057e886ffddf524bb0687a203dfc8ca7db6e23001bf1c8eab10411fd91418568a8b856af4609e4dfa376e32803e2d7870d489b2a2d444a254b9160000017bf884c7d31e278c480000004f01010080004e20bb9aca000001c41a055690d9db800000627ae790cbf98235030992faf3496d94da8900ba1dae0ee3458b10a20b633e500000017bf88def93000a03000000017bf884c7d3000000000000001e278c4800000000000000000000000000000000000000000000000000000000000003000000010000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c00000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec411500001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000007f5ce0dcddcfd21ba899e4988227315aec1b303b0a68653c3c6f7b843ac00001a557a4e95d0ce810bef592d9b81596da5b43220699a0339c0d0cf21958d000018d15bf8dc5f105110e2a8053d9ce0b681a62f61e742f2f71680c05a105e000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f9518f6ccc3e057e886ffddf524bb0687a203dfc8ca7db6e23001bf1c8eab1041d89486ac231f0c3df2be758e5d3f95ded505de69a3e98c5fcb6d0c5453f67f6c0000017bf884c7d31e24a7ae0000004f01010080004e20bb9aca000001c41a055690d9db800000303b30e2c4379f0bd813e60f86b412f21ac92c8ba9012e79da61ff714fe166fd0000017bf88def93000a03010000017bf884c7d3000000000000001e24a7ae00000000000000000000000000000000000000000000000000000000000003000000020000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c00000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec411500001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000007f5ce0dcddcfd21ba899e4988227315aec1b303b0a68653c3c6f7b843ac00001a557a4e95d0ce810bef592d9b81596da5b43220699a0339c0d0cf21958d000018d15bf8dc5f105110e2a8053d9ce0b681a62f61e742f2f71680c05a105e000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f9518f6ccc3e057e886ffddf524bb0687a203dfc8ca7db6e23001bf1c8eab1041c90c1f5cebc84972c0d515a4aec63420aac1be0a62f902f385d4c18c0f8154ff0000017bf884c7d51e250f0b0000004f01010080004e20bb9aca000001c41a055690d9db80000047d375949b85b11ffd5385518183926ac12df8a77b5e768a11b4d7ca44aa71e20000017bf88def95000a03020000017bf884c7d5000000000000001e250f0b00000000000000000000000000000000000000000000000000000000000003000000030000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c00000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec411500001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000007f5ce0dcddcfd21ba899e4988227315aec1b303b0a68653c3c6f7b843ac00001a557a4e95d0ce810bef592d9b81596da5b43220699a0339c0d0cf21958d000018d15bf8dc5f105110e2a8053d9ce0b681a62f61e742f2f71680c05a105e000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f9518f6ccc3e057e886ffddf524bb0687a203dfc8ca7db6e23001bf1c8eab1041385cfb0633b1f0728b648375d7982368d76e3f1cf4ac4a4d3f94ebdb59a40edd0000017bf884c7d91e22b1ca0000004f01010080004e20bb9aca000001c41a055690d9db80000097e542bd01a14e12b1a8076e55acdcdfe4704eb297ace191402b5a58cb81016f0000017bf88def99000a03030000017bf884c7d9000000000000001e22b1ca000000000000000000000000000000000000000000000000000000"; 42 | static void extract_jobs_test(void **state) 43 | { 44 | assert_int_equal(strlen(hex_jobs), 13800); 45 | blob_t blob; 46 | hex_to_bytes(hex_jobs, &blob); 47 | 48 | jobs_t jobs; 49 | extract_jobs(&blob.blob, &jobs); 50 | assert_int_equal(jobs.len, 16); 51 | } 52 | 53 | char *hex_server_message0 = "00001af5000000001000000000000000000000012e00070000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec411500001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f00001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c000002349b6af9967de46fc5e7cce9858fb8a7b7ef334e194ab2a040b21e511b100000225e862ddfed99cda3f859a01758a58c90876c703676e0dccb35951f97200001832f64f200d39da63f58d059d062ba6ce7df1218a0af6a1ad6cb859a1334a454d47afc7672488d906f7699d6a2f73fae33c265afc6d9eda3b60351cd2fa60cd823ebdd1605cd2ff4a80c3540dd93ad20a9957519d4dc9612052dc4ed11b0000017bf884c7a71e2456710000004f01010080004e20bb9aca000001c41a055690d9db800000627ae790cbf98235030992faf3496d94da8900ba1dae0ee3458b10a20b633e500000017bf88def67000a00000000017bf884c7a7000000000000001e24567100000000000000000000000000000000000000000000000000000000000000000000010000012e00070000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec411500001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f00001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c000002349b6af9967de46fc5e7cce9858fb8a7b7ef334e194ab2a040b21e511b100000225e862ddfed99cda3f859a01758a58c90876c703676e0dccb35951f97200001832f64f200d39da63f58d059d062ba6ce7df1218a0af6a1ad6cb859a1334a454d47afc7672488d906f7699d6a2f73fae33c265afc6d9eda3b60351cd2fadc52ede9d0afacaddbecff9f1925e08e7bba59556dabfcb93f60aa9d218d6a340000017bf884c7b11e25f54d0000004f01010080004e20bb9aca000001c41a055690d9db800000303b30e2c4379f0bd813e60f86b412f21ac92c8ba9012e79da61ff714fe166fd0000017bf88def71000a00010000017bf884c7b1000000000000001e25f54d00000000000000000000000000000000000000000000000000000000000000000000020000012e00070000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec411500001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f00001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c000002349b6af9967de46fc5e7cce9858fb8a7b7ef334e194ab2a040b21e511b100000225e862ddfed99cda3f859a01758a58c90876c703676e0dccb35951f97200001832f64f200d39da63f58d059d062ba6ce7df1218a0af6a1ad6cb859a1334a454d47afc7672488d906f7699d6a2f73fae33c265afc6d9eda3b60351cd2faa93b9a5754e2ce79b5bc3bc34fa54e12ed01293eebb11090a859d401ae93c5a70000017bf884c7b81e27eca80000004f01010080004e20bb9aca000001c41a055690d9db80000047d375949b85b11ffd5385518183926ac12df8a77b5e768a11b4d7ca44aa71e20000017bf88def78000a00020000017bf884c7b8000000000000001e27eca800000000000000000000000000000000000000000000000000000000000000000000030000012e00070000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec411500001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f00001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c000002349b6af9967de46fc5e7cce9858fb8a7b7ef334e194ab2a040b21e511b100000225e862ddfed99cda3f859a01758a58c90876c703676e0dccb35951f97200001832f64f200d39da63f58d059d062ba6ce7df1218a0af6a1ad6cb859a1334a454d47afc7672488d906f7699d6a2f73fae33c265afc6d9eda3b60351cd2fa86188b14528c7b14eb989c0b4b39c32b0db8c63a31702096c261bc7ebc02225d0000017bf884c7bb1e25531e0000004f01010080004e20bb9aca000001c41a055690d9db80000097e542bd01a14e12b1a8076e55acdcdfe4704eb297ace191402b5a58cb81016f0000017bf88def7b000a00030000017bf884c7bb000000000000001e25531e00000000000000000000000000000000000000000000000000000000000001000000000000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c000001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f00001503034b6a8a3906abe80b33dab6f7311339fd2130df61f01e2e43319cc40000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec41150000111035bb3104c52f7886fc297522dc434a0898ea3c89a0fced320b45c3160000170a4d2327a15f77e5ed8b17f422b711376d09969282bb98065af15d26576c2d43c7d78c8f904c9a89b433cddec61874044cb95591a112c15511b487a7bf88a12da4b0617df1ce801186250f0b7ac213334b1e258ce25c8a40d9ac48f6c80000017bf884c7c01e21af500000004f01010080004e20bb9aca000001c41a055690d9db800000627ae790cbf98235030992faf3496d94da8900ba1dae0ee3458b10a20b633e500000017bf88def80000a01000000017bf884c7c0000000000000001e21af5000000000000000000000000000000000000000000000000000000000000001000000010000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c000001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f00001503034b6a8a3906abe80b33dab6f7311339fd2130df61f01e2e43319cc40000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec41150000111035bb3104c52f7886fc297522dc434a0898ea3c89a0fced320b45c3160000170a4d2327a15f77e5ed8b17f422b711376d09969282bb98065af15d26576c2d43c7d78c8f904c9a89b433cddec61874044cb95591a112c15511b487a7bff35a7b57892451169c449661b99dd9d81b2090511c17f74d13b59e6575ef5a260000017bf884c7c11e2330de0000004f01010080004e20bb9aca000001c41a055690d9db800000303b30e2c4379f0bd813e60f86b412f21ac92c8ba9012e79da61ff714fe166fd0000017bf88def81000a01010000017bf884c7c1000000000000001e2330de00000000000000000000000000000000000000000000000000000000000001000000020000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c000001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f00001503034b6a8a3906abe80b33dab6f7311339fd2130df61f01e2e43319cc40000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec41150000111035bb3104c52f7886fc297522dc434a0898ea3c89a0fced320b45c3160000170a4d2327a15f77e5ed8b17f422b711376d09969282bb98065af15d26576c2d43c7d78c8f904c9a89b433cddec61874044cb95591a112c15511b487a7bffe7810d753243282941e09b840edb58a34072b71a1bc9c3676976e75d59580490000017bf884c7c31e241e5d0000004f01010080004e20bb9aca000001c41a055690d9db80000047d375949b85b11ffd5385518183926ac12df8a77b5e768a11b4d7ca44aa71e20000017bf88def83000a01020000017bf884c7c3000000000000001e241e5d00000000000000000000000000000000000000000000000000000000000001000000030000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c000001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f00001503034b6a8a3906abe80b33dab6f7311339fd2130df61f01e2e43319cc40000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec41150000111035bb3104c52f7886fc297522dc434a0898ea3c89a0fced320b45c3160000170a4d2327a15f77e5ed8b17f422b711376d09969282bb98065af15d26576c2d43c7d78c8f904c9a89b433cddec61874044cb95591a112c15511b487a7bff903273a7106fd99f20a3ba9124a36caa71297b8a9e5d318eb3fbff66ec90fb60000017bf884c7c41e2100610000004f01010080004e20bb9aca000001c41a055690d9db80000097e542bd01a14e12b1a8076e55acdcdfe4704eb297ace191402b5a58cb81016f0000017bf88def84000a01030000017bf884c7c4000000000000001e21006100000000000000000000000000000000000000000000000000000000000002000000000000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c00000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec4115000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f0000179daff6fb7468cdf1ee72dd94f3912e33786490dcc550c3e77e644fa22800001c16be82a2a19e26def1300afe7b19881463c255664f0ce1ec8a982eb6f900001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000010f1715760c2be3563a5e34bd3b6155e6f6f13e39a8056f46f3bf34ab24b0c23cfe2589f2ece18830f0c9dd6e07db135efc1f3adf88c2111e90a3ef7c08b9e9ff0d009a864419f6c56449d1be704f3f7ffd0909303db506844ec5d763b610000017bf884c7c51e26da660000004f01010080004e20bb9aca000001c41a055690d9db800000627ae790cbf98235030992faf3496d94da8900ba1dae0ee3458b10a20b633e500000017bf88def85000a02000000017bf884c7c5000000000000001e26da6600000000000000000000000000000000000000000000000000000000000002000000010000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c00000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec4115000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f0000179daff6fb7468cdf1ee72dd94f3912e33786490dcc550c3e77e644fa22800001c16be82a2a19e26def1300afe7b19881463c255664f0ce1ec8a982eb6f900001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000010f1715760c2be3563a5e34bd3b6155e6f6f13e39a8056f46f3bf34ab24b0c23cfe2589f2ece18830f0c9dd6e07db135efc1f3adf88c2111e90a3ef7c08b41812cddacd14fe1dd2fba9bffb4e1d89e90ef5ec3b5eb5b5ed1a45e4bad90880000017bf884c7c51e22dd680000004f01010080004e20bb9aca000001c41a055690d9db800000303b30e2c4379f0bd813e60f86b412f21ac92c8ba9012e79da61ff714fe166fd0000017bf88def85000a02010000017bf884c7c5000000000000001e22dd6800000000000000000000000000000000000000000000000000000000000002000000020000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c00000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec4115000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f0000179daff6fb7468cdf1ee72dd94f3912e33786490dcc550c3e77e644fa22800001c16be82a2a19e26def1300afe7b19881463c255664f0ce1ec8a982eb6f900001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000010f1715760c2be3563a5e34bd3b6155e6f6f13e39a8056f46f3bf34ab24b0c23cfe2589f2ece18830f0c9dd6e07db135efc1f3adf88c2111e90a3ef7c08b7f1080cba230811d2a0b28149534cc0f62ff18f4b8c56e62e7b55a06a36c1c070000017bf884c7ce1e2322e10000004f01010080004e20bb9aca000001c41a055690d9db80000047d375949b85b11ffd5385518183926ac12df8a77b5e768a11b4d7ca44aa71e20000017bf88def8e000a02020000017bf884c7ce000000000000001e2322e100000000000000000000000000000000000000000000000000000000000002000000030000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c00000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec4115000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f0000179daff6fb7468cdf1ee72dd94f3912e33786490dcc550c3e77e644fa22800001c16be82a2a19e26def1300afe7b19881463c255664f0ce1ec8a982eb6f900001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000010f1715760c2be3563a5e34bd3b6155e6f6f13e39a8056f46f3bf34ab24b0c23cfe2589f2ece18830f0c9dd6e07db135efc1f3adf88c2111e90a3ef7c08b14bca80748f00984d765e578b1fdc20312cb5d26eba70cc279c64fe3e096b43f0000017bf884c7d01e22e1700000004f01010080004e20bb9aca000001c41a055690d9db80000097e542bd01a14e12b1a8076e55acdcdfe4704eb297ace191402b5a58cb81016f0000017bf88def90000a02030000017bf884c7d0000000000000001e22e17000000000000000000000000000000000000000000000000000000000000003000000000000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c00000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec411500001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000007f5ce0dcddcfd21ba899e4988227315aec1b303b0a68653c3c6f7b843ac00001a557a4e95d0ce810bef592d9b81596da5b43220699a0339c0d0cf21958d000018d15bf8dc5f105110e2a8053d9ce0b681a62f61e742f2f71680c05a105e000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f9518f6ccc3e057e886ffddf524bb0687a203dfc8ca7db6e23001bf1c8eab10411fd91418568a8b856af4609e4dfa376e32803e2d7870d489b2a2d444a254b9160000017bf884c7d31e278c480000004f01010080004e20bb9aca000001c41a055690d9db800000627ae790cbf98235030992faf3496d94da8900ba1dae0ee3458b10a20b633e500000017bf88def93000a03000000017bf884c7d3000000000000001e278c4800000000000000000000000000000000000000000000000000000000000003000000010000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c00000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec411500001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000007f5ce0dcddcfd21ba899e4988227315aec1b303b0a68653c3c6f7b843ac00001a557a4e95d0ce810bef592d9b81596da5b43220699a0339c0d0cf21958d000018d15bf8dc5f105110e2a8053d9ce0b681a62f61e742f2f71680c05a105e000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f9518f6ccc3e057e886ffddf524bb0687a203dfc8ca7db6e23001bf1c8eab1041d89486ac231f0c3df2be758e5d3f95ded505de69a3e98c5fcb6d0c5453f67f6c0000017bf884c7d31e24a7ae0000004f01010080004e20bb9aca000001c41a055690d9db800000303b30e2c4379f0bd813e60f86b412f21ac92c8ba9012e79da61ff714fe166fd0000017bf88def93000a03010000017bf884c7d3000000000000001e24a7ae00000000000000000000000000000000000000000000000000000000000003000000020000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c00000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec411500001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000007f5ce0dcddcfd21ba899e4988227315aec1b303b0a68653c3c6f7b843ac00001a557a4e95d0ce810bef592d9b81596da5b43220699a0339c0d0cf21958d000018d15bf8dc5f105110e2a8053d9ce0b681a62f61e742f2f71680c05a105e000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f9518f6ccc3e057e886ffddf524bb0687a203dfc8ca7db6e23001bf1c8eab1041c90c1f5cebc84972c0d515a4aec63420aac1be0a62f902f385d4c18c0f8154ff0000017bf884c7d51e250f0b0000004f01010080004e20bb9aca000001c41a055690d9db80000047d375949b85b11ffd5385518183926ac12df8a77b5e768a11b4d7ca44aa71e20000017bf88def95000a03020000017bf884c7d5000000000000001e250f0b00000000000000000000000000000000000000000000000000000000000003000000030000012e000700001e4218bfc7f7126f97192c3ae193ec5526eab4f0edb090246216f1aa80c00000161fe401202e1345d2d2f38d57dd5715f7f54953cde71b2d7ccd45ec411500001b25550e7be2d69b5f764ba833a02943a656605b320e79a6dc4dda88924a000007f5ce0dcddcfd21ba899e4988227315aec1b303b0a68653c3c6f7b843ac00001a557a4e95d0ce810bef592d9b81596da5b43220699a0339c0d0cf21958d000018d15bf8dc5f105110e2a8053d9ce0b681a62f61e742f2f71680c05a105e000009756a21e45136379b925bf024d9e6d4134a92bede7be5bc32801c2ae08f9518f6ccc3e057e886ffddf524bb0687a203dfc8ca7db6e23001bf1c8eab1041385cfb0633b1f0728b648375d7982368d76e3f1cf4ac4a4d3f94ebdb59a40edd0000017bf884c7d91e22b1ca0000004f01010080004e20bb9aca000001c41a055690d9db80000097e542bd01a14e12b1a8076e55acdcdfe4704eb297ace191402b5a58cb81016f0000017bf88def99000a03030000017bf884c7d9000000000000001e22b1ca000000000000000000000000000000000000000000000000000000"; 54 | static void extract_jobs_message_test(void **state) 55 | { 56 | assert_int_equal(strlen(hex_server_message0), 13810); 57 | blob_t blob; 58 | server_message_t *message; 59 | 60 | hex_to_bytes(hex_server_message0, &blob); 61 | message = decode_server_message(&blob); 62 | assert_int_equal(message->kind, JOBS); 63 | assert_int_equal(message->jobs->len, 16); 64 | assert_int_equal(blob.len, 0); 65 | 66 | char *partial_message = "00001af5000000001000000000000000000000012e00070000"; 67 | hex_to_bytes(partial_message, &blob); 68 | message = decode_server_message(&blob); 69 | assert_null(message); 70 | assert_int_equal(blob.len, 25); 71 | assert_string_equal(bytes_to_hex(blob.blob, blob.len), partial_message); 72 | 73 | char long_message[strlen(hex_server_message0) + strlen(partial_message) + 1]; 74 | strcpy(long_message, hex_server_message0); 75 | strcat(long_message, partial_message); 76 | hex_to_bytes(long_message, &blob); 77 | message = decode_server_message(&blob); 78 | assert_int_equal(message->kind, JOBS); 79 | assert_int_equal(message->jobs->len, 16); 80 | assert_int_equal(blob.len, 25); 81 | assert_string_equal(bytes_to_hex(blob.blob, blob.len), partial_message); 82 | } 83 | 84 | char *hex_server_message1 = "0000000a01000000000000000101"; 85 | static void extract_submit_result_message_test(void **state) 86 | { 87 | blob_t blob; 88 | hex_to_bytes(hex_server_message0, &blob); 89 | 90 | server_message_t *message = decode_server_message(&blob); 91 | assert_int_equal(message->kind, SUBMIT_RESULT); 92 | assert_int_equal(message->submit_result->from_group, 0); 93 | assert_int_equal(message->submit_result->to_group, 1); 94 | assert_int_equal(message->submit_result->status, true); 95 | } 96 | 97 | static void check_target_test(void **state) 98 | { 99 | blob_t hash; 100 | blob_t target; 101 | hex_to_bytes("00000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", &hash); 102 | 103 | hex_to_bytes("00000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", &target); 104 | assert_int_equal(check_target(hash.blob, &target), true); 105 | 106 | // remove 4 leading zeros 107 | hex_to_bytes("0000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", &target); 108 | assert_int_equal(check_target(hash.blob, &target), true); 109 | 110 | // remove all leading zeros 111 | hex_to_bytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", &target); 112 | assert_int_equal(check_target(hash.blob, &target), true); 113 | 114 | // remove all leading zeros + "aa" 115 | hex_to_bytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", &target); 116 | assert_int_equal(check_target(hash.blob, &target), false); 117 | 118 | // replace leading "aa" with "bb" 119 | hex_to_bytes("bbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", &target); 120 | assert_int_equal(check_target(hash.blob, &target), true); 121 | 122 | // replace the last "a" with "b" 123 | hex_to_bytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab", &target); 124 | assert_int_equal(check_target(hash.blob, &target), true); 125 | 126 | // replace the last "a" with "9" 127 | hex_to_bytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9", &target); 128 | assert_int_equal(check_target(hash.blob, &target), false); 129 | 130 | hex_to_bytes("a9aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", &target); 131 | assert_int_equal(check_target(hash.blob, &target), false); 132 | } 133 | 134 | static void check_index_test(void **state) 135 | { 136 | blob_t hash; 137 | hex_to_bytes("00000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaae", &hash); 138 | 139 | assert_int_equal(check_index(hash.blob, 3, 2), true); 140 | assert_int_equal(check_index(hash.blob, 3, 3), false); 141 | } 142 | 143 | static void write_size_test(void **state) 144 | { 145 | uint8_t buf[4]; 146 | uint8_t *pos = buf; 147 | 148 | write_size(&pos, 257); 149 | assert_int_equal(pos, buf + 4); 150 | assert_int_equal(buf[0], 0); 151 | assert_int_equal(buf[1], 0); 152 | assert_int_equal(buf[2], 1); 153 | assert_int_equal(buf[3], 1); 154 | } 155 | 156 | static void write_byte_test(void **state) 157 | { 158 | uint8_t buf[1]; 159 | uint8_t *pos = buf; 160 | 161 | write_byte(&pos, 111); 162 | assert_int_equal(pos, buf + 1); 163 | assert_int_equal(buf[0], 111); 164 | } 165 | 166 | static void write_bytes_test(void **state) 167 | { 168 | uint8_t buf[5]; 169 | uint8_t *pos = buf; 170 | 171 | blob_t blob; 172 | hex_to_bytes("01020a0b", &blob); 173 | write_bytes(&pos, blob.blob, blob.len); 174 | assert_int_equal(pos, buf + 4); 175 | assert_int_equal(buf[0], 1); 176 | assert_int_equal(buf[1], 2); 177 | assert_int_equal(buf[2], 10); 178 | assert_int_equal(buf[3], 11); 179 | } 180 | 181 | static void write_blob_test(void **state) 182 | { 183 | uint8_t buf[10]; 184 | uint8_t *pos = buf; 185 | 186 | blob_t blob; 187 | hex_to_bytes("01020a0b", &blob); 188 | write_blob(&pos, &blob); 189 | assert_int_equal(pos, buf + 4); 190 | assert_int_equal(buf[0], 1); 191 | assert_int_equal(buf[1], 2); 192 | assert_int_equal(buf[2], 10); 193 | assert_int_equal(buf[3], 11); 194 | } 195 | 196 | static void blake3_test(void **state) 197 | { 198 | blob_t blob; 199 | hex_to_bytes("012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", &blob); 200 | 201 | blake3_hasher hasher; 202 | blake3_hasher_init(&hasher); 203 | blake3_hasher_update(&hasher, blob.blob, blob.len); 204 | uint8_t hash[32]; 205 | blake3_hasher_finalize(&hasher, hash); 206 | char *hash_string = bytes_to_hex(hash, 32); 207 | char *expected = "5bbc7833cf38fb4d07a36ddcbd73bdf8b77a1877c8334d9fa535b91cf0da1199"; 208 | assert_string_equal(hash_string, expected); 209 | 210 | blake3_hasher_init(&hasher); 211 | blake3_hasher_update(&hasher, hash, 32); 212 | blake3_hasher_finalize(&hasher, hash); 213 | char *hash_string1 = bytes_to_hex(hash, 32); 214 | char *expected1 = "9953ad1fabb792a65283f692e6d2da939b133d1f167bf2bff9aa1b1b130c769e"; 215 | assert_string_equal(hash_string1, expected1); 216 | } 217 | 218 | int main(void) { 219 | 220 | const struct CMUnitTest tests[] = { 221 | cmocka_unit_test(convert_hex), 222 | cmocka_unit_test(decode_size_test), 223 | cmocka_unit_test(extract_blob_test), 224 | cmocka_unit_test(extract_jobs_test), 225 | cmocka_unit_test(extract_jobs_message_test), 226 | cmocka_unit_test(check_target_test), 227 | cmocka_unit_test(check_index_test), 228 | cmocka_unit_test(write_size_test), 229 | cmocka_unit_test(write_byte_test), 230 | cmocka_unit_test(write_bytes_test), 231 | cmocka_unit_test(write_blob_test), 232 | cmocka_unit_test(blake3_test), 233 | }; 234 | 235 | 236 | /* Run the tests */ 237 | return cmocka_run_group_tests(tests, NULL, NULL); 238 | } 239 | --------------------------------------------------------------------------------