├── .ci ├── install.yml └── release.yml ├── .gitignore ├── .gitmodules ├── CODE_OF_CONDUCT.md ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── README.md ├── azure-pipelines.yml ├── config ├── Cargo.toml └── src │ ├── config.rs │ ├── lib.rs │ └── types.rs ├── cuckoo-miner ├── Cargo.toml └── src │ ├── build.rs │ ├── config │ ├── mod.rs │ └── types.rs │ ├── cuckoo_sys │ ├── ffi.rs │ ├── mod.rs │ └── plugins │ │ ├── CMakeLists.txt │ │ └── cmake │ │ ├── CudaComputeTargetFlags.cmake │ │ └── find_cuda.cmake │ ├── error │ └── mod.rs │ ├── lib.rs │ └── miner │ ├── consensus.rs │ ├── miner.rs │ ├── mod.rs │ ├── types.rs │ └── util.rs ├── etc └── crate-release.sh ├── grin-miner.toml ├── install_ocl_plugins.sh ├── ocl_cuckaroo ├── .gitignore ├── Cargo.toml └── src │ ├── finder.rs │ ├── lib.rs │ ├── main.rs │ └── trimmer.rs ├── ocl_cuckatoo ├── .gitignore ├── Cargo.toml └── src │ ├── finder.rs │ ├── lib.rs │ └── trimmer.rs ├── plugin ├── Cargo.toml └── src │ └── lib.rs ├── rustfmt.toml ├── src ├── bin │ ├── client.rs │ ├── grin_miner.rs │ ├── mining.rs │ ├── stats.rs │ ├── tui │ │ ├── constants.rs │ │ ├── menu.rs │ │ ├── mining.rs │ │ ├── mod.rs │ │ ├── table.rs │ │ ├── types.rs │ │ ├── ui.rs │ │ └── version.rs │ └── types.rs └── build │ └── build.rs └── util ├── Cargo.toml └── src ├── hex.rs ├── lib.rs ├── logger.rs └── types.rs /.ci/install.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: | 3 | wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-repo-ubuntu1804_10.1.168-1_amd64.deb 4 | sudo dpkg -i cuda-repo-ubuntu1804_10.1.168-1_amd64.deb 5 | sudo apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub 6 | sudo apt-get update -yqq 7 | sudo apt-get install cuda ocl-icd-opencl-dev libncursesw5-dev 8 | displayName: Linux Install Dependencies 9 | condition: eq( variables['Agent.OS'], 'Linux' ) 10 | - script: | 11 | git submodule update --init --recursive 12 | displayName: Init Dependencies 13 | -------------------------------------------------------------------------------- /.ci/release.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: 'cargo build --release --features opencl' 3 | displayName: Build Release 4 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 5 | - script: | 6 | MY_TAG="$(Build.SourceBranch)" 7 | MY_TAG=${MY_TAG#refs/tags/} 8 | echo $MY_TAG 9 | echo "##vso[task.setvariable variable=build.my_tag]$MY_TAG" 10 | echo "##vso[task.setvariable variable=build.platform]$PLATFORM" 11 | displayName: "Create my tag variable" 12 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 13 | - task: CopyFiles@2 14 | displayName: Copy assets 15 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 16 | inputs: 17 | sourceFolder: '$(Build.SourcesDirectory)/target/release' 18 | contents: 'grin-miner' 19 | targetFolder: '$(Build.BinariesDirectory)/grin-miner' 20 | - task: CopyFiles@2 21 | displayName: Copy plugins 22 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 23 | inputs: 24 | sourceFolder: '$(Build.SourcesDirectory)/target/release/plugins' 25 | contents: '*' 26 | targetFolder: '$(Build.BinariesDirectory)/grin-miner/plugins' 27 | - task: CopyFiles@2 28 | displayName: Copy config 29 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 30 | inputs: 31 | sourceFolder: '$(Build.SourcesDirectory)' 32 | contents: 'grin-miner.toml' 33 | targetFolder: '$(Build.BinariesDirectory)/grin-miner' 34 | - task: ArchiveFiles@2 35 | displayName: Gather assets 36 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 37 | inputs: 38 | rootFolderOrFile: '$(Build.BinariesDirectory)/grin-miner' 39 | archiveType: 'tar' 40 | tarCompression: 'gz' 41 | archiveFile: '$(Build.ArtifactStagingDirectory)/grin-miner-$(build.my_tag)-$(build.platform).tar.gz' 42 | - script: | 43 | openssl sha256 $(Build.ArtifactStagingDirectory)/grin-miner-$(build.my_tag)-$(build.platform).tar.gz > $(Build.ArtifactStagingDirectory)/grin-miner-$(build.my_tag)-$(build.platform)-sha256sum.txt 44 | displayName: Create Checksum 45 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 46 | - task: GithubRelease@0 47 | displayName: Github release 48 | condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 49 | inputs: 50 | gitHubConnection: 'ignopeverell' 51 | repositoryName: 'mimblewimble/grin-miner' 52 | action: 'edit' 53 | target: '$(build.sourceVersion)' 54 | tagSource: 'manual' 55 | tag: '$(build.my_tag)' 56 | assets: | 57 | $(Build.ArtifactStagingDirectory)/grin-miner-$(build.my_tag)-$(build.platform).tar.gz 58 | $(Build.ArtifactStagingDirectory)/grin-miner-$(build.my_tag)-$(build.platform)-sha256sum.txt 59 | title: '$(build.my_tag)' 60 | assetUploadMode: 'replace' 61 | addChangeLog: true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | .DS_Store 3 | .grin* 4 | node* 5 | target 6 | *.iml 7 | grin-miner.log 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "cuckoo-miner/src/cuckoo_sys/plugins/cuckoo"] 2 | path = cuckoo-miner/src/cuckoo_sys/plugins/cuckoo 3 | url = https://github.com/tromp/cuckoo.git 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | The Code of Conduct for this repository [can be found online](https://grin.mw/policies/code_of_conduct). 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "grin_miner" 3 | version = "4.0.0" 4 | authors = ["Grin Developers "] 5 | description = "Mining software for Grin, supports CPU and CUDA GPUs." 6 | build = "src/build/build.rs" 7 | license = "Apache-2.0" 8 | repository = "https://github.com/mimblewimble/grin-miner" 9 | keywords = [ "crypto", "grin", "mimblewimble", "mining"] 10 | autobins = false 11 | 12 | [workspace] 13 | members = ["config", "util", "plugin"] 14 | 15 | [features] 16 | default = ["tui"] 17 | opencl = ["ocl_cuckatoo", "ocl_cuckaroo"] 18 | tui = ["cursive"] 19 | 20 | [[bin]] 21 | name = "grin-miner" 22 | path = "src/bin/grin_miner.rs" 23 | 24 | [dependencies] 25 | backtrace = "0.3" 26 | bufstream = "0.1" 27 | native-tls = "0.2" 28 | serde = "1" 29 | serde_derive = "1" 30 | serde_json = "1" 31 | slog = { version = "2", features = ["max_level_trace", "release_max_level_trace"] } 32 | term = "0.6" 33 | time = "0.1" 34 | 35 | grin_miner_util = { path = "./util", version = "4.0.0" } 36 | grin_miner_plugin = { path = "./plugin", version = "4.0.0" } 37 | grin_miner_config = { path = "./config", version = "4.0.0" } 38 | #cuckoo_miner = { path = "./cuckoo-miner", version = "4.0.0" } 39 | #use this alternative inclusion below to build cuda plugins 40 | cuckoo_miner = { path = "./cuckoo-miner", version = "4.0.0", features = ["build-cuda-plugins"]} 41 | ocl_cuckatoo = { path = "./ocl_cuckatoo", version = "1.0.2", optional = true} 42 | ocl_cuckaroo = { path = "./ocl_cuckaroo", version = "1.0.2", optional = true} 43 | 44 | [dependencies.cursive] 45 | version = "0.14" 46 | default-features = false 47 | features = ["pancurses-backend"] 48 | optional = true 49 | 50 | 51 | [build-dependencies] 52 | built = { version= "0.4", features = ["git2","chrono"] } 53 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Multistage docker build, requires docker 17.05 2 | 3 | # builder stage 4 | FROM nvidia/cuda:10.0-devel as builder 5 | 6 | RUN set -ex && \ 7 | apt-get update && \ 8 | apt-get --no-install-recommends --yes install \ 9 | libncurses5-dev \ 10 | libncursesw5-dev \ 11 | cmake \ 12 | git \ 13 | curl \ 14 | libssl-dev \ 15 | pkg-config 16 | 17 | RUN curl https://sh.rustup.rs -sSf | sh -s -- -y 18 | 19 | RUN git clone https://github.com/mimblewimble/grin-miner && cd grin-miner && git submodule update --init 20 | 21 | RUN cd grin-miner && sed -i '/^cuckoo_miner = {/s/^/#/' Cargo.toml && sed -i '/^#.*build-cuda-plugins"]/s/^#//' Cargo.toml 22 | 23 | RUN cd grin-miner && $HOME/.cargo/bin/cargo build --release 24 | 25 | # runtime stage 26 | FROM nvidia/cuda:10.0-base 27 | 28 | RUN set -ex && \ 29 | apt-get update && \ 30 | apt-get --no-install-recommends --yes install \ 31 | libncurses5 \ 32 | libncursesw5 33 | 34 | COPY --from=builder /grin-miner/target/release/grin-miner /grin-miner/target/release/grin-miner 35 | COPY --from=builder /grin-miner/target/release/plugins/* /grin-miner/target/release/plugins/ 36 | COPY --from=builder /grin-miner/grin-miner.toml /grin-miner/grin-miner.toml 37 | 38 | WORKDIR /grin-miner 39 | 40 | RUN sed -i -e 's/run_tui = true/run_tui = false/' grin-miner.toml 41 | 42 | RUN echo '#!/bin/bash\n\ 43 | if [ $# -eq 1 ]\n\ 44 | then\n\ 45 | sed -i -e 's/127.0.0.1/\$1/g' grin-miner.toml\n\ 46 | fi\n\ 47 | ./target/release/grin-miner' > run.sh 48 | 49 | # If the grin server is not at 127.0.0.1 provide the ip or hostname to the container 50 | # by command line (i.e. docker run --name miner1 --rm -i -t miner_image 1.2.3.4) 51 | 52 | ENTRYPOINT ["sh", "run.sh"] 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://dev.azure.com/mimblewimble/grin-miner/_apis/build/status/mimblewimble.grin-miner?branchName=master)](https://dev.azure.com/mimblewimble/grin-miner/_build/latest?definitionId=5&branchName=master) 2 | 3 | # Grin Miner 4 | 5 | A standalone mining implementation intended for mining Grin against a running Grin node. 6 | 7 | ## Supported Platforms 8 | 9 | At present, only mining plugins for linux-x86_64 and MacOS exist. This will likely change over time as the community creates more solvers for different platforms. 10 | 11 | ## Requirements 12 | 13 | - rust 1.30+ (use [rustup]((https://www.rustup.rs/))- i.e. `curl https://sh.rustup.rs -sSf | sh; source $HOME/.cargo/env`) 14 | - cmake 3.2+ (for [Cuckoo mining plugins]((https://github.com/mimblewimble/cuckoo-miner))) 15 | - ncurses and libs (ncurses, ncursesw5) 16 | - zlib libs (zlib1g-dev or zlib-devel) 17 | - linux-headers (reported needed on Alpine linux) 18 | 19 | And a [running Grin node](https://github.com/mimblewimble/grin/blob/master/doc/build.md) to mine into! 20 | 21 | ## Build steps 22 | 23 | ```sh 24 | git clone https://github.com/mimblewimble/grin-miner.git 25 | cd grin-miner 26 | git submodule update --init 27 | cargo build 28 | ``` 29 | 30 | ### Building the Cuckoo-Miner plugins 31 | 32 | Grin-miner automatically builds x86_64 CPU plugins. Cuda plugins are also provided, but are 33 | not enabled by default. To enable them, modify `Cargo.toml` as follows: 34 | 35 | ``` 36 | change: 37 | cuckoo_miner = { path = "./cuckoo-miner" } 38 | to: 39 | cuckoo_miner = { path = "./cuckoo-miner", features = ["build-cuda-plugins"]} 40 | ``` 41 | 42 | The Cuda toolkit 9+ must be installed on your system (check with `nvcc --version`) 43 | 44 | ### Building the OpenCL plugins 45 | OpenCL plugins are not enabled by default. Run `install_ocl_plugins.sh` script to build and install them. 46 | 47 | ``` 48 | ./install_ocl_plugins.sh 49 | ``` 50 | You must install OpenCL libraries for your operating system before. 51 | If you just need to compile them (for development or testing purposes) build grin-miner the following way: 52 | 53 | ``` 54 | cargo build --features opencl 55 | ``` 56 | 57 | ### Build errors 58 | 59 | See [Troubleshooting](https://github.com/mimblewimble/docs/wiki/Troubleshooting) 60 | 61 | ## What was built? 62 | 63 | A successful build gets you: 64 | 65 | - `target/debug/grin-miner` - the main grin-miner binary 66 | - `target/debug/plugins/*` - mining plugins 67 | 68 | Make sure you always run grin-miner within a directory that contains a 69 | `grin-miner.toml` configuration file. 70 | 71 | While testing, put the grin-miner binary on your path like this: 72 | 73 | ``` 74 | export PATH=/path/to/grin-miner/dir/target/debug:$PATH 75 | ``` 76 | 77 | You can then run `grin-miner` directly. 78 | 79 | # Configuration 80 | 81 | Grin-miner can be further configured via the `grin-miner.toml` file. 82 | This file contains contains inline documentation on all configuration 83 | options, and should be the first point of reference. 84 | 85 | You should always ensure that this file exists in the directory from which you're 86 | running grin-miner. 87 | 88 | # Using grin-miner 89 | 90 | There is a [Grin forum post](https://www.grin-forum.org/t/how-to-mine-cuckoo-30-in-grin-help-us-test-and-collect-stats/152) with further detail on how to configure grin-miner and mine grin's testnet. 91 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The Grin Developers 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | trigger: 16 | branches: 17 | include: 18 | - master 19 | - milestone/* 20 | tags: 21 | include: ['*'] 22 | 23 | pr: 24 | branches: 25 | include: ['*'] 26 | 27 | variables: 28 | RUST_BACKTRACE: '1' 29 | RUSTFLAGS: '-C debug-assertions' 30 | 31 | jobs: 32 | - job: linux 33 | pool: 34 | vmImage: ubuntu-latest 35 | strategy: 36 | matrix: 37 | test: 38 | CI_JOB: test 39 | release: 40 | CI_JOB: release 41 | PLATFORM: linux-amd64 42 | steps: 43 | - template: '.ci/install.yml' 44 | - script: cd cuckoo-miner && cargo test --release 45 | displayName: Cargo Test 46 | - template: '.ci/release.yml' 47 | - job: macos 48 | pool: 49 | vmImage: macos-latest 50 | strategy: 51 | matrix: 52 | release: 53 | CI_JOB: release 54 | PLATFORM: macos 55 | steps: 56 | - template: '.ci/install.yml' 57 | - template: '.ci/release.yml' -------------------------------------------------------------------------------- /config/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "grin_miner_config" 3 | version = "4.0.0" 4 | authors = ["Grin Developers "] 5 | description = "Configuration handling for the grin miner" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/mimblewimble/grin-miner" 8 | workspace = ".." 9 | 10 | [dependencies] 11 | cuckoo_miner = { path = "../cuckoo-miner", version = "4.0.0" } 12 | dirs = "2" 13 | grin_miner_util = { path = "../util", version = "4.0.0" } 14 | serde = "1" 15 | serde_derive = "1" 16 | slog = { version = "2", features = ["max_level_trace", "release_max_level_trace"] } 17 | toml = "0.5" 18 | -------------------------------------------------------------------------------- /config/src/config.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Configuration file management 16 | 17 | use std::env; 18 | use std::fs::File; 19 | use std::io::Read; 20 | use std::path::PathBuf; 21 | 22 | use cuckoo::{CuckooMinerError, PluginConfig}; 23 | use toml; 24 | use types::MinerConfig; 25 | use types::{ConfigError, ConfigMembers, GlobalConfig, GrinMinerPluginConfig}; 26 | use util::{LoggingConfig, LOGGER}; 27 | 28 | extern crate dirs; 29 | 30 | /// The default file name to use when trying to derive 31 | /// the config file location 32 | 33 | const CONFIG_FILE_NAME: &str = "grin-miner.toml"; 34 | const GRIN_HOME: &str = ".grin"; 35 | 36 | /// resolve a read parameter to a solver param, (or not if it isn't found) 37 | fn resolve_param(config: &mut PluginConfig, name: &str, value: u32) { 38 | match name { 39 | "nthreads" => config.params.nthreads = value, 40 | "ntrims" => config.params.ntrims = value, 41 | "cpuload" => { 42 | config.params.cpuload = match value { 43 | 1 => true, 44 | _ => false, 45 | } 46 | } 47 | "device" => config.params.device = value, 48 | "blocks" => config.params.blocks = value, 49 | "tbp" => config.params.tpb = value, 50 | "expand" => config.params.expand = value, 51 | "genablocks" => config.params.genablocks = value, 52 | "genatpb" => config.params.genatpb = value, 53 | "genbtpb" => config.params.genbtpb = value, 54 | "trimtpb" => config.params.trimtpb = value, 55 | "tailtpb" => config.params.tailtpb = value, 56 | "recoverblocks" => config.params.recoverblocks = value, 57 | "recovertpb" => config.params.recovertpb = value, 58 | "platform" => config.params.platform = value, 59 | "edge_bits" => config.params.edge_bits = value, 60 | n => { 61 | warn!(LOGGER, "Configuration param: {} unknown. Ignored.", n); 62 | } 63 | }; 64 | } 65 | 66 | /// Transforms a set of grin-miner plugin configs to cuckoo-miner plugins configs 67 | pub fn read_configs( 68 | plugin_dir: Option, 69 | conf_in: Vec, 70 | ) -> Result, CuckooMinerError> { 71 | // Resolve a final plugin path, either config-provided or from the current executable path 72 | let plugin_dir_absolute_path = match plugin_dir { 73 | Some(path) => { 74 | let absolute_path = path.canonicalize().map_err(CuckooMinerError::from); 75 | if let Ok(path) = &absolute_path { 76 | debug!( 77 | LOGGER, 78 | "Using mining plugin dir provided by config: {:?}", path 79 | ); 80 | }; 81 | absolute_path 82 | } 83 | None => { 84 | let absolute_path = 85 | env::current_exe() 86 | .map_err(CuckooMinerError::from) 87 | .map(|mut env_path| { 88 | env_path.pop(); 89 | // cargo test exes are a directory further down 90 | if env_path.ends_with("deps") { 91 | env_path.pop(); 92 | } 93 | env_path.push("plugins"); 94 | env_path 95 | }); 96 | if let Ok(path) = &absolute_path { 97 | debug!( 98 | LOGGER, 99 | "No mining plugin dir provided by config. Using default plugin dir: {:?}", path 100 | ); 101 | }; 102 | absolute_path 103 | } 104 | }?; 105 | 106 | let mut return_vec = vec![]; 107 | for conf in conf_in { 108 | let res = PluginConfig::new(plugin_dir_absolute_path.clone(), &conf.plugin_name); 109 | match res { 110 | Err(e) => { 111 | error!(LOGGER, "Error reading plugin config: {:?}", e); 112 | return Err(e); 113 | } 114 | Ok(mut c) => { 115 | if conf.parameters.is_some() { 116 | let params = conf.parameters.unwrap(); 117 | for k in params.keys() { 118 | resolve_param(&mut c, k, *params.get(k).unwrap()); 119 | } 120 | } 121 | return_vec.push(c) 122 | } 123 | } 124 | } 125 | Ok(return_vec) 126 | } 127 | 128 | /// Returns the defaults, as strewn throughout the code 129 | impl Default for ConfigMembers { 130 | fn default() -> ConfigMembers { 131 | ConfigMembers { 132 | mining: MinerConfig::default(), 133 | logging: Some(LoggingConfig::default()), 134 | } 135 | } 136 | } 137 | 138 | impl Default for GlobalConfig { 139 | fn default() -> GlobalConfig { 140 | GlobalConfig { 141 | config_file_path: None, 142 | using_config_file: false, 143 | members: Some(ConfigMembers::default()), 144 | } 145 | } 146 | } 147 | 148 | impl GlobalConfig { 149 | /// Need to decide on rules where to read the config file from, 150 | /// but will take a stab at logic for now 151 | 152 | fn derive_config_location(&mut self) -> Result<(), ConfigError> { 153 | // First, check working directory 154 | let mut config_path = env::current_dir().unwrap(); 155 | config_path.push(CONFIG_FILE_NAME); 156 | if config_path.exists() { 157 | self.config_file_path = Some(config_path); 158 | return Ok(()); 159 | } 160 | // Next, look in directory of executable 161 | let mut config_path = env::current_exe().unwrap(); 162 | config_path.pop(); 163 | config_path.push(CONFIG_FILE_NAME); 164 | if config_path.exists() { 165 | self.config_file_path = Some(config_path); 166 | return Ok(()); 167 | } 168 | // Then look in {user_home}/.grin 169 | let config_path = dirs::home_dir(); 170 | if let Some(mut p) = config_path { 171 | p.push(GRIN_HOME); 172 | p.push(CONFIG_FILE_NAME); 173 | if p.exists() { 174 | self.config_file_path = Some(p); 175 | return Ok(()); 176 | } 177 | } 178 | 179 | // Give up 180 | Err(ConfigError::FileNotFoundError(String::from(""))) 181 | } 182 | 183 | /// Takes the path to a config file, or if NONE, tries 184 | /// to determine a config file based on rules in 185 | /// derive_config_location 186 | 187 | pub fn new(file_path: Option<&str>) -> Result { 188 | let mut return_value = GlobalConfig::default(); 189 | if let Some(fp) = file_path { 190 | return_value.config_file_path = Some(PathBuf::from(&fp)); 191 | } else { 192 | let _result = return_value.derive_config_location(); 193 | } 194 | 195 | // No attempt at a config file, just return defaults 196 | if return_value.config_file_path.is_none() { 197 | return Ok(return_value); 198 | } 199 | 200 | // Config file path is given but not valid 201 | if !return_value.config_file_path.as_mut().unwrap().exists() { 202 | return Err(ConfigError::FileNotFoundError( 203 | return_value 204 | .config_file_path 205 | .unwrap() 206 | .to_str() 207 | .unwrap() 208 | .to_string(), 209 | )); 210 | } 211 | 212 | // Try to parse the config file if it exists 213 | // explode if it does exist but something's wrong 214 | // with it 215 | return_value.read_config() 216 | } 217 | 218 | /// Read config 219 | pub fn read_config(mut self) -> Result { 220 | let mut file = File::open(self.config_file_path.as_mut().unwrap())?; 221 | let mut contents = String::new(); 222 | file.read_to_string(&mut contents)?; 223 | let decoded: Result = toml::from_str(&contents); 224 | match decoded { 225 | Ok(gc) => { 226 | // Put the struct back together, because the config 227 | // file was flattened a bit 228 | self.using_config_file = true; 229 | self.members = Some(gc); 230 | Ok(self) 231 | } 232 | Err(e) => Err(ConfigError::ParseError( 233 | self.config_file_path.unwrap().to_str().unwrap().to_string(), 234 | format!("{}", e), 235 | )), 236 | } 237 | } 238 | 239 | /// Serialize config 240 | pub fn ser_config(&mut self) -> Result { 241 | let encoded: Result = 242 | toml::to_string(self.members.as_mut().unwrap()); 243 | match encoded { 244 | Ok(enc) => Ok(enc), 245 | Err(e) => Err(ConfigError::SerializationError(format!("{}", e))), 246 | } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /config/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Crate wrapping up mining configuration file 16 | 17 | #![deny(non_upper_case_globals)] 18 | #![deny(non_camel_case_types)] 19 | #![deny(non_snake_case)] 20 | #![deny(unused_mut)] 21 | #![warn(missing_docs)] 22 | 23 | extern crate serde; 24 | #[macro_use] 25 | extern crate serde_derive; 26 | extern crate toml; 27 | 28 | #[macro_use] 29 | extern crate slog; 30 | 31 | extern crate cuckoo_miner as cuckoo; 32 | extern crate grin_miner_util as util; 33 | 34 | mod config; 35 | mod types; 36 | 37 | pub use config::read_configs; 38 | pub use types::{ConfigError, ConfigMembers, GlobalConfig, GrinMinerPluginConfig, MinerConfig}; 39 | -------------------------------------------------------------------------------- /config/src/types.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Public types for config modules 16 | 17 | use std::collections::HashMap; 18 | use std::path::PathBuf; 19 | use std::{fmt, io}; 20 | 21 | use util; 22 | 23 | /// CuckooMinerPlugin configuration 24 | #[derive(Debug, Clone, Serialize, Deserialize)] 25 | pub struct GrinMinerPluginConfig { 26 | /// The type of plugin to load (i.e. filters on filename) 27 | pub plugin_name: String, 28 | 29 | /// 30 | pub parameters: Option>, 31 | } 32 | 33 | impl Default for GrinMinerPluginConfig { 34 | fn default() -> GrinMinerPluginConfig { 35 | GrinMinerPluginConfig { 36 | plugin_name: String::new(), 37 | parameters: None, 38 | } 39 | } 40 | } 41 | 42 | /// Error type wrapping config errors. 43 | #[derive(Debug)] 44 | pub enum ConfigError { 45 | /// Error with parsing of config file 46 | ParseError(String, String), 47 | 48 | /// Error with fileIO while reading config file 49 | FileIOError(String, String), 50 | 51 | /// No file found 52 | FileNotFoundError(String), 53 | 54 | /// Error serializing config values 55 | SerializationError(String), 56 | } 57 | 58 | impl fmt::Display for ConfigError { 59 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 60 | match *self { 61 | ConfigError::ParseError(ref file_name, ref message) => write!( 62 | f, 63 | "Error parsing configuration file at {} - {}", 64 | file_name, message 65 | ), 66 | ConfigError::FileIOError(ref file_name, ref message) => { 67 | write!(f, "{} {}", message, file_name) 68 | } 69 | ConfigError::FileNotFoundError(ref file_name) => { 70 | write!(f, "Configuration file not found: {}", file_name) 71 | } 72 | ConfigError::SerializationError(ref message) => { 73 | write!(f, "Error serializing configuration: {}", message) 74 | } 75 | } 76 | } 77 | } 78 | 79 | impl From for ConfigError { 80 | fn from(error: io::Error) -> ConfigError { 81 | ConfigError::FileIOError( 82 | String::from(""), 83 | format!("Error loading config file: {}", error), 84 | ) 85 | } 86 | } 87 | 88 | /// basic mining configuration 89 | #[derive(Debug, Clone, Serialize, Deserialize)] 90 | pub struct MinerConfig { 91 | /// Whether to run the tui 92 | pub run_tui: bool, 93 | 94 | /// mining loop by adding a sleep to the thread 95 | pub stratum_server_addr: String, 96 | 97 | /// login for the stratum server 98 | pub stratum_server_login: Option, 99 | 100 | /// password for the stratum server 101 | pub stratum_server_password: Option, 102 | 103 | /// whether tls is enabled for the stratum server 104 | pub stratum_server_tls_enabled: Option, 105 | 106 | /// plugin dir 107 | pub miner_plugin_dir: Option, 108 | 109 | /// Cuckoo miner plugin configuration, one for each plugin 110 | pub miner_plugin_config: Vec, 111 | } 112 | 113 | impl Default for MinerConfig { 114 | fn default() -> MinerConfig { 115 | MinerConfig { 116 | run_tui: false, 117 | miner_plugin_dir: None, 118 | miner_plugin_config: vec![], 119 | stratum_server_addr: String::from("http://127.0.0.1:13416"), 120 | stratum_server_login: None, 121 | stratum_server_password: None, 122 | stratum_server_tls_enabled: None, 123 | } 124 | } 125 | } 126 | 127 | /// separately for now, then put them together as a single 128 | /// ServerConfig object afterwards. This is to flatten 129 | /// out the configuration file into logical sections, 130 | /// as they tend to be quite nested in the code 131 | /// Most structs optional, as they may or may not 132 | /// be needed depending on what's being run 133 | #[derive(Debug, Serialize, Deserialize)] 134 | pub struct GlobalConfig { 135 | /// Keep track of the file we've read 136 | pub config_file_path: Option, 137 | /// keep track of whether we're using 138 | /// a config file or just the defaults 139 | /// for each member 140 | pub using_config_file: bool, 141 | /// Global member config 142 | pub members: Option, 143 | } 144 | 145 | /// Keeping an 'inner' structure here, as the top 146 | /// level GlobalConfigContainer options might want to keep 147 | /// internal state that we don't necessarily 148 | /// want serialised or deserialised 149 | #[derive(Debug, Serialize, Deserialize)] 150 | pub struct ConfigMembers { 151 | /// Server config 152 | /// Mining config 153 | pub mining: MinerConfig, 154 | /// Logging config 155 | pub logging: Option, 156 | } 157 | -------------------------------------------------------------------------------- /cuckoo-miner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cuckoo_miner" 3 | version = "4.0.0" 4 | authors = ["yeastplume"] 5 | license = "MIT/Apache-2.0/BSD-3-Clause" 6 | description = "Rust bindings to John Tromp's Cuckoo Cycle Implementations" 7 | repository = "https://github.com/mimblewimble/grin-miner" 8 | readme = "../README.md" 9 | build = "src/build.rs" 10 | 11 | [features] 12 | default = [] 13 | #feature to allow turing off plugin builds 14 | no-plugin-build = [] 15 | #whether to test avx2 CPU plugins 16 | test-avx2 = [] 17 | #feature which defines whether to build cuda libs 18 | build-cuda-plugins = [] 19 | 20 | [dependencies] 21 | byteorder = "1" 22 | blake2-rfc = "0.2" 23 | glob = "0.3" 24 | grin_miner_util = { path = "../util", version = "4.0.0" } 25 | grin_miner_plugin = { path = "../plugin", version = "4.0.0" } 26 | libc = "0.2" 27 | libloading = "0.6" 28 | serde = "1" 29 | serde_derive = "1" 30 | serde_json = "1" 31 | slog = { version = "2", features = ["max_level_trace", "release_max_level_trace"] } 32 | rand = "0.3" 33 | regex = "1.3" 34 | rust-crypto = "0.2" 35 | time = "0.1" 36 | 37 | [dev-dependencies] 38 | const-cstr = "0.3" 39 | 40 | [build-dependencies] 41 | cmake = "0.1" 42 | fs_extra = "1" 43 | -------------------------------------------------------------------------------- /cuckoo-miner/src/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! cmake wrapper for build 16 | 17 | extern crate cmake; 18 | extern crate fs_extra; 19 | 20 | use cmake::Config; 21 | use fs_extra::dir::*; 22 | use std::path::PathBuf; 23 | use std::{env, fs}; 24 | 25 | #[cfg(feature = "build-cuda-plugins")] 26 | const BUILD_CUDA_PLUGINS: &str = "TRUE"; 27 | #[cfg(not(feature = "build-cuda-plugins"))] 28 | const BUILD_CUDA_PLUGINS: &str = "FALSE"; 29 | 30 | /// Tests whether source cuckoo directory exists 31 | 32 | pub fn fail_on_empty_directory(name: &str) { 33 | if fs::read_dir(name).unwrap().count() == 0 { 34 | println!( 35 | "The `{}` directory is empty. Did you forget to pull the submodules?", 36 | name 37 | ); 38 | println!("Try `git submodule update --init --recursive`"); 39 | panic!(); 40 | } 41 | } 42 | 43 | fn main() { 44 | #[cfg(feature = "no-plugin-build")] 45 | return; 46 | fail_on_empty_directory("src/cuckoo_sys/plugins/cuckoo"); 47 | let path_str = env::var("OUT_DIR").unwrap(); 48 | let mut out_path = PathBuf::from(&path_str); 49 | out_path.pop(); 50 | out_path.pop(); 51 | out_path.pop(); 52 | let mut plugin_path = PathBuf::from(&path_str); 53 | plugin_path.push("build"); 54 | plugin_path.push("plugins"); 55 | // Collect the files and directories we care about 56 | let p = PathBuf::from("src/cuckoo_sys/plugins"); 57 | let dir_content = match get_dir_content(p) { 58 | Ok(c) => c, 59 | Err(e) => panic!("Error getting directory content: {}", e), 60 | }; 61 | for d in dir_content.directories { 62 | let file_content = get_dir_content(d).unwrap(); 63 | for f in file_content.files { 64 | println!("cargo:rerun-if-changed={}", f); 65 | } 66 | } 67 | for f in dir_content.files { 68 | println!("cargo:rerun-if-changed={}", f); 69 | } 70 | 71 | let dst = Config::new("src/cuckoo_sys/plugins") 72 | .define("BUILD_CUDA_PLUGINS", BUILD_CUDA_PLUGINS) //whatever flags go here 73 | //.cflag("-foo") //and here 74 | .build_target("") 75 | .build(); 76 | 77 | println!("Plugin path: {:?}", plugin_path); 78 | println!("OUT PATH: {:?}", out_path); 79 | let mut options = CopyOptions::new(); 80 | options.overwrite = true; 81 | if let Err(e) = copy(plugin_path, out_path, &options) { 82 | println!("{:?}", e); 83 | } 84 | 85 | println!("cargo:rustc-link-search=native={}", dst.display()); 86 | } 87 | -------------------------------------------------------------------------------- /cuckoo-miner/src/config/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Configuration types 16 | //! for loading a mining plugin and performing a cuckoo mining call. 17 | 18 | #![deny(non_upper_case_globals)] 19 | #![deny(non_camel_case_types)] 20 | #![deny(non_snake_case)] 21 | #![deny(unused_mut)] 22 | #![warn(missing_docs)] 23 | 24 | pub mod types; 25 | -------------------------------------------------------------------------------- /cuckoo-miner/src/config/types.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Public Types used for cuckoo-miner module 16 | 17 | use plugin::SolverParams; 18 | use std::path::PathBuf; 19 | use std::{fmt, io}; 20 | use {CuckooMinerError, PluginLibrary}; 21 | 22 | pub static SO_SUFFIX: &str = ".cuckooplugin"; 23 | 24 | /// CuckooMinerPlugin configuration 25 | #[derive(Debug, Clone, Serialize, Deserialize)] 26 | pub struct PluginConfig { 27 | /// The display name of the plugin to load 28 | pub name: String, 29 | 30 | /// The path to the file 31 | pub file: String, 32 | 33 | /// device params 34 | pub params: SolverParams, 35 | } 36 | 37 | impl PluginConfig { 38 | /// create new! 39 | pub fn new(mut plugin_dir: PathBuf, name: &str) -> Result { 40 | plugin_dir.push(format!("{}{}", name, SO_SUFFIX).as_str()); 41 | let plugin_file_str = plugin_dir.to_str().ok_or_else(|| { 42 | CuckooMinerError::PluginNotFoundError( 43 | "Invalid plugin path. Paths must be valid unicode".to_owned(), 44 | ) 45 | })?; 46 | 47 | PluginLibrary::new(plugin_file_str).map(|plugin_library| { 48 | let params = plugin_library.get_default_params(); 49 | plugin_library.unload(); 50 | PluginConfig { 51 | name: name.to_owned(), 52 | file: plugin_file_str.to_owned(), 53 | params, 54 | } 55 | }) 56 | } 57 | } 58 | 59 | /// Error type wrapping config errors. 60 | #[derive(Debug)] 61 | #[allow(dead_code)] 62 | pub enum ConfigError { 63 | /// Error with parsing of config file 64 | ParseError(String, String), 65 | 66 | /// Error with fileIO while reading config file 67 | FileIOError(String, String), 68 | 69 | /// No file found 70 | FileNotFoundError(String), 71 | 72 | /// Error serializing config values 73 | SerializationError(String), 74 | } 75 | 76 | impl fmt::Display for ConfigError { 77 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 78 | match *self { 79 | ConfigError::ParseError(ref file_name, ref message) => write!( 80 | f, 81 | "Error parsing configuration file at {} - {}", 82 | file_name, message 83 | ), 84 | ConfigError::FileIOError(ref file_name, ref message) => { 85 | write!(f, "{} {}", message, file_name) 86 | } 87 | ConfigError::FileNotFoundError(ref file_name) => { 88 | write!(f, "Configuration file not found: {}", file_name) 89 | } 90 | ConfigError::SerializationError(ref message) => { 91 | write!(f, "Error serializing configuration: {}", message) 92 | } 93 | } 94 | } 95 | } 96 | 97 | impl From for ConfigError { 98 | fn from(error: io::Error) -> ConfigError { 99 | ConfigError::FileIOError( 100 | String::from(""), 101 | format!("Error loading config file: {}", error), 102 | ) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /cuckoo-miner/src/cuckoo_sys/ffi.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Low-Level manager for loading and unloading plugins. These functions 16 | //! should generally not be called directly by most consumers, who should 17 | //! be using the high level interfaces found in the config, manager, and 18 | //! miner modules. These functions are meant for internal cuckoo-miner crates, 19 | //! and will not be exposed to other projects including the cuckoo-miner crate. 20 | 21 | use plugin::*; 22 | use std::sync::{Arc, Mutex}; 23 | use util::LOGGER; 24 | 25 | use libloading; 26 | 27 | use error::CuckooMinerError; 28 | 29 | /// Struct to hold instances of loaded plugins 30 | 31 | pub struct PluginLibrary { 32 | ///The full file path to the plugin loaded by this instance 33 | pub lib_full_path: String, 34 | 35 | loaded_library: Arc>, 36 | cuckoo_create_solver_ctx: Arc>, 37 | cuckoo_destroy_solver_ctx: Arc>, 38 | cuckoo_run_solver: Arc>, 39 | cuckoo_stop_solver: Arc>, 40 | cuckoo_fill_default_params: Arc>, 41 | } 42 | 43 | impl PluginLibrary { 44 | /// Loads the specified library 45 | 46 | pub fn new(lib_full_path: &str) -> Result { 47 | debug!(LOGGER, "Loading miner plugin: {}", &lib_full_path); 48 | 49 | let result = libloading::Library::new(lib_full_path); 50 | 51 | if let Err(e) = result { 52 | return Err(CuckooMinerError::PluginNotFoundError(format!( 53 | "{} - {:?}", 54 | lib_full_path, e 55 | ))); 56 | } 57 | 58 | let loaded_library = result.unwrap(); 59 | PluginLibrary::load_symbols(loaded_library, lib_full_path) 60 | } 61 | 62 | fn load_symbols( 63 | loaded_library: libloading::Library, 64 | path: &str, 65 | ) -> Result { 66 | unsafe { 67 | let ret_val = PluginLibrary { 68 | lib_full_path: String::from(path), 69 | 70 | cuckoo_create_solver_ctx: { 71 | let cuckoo_create_solver_ctx: libloading::Symbol = 72 | loaded_library.get(b"create_solver_ctx\0").unwrap(); 73 | Arc::new(Mutex::new(*cuckoo_create_solver_ctx.into_raw())) 74 | }, 75 | 76 | cuckoo_destroy_solver_ctx: { 77 | let cuckoo_destroy_solver_ctx: libloading::Symbol = 78 | loaded_library.get(b"destroy_solver_ctx\0").unwrap(); 79 | Arc::new(Mutex::new(*cuckoo_destroy_solver_ctx.into_raw())) 80 | }, 81 | 82 | cuckoo_run_solver: { 83 | let cuckoo_run_solver: libloading::Symbol = 84 | loaded_library.get(b"run_solver\0").unwrap(); 85 | Arc::new(Mutex::new(*cuckoo_run_solver.into_raw())) 86 | }, 87 | 88 | cuckoo_stop_solver: { 89 | let cuckoo_stop_solver: libloading::Symbol = 90 | loaded_library.get(b"stop_solver\0").unwrap(); 91 | Arc::new(Mutex::new(*cuckoo_stop_solver.into_raw())) 92 | }, 93 | 94 | cuckoo_fill_default_params: { 95 | let cuckoo_fill_default_params: libloading::Symbol = 96 | loaded_library.get(b"fill_default_params\0").unwrap(); 97 | Arc::new(Mutex::new(*cuckoo_fill_default_params.into_raw())) 98 | }, 99 | 100 | loaded_library: Arc::new(Mutex::new(loaded_library)), 101 | }; 102 | 103 | Ok(ret_val) 104 | } 105 | } 106 | 107 | /// #Description 108 | /// 109 | /// Unloads the currently loaded plugin and all symbols. 110 | /// 111 | /// #Arguments 112 | /// 113 | /// None 114 | /// 115 | /// #Returns 116 | /// 117 | /// Nothing 118 | /// 119 | 120 | pub fn unload(&self) { 121 | let cuckoo_create_solver_ref = self.cuckoo_create_solver_ctx.lock().unwrap(); 122 | drop(cuckoo_create_solver_ref); 123 | 124 | let cuckoo_destroy_solver_ref = self.cuckoo_destroy_solver_ctx.lock().unwrap(); 125 | drop(cuckoo_destroy_solver_ref); 126 | 127 | let cuckoo_run_solver_ref = self.cuckoo_run_solver.lock().unwrap(); 128 | drop(cuckoo_run_solver_ref); 129 | 130 | let cuckoo_stop_solver_ref = self.cuckoo_stop_solver.lock().unwrap(); 131 | drop(cuckoo_stop_solver_ref); 132 | 133 | let cuckoo_fill_default_params_ref = self.cuckoo_fill_default_params.lock().unwrap(); 134 | drop(cuckoo_fill_default_params_ref); 135 | 136 | let loaded_library_ref = self.loaded_library.lock().unwrap(); 137 | drop(loaded_library_ref); 138 | } 139 | 140 | /// Create a solver context 141 | pub fn create_solver_ctx(&self, params: &mut SolverParams) -> *mut SolverCtx { 142 | let call_ref = self.cuckoo_create_solver_ctx.lock().unwrap(); 143 | unsafe { call_ref(params) } 144 | } 145 | 146 | /// Destroy solver context 147 | pub fn destroy_solver_ctx(&self, ctx: *mut SolverCtx) { 148 | let call_ref = self.cuckoo_destroy_solver_ctx.lock().unwrap(); 149 | unsafe { call_ref(ctx) } 150 | } 151 | 152 | /// Run Solver 153 | pub fn run_solver( 154 | &self, 155 | ctx: *mut SolverCtx, 156 | header: Vec, 157 | nonce: u64, 158 | range: u32, 159 | solutions: &mut SolverSolutions, 160 | stats: &mut SolverStats, 161 | ) -> u32 { 162 | let call_ref = self.cuckoo_run_solver.lock().unwrap(); 163 | unsafe { 164 | call_ref( 165 | ctx, 166 | header.as_ptr(), 167 | header.len() as u32, 168 | nonce, 169 | range, 170 | solutions, 171 | stats, 172 | ) 173 | } 174 | } 175 | 176 | /// Stop solver 177 | pub fn stop_solver(&self, ctx: *mut SolverCtx) { 178 | let call_ref = self.cuckoo_stop_solver.lock().unwrap(); 179 | unsafe { call_ref(ctx) } 180 | } 181 | 182 | /// Get default params 183 | pub fn get_default_params(&self) -> SolverParams { 184 | let mut ret_params = SolverParams::default(); 185 | let call_ref = self.cuckoo_fill_default_params.lock().unwrap(); 186 | unsafe { 187 | call_ref(&mut ret_params); 188 | ret_params 189 | } 190 | } 191 | 192 | /// Get an instance of the stop function, to allow it to run in another thread 193 | pub fn get_stop_solver_instance(&self) -> Arc> { 194 | self.cuckoo_stop_solver.clone() 195 | } 196 | 197 | /// Stop solver from a "detached" instance 198 | pub fn stop_solver_from_instance(inst: Arc>, ctx: *mut SolverCtx) { 199 | let call_ref = inst.lock().unwrap(); 200 | unsafe { call_ref(ctx) } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /cuckoo-miner/src/cuckoo_sys/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![deny(non_upper_case_globals)] 16 | #![deny(non_camel_case_types)] 17 | #![deny(non_snake_case)] 18 | #![deny(unused_mut)] 19 | #![warn(missing_docs)] 20 | 21 | //! Crate containing the low level calls to cuckoo-miner plugins, including 22 | //! functions 23 | //! for loading and unloading plugins, querying what plugins are installed on 24 | //! the system, 25 | //! as well as the actual mining calls to a plugin. This crate should be used 26 | //! by other 27 | //! cuckoo-miner crates, but should not be exposed to external consumers of the 28 | //! crate. 29 | 30 | pub mod ffi; 31 | -------------------------------------------------------------------------------- /cuckoo-miner/src/cuckoo_sys/plugins/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project (CuckooMinerPlugins) 3 | 4 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins) 5 | set (CMAKE_CXX_FLAGS "--std=c++11") 6 | 7 | set (OPT "-O3") 8 | set (DOPT "-DPREFETCH") 9 | 10 | set (FLAGS "-Wno-format -Wno-deprecated-declarations -D_POSIX_C_SOURCE=200112L ${OPT} ${DOPT} -I. ${CPPFLAGS} -pthread") 11 | set (GPP_FLAGS "-march=native -m64 ${FLAGS}") 12 | set (CFLAGS "-Wno-format -fomit-frame-pointer ${OPT}") 13 | set (GCC_FLAGS "-m64 -std=gnu11 ${CFLAGS}") 14 | 15 | set (CUDA_HOST_COMPILER_OVERRIDE $ENV{CUDA_HOST_COMPILER}) 16 | set (SKIP_CUCKATOO_GPU $ENV{SKIP_CUCKATOO_GPU}) 17 | set (SKIP_CUCKAROO_GPU $ENV{SKIP_CUCKAROO_GPU}) 18 | set (SKIP_CUCKAROOD_GPU $ENV{SKIP_CUCKAROOD_GPU}) 19 | set (SKIP_CUCKAROOM_CPU "1") 20 | set (SKIP_CUCKAROOM_GPU $ENV{SKIP_CUCKAROOM_GPU}) 21 | set (SKIP_CUCKAROOZ_CPU "1") 22 | set (SKIP_CUCKAROOZ_GPU $ENV{SKIP_CUCKAROOZ_GPU}) 23 | 24 | #blake2b prerequisite 25 | set (BLAKE_2B "cuckoo/src/crypto/blake2b-ref.c") 26 | 27 | #common to all plugins 28 | set (PLUGIN_BUILD_FLAGS "-DC_CALL_CONVENTION=1 -DSQUASH_OUTPUT=1") 29 | 30 | #build CPU target 31 | function (build_cpu_target sources target props) 32 | add_library(${target} SHARED ${sources}) 33 | set_target_properties(${target} PROPERTIES COMPILE_FLAGS "${GPP_FLAGS} ${props} ${PLUGIN_BUILD_FLAGS}" PREFIX "" SUFFIX ".cuckooplugin") 34 | endfunction() 35 | 36 | function (build_cuda_target sources target props) 37 | if (BUILD_CUDA_PLUGINS) 38 | include("cmake/find_cuda.cmake") 39 | if (CUDA_FOUND) 40 | set (CUDA_PROPAGATE_HOST_FLAGS ON) 41 | cuda_add_library (${target} SHARED ${sources} OPTIONS "${props} ${PLUGIN_BUILD_FLAGS}") 42 | set_target_properties(${target} PROPERTIES PREFIX "" SUFFIX ".cuckooplugin") 43 | endif (CUDA_FOUND) 44 | endif (BUILD_CUDA_PLUGINS) 45 | endfunction() 46 | 47 | ################################################################################## 48 | ### CUCKATOO (Asic Tuned) ######################################################## 49 | ################################################################################## 50 | 51 | ### AT LEAN CPU TARGETS ######################################### 52 | 53 | set (AT_LEAN_CPU_SRC 54 | cuckoo/src/cuckatoo/cuckatoo.h 55 | cuckoo/src/cuckatoo/bitmap.hpp 56 | cuckoo/src/cuckatoo/graph.hpp 57 | cuckoo/src/cuckatoo/compress.hpp 58 | cuckoo/src/threads/barrier.hpp 59 | cuckoo/src/crypto/siphash.hpp 60 | cuckoo/src/cuckatoo/lean.hpp 61 | cuckoo/src/cuckatoo/lean.cpp 62 | ${BLAKE_2B}) 63 | 64 | build_cpu_target("${AT_LEAN_CPU_SRC}" cuckatoo_lean_cpu_compat_19 "-mno-avx2 -DNSIPHASH=4 -DATOMIC -DEDGEBITS=19") 65 | build_cpu_target("${AT_LEAN_CPU_SRC}" cuckatoo_lean_cpu_compat_31 "-mno-avx2 -DNSIPHASH=4 -DATOMIC -DEDGEBITS=31") 66 | build_cpu_target("${AT_LEAN_CPU_SRC}" cuckatoo_lean_cpu_avx2_31 "-mavx2 -DNSIPHASH=8 -DATOMIC -DEDGEBITS=31") 67 | 68 | ### AT MEAN CPU TARGETS ######################################### 69 | 70 | set (AT_MEAN_CPU_SRC 71 | cuckoo/src/cuckatoo/cuckatoo.h 72 | cuckoo/src/cuckatoo/bitmap.hpp 73 | cuckoo/src/cuckatoo/graph.hpp 74 | cuckoo/src/cuckatoo/compress.hpp 75 | cuckoo/src/threads/barrier.hpp 76 | cuckoo/src/crypto/siphash.hpp 77 | cuckoo/src/cuckatoo/mean.hpp 78 | cuckoo/src/cuckatoo/mean.cpp 79 | ${BLAKE_2B}) 80 | 81 | build_cpu_target("${AT_MEAN_CPU_SRC}" cuckatoo_mean_cpu_compat_19 "-mno-avx2 -DXBITS=2 -DNSIPHASH=4 -DSAVEEDGES -DEDGEBITS=19") 82 | build_cpu_target("${AT_MEAN_CPU_SRC}" cuckatoo_mean_cpu_avx2_19 "-mavx2 -DXBITS=2 -DNSIPHASH=8 -DSAVEEDGES -DEDGEBITS=19") 83 | build_cpu_target("${AT_MEAN_CPU_SRC}" cuckatoo_mean_cpu_compat_31 "-mno-avx2 -DXBITS=8 -DNSIPHASH=4 -DEXPANDROUND=8 -DCOMPRESSROUND=22 -DSAVEEDGES -DEDGEBITS=31") 84 | build_cpu_target("${AT_MEAN_CPU_SRC}" cuckatoo_mean_cpu_avx2_31 "-mavx2 -DXBITS=8 -DNSIPHASH=8 -DEXPANDROUND=8 -DCOMPRESSROUND=22 -DSAVEEDGES -DEDGEBITS=31") 85 | 86 | ### AT LEAN CUDA TARGETS ######################################### 87 | 88 | set (AT_LEAN_CUDA_SRC 89 | cuckoo/src/crypto/siphash.cuh 90 | cuckoo/src/cuckatoo/lean.cu 91 | ${BLAKE_2B} ) 92 | 93 | if (NOT SKIP_CUCKATOO_GPU) 94 | build_cuda_target("${AT_LEAN_CUDA_SRC}" cuckatoo_lean_cuda_19 "-DEDGEBITS=19") 95 | build_cuda_target("${AT_LEAN_CUDA_SRC}" cuckatoo_lean_cuda_31 "-DEDGEBITS=31") 96 | endif() 97 | 98 | ### AT MEAN CUDA TARGETS ######################################### 99 | 100 | set (AT_MEAN_CUDA_SRC 101 | cuckoo/src/crypto/siphash.cuh 102 | cuckoo/src/cuckatoo/mean.cu 103 | ${BLAKE_2B} ) 104 | 105 | if (NOT SKIP_CUCKATOO_GPU) 106 | build_cuda_target("${AT_MEAN_CUDA_SRC}" cuckatoo_mean_cuda_gtx_31 "-DNEPS_A=135 -DNEPS_B=88 -DPART_BITS=1 -DFLUSHA=2 -DEDGEBITS=31") 107 | build_cuda_target("${AT_MEAN_CUDA_SRC}" cuckatoo_mean_cuda_rtx_31 "-DNEPS_A=135 -DNEPS_B=88 -DPART_BITS=0 -DEDGEBITS=31") 108 | build_cuda_target("${AT_MEAN_CUDA_SRC}" cuckatoo_mean_cuda_rtx_32 "-DNEPS_A=135 -DNEPS_B=88 -DPART_BITS=1 -DEDGEBITS=32") 109 | endif() 110 | 111 | ################################################################################## 112 | ### CUCKAROO (Asic Resistant) ################################################### 113 | ################################################################################## 114 | 115 | ### AR CPU BUILDING ######################################### 116 | 117 | set (AR_CPU_SRC 118 | cuckoo/src/cuckaroo/cuckaroo.hpp 119 | cuckoo/src/cuckaroo/bitmap.hpp 120 | cuckoo/src/cuckaroo/graph.hpp 121 | cuckoo/src/threads/barrier.hpp 122 | cuckoo/src/crypto/siphash.hpp 123 | cuckoo/src/cuckaroo/mean.hpp 124 | cuckoo/src/cuckaroo/mean.cpp 125 | ${BLAKE_2B}) 126 | 127 | ### AR CPU TARGETS ######################################### 128 | 129 | build_cpu_target("${AR_CPU_SRC}" cuckaroo_cpu_compat_19 "-mno-avx2 -DXBITS=2 -DNSIPHASH=4 -DEDGEBITS=19 -DSAVEEDGES") 130 | build_cpu_target("${AR_CPU_SRC}" cuckaroo_cpu_avx2_19 "-mavx2 -DXBITS=2 -DNSIPHASH=8 -DEDGEBITS=19 -DSAVEEDGES") 131 | build_cpu_target("${AR_CPU_SRC}" cuckaroo_cpu_compat_29 "-mno-avx2 -DNSIPHASH=4 -DEDGEBITS=29 -DSAVEEDGES") 132 | build_cpu_target("${AR_CPU_SRC}" cuckaroo_cpu_avx2_29 "-mavx2 -DNSIPHASH=8 -DEDGEBITS=29 -DSAVEEDGES") 133 | 134 | ### AR CUDA TARGETS ######################################### 135 | 136 | set (AR_CUDA_SRC cuckoo/src/cuckaroo/mean.cu ${BLAKE_2B} ) 137 | 138 | if (NOT SKIP_CUCKAROO_GPU) 139 | build_cuda_target("${AR_CUDA_SRC}" cuckaroo_cuda_19 "-DEPS_A=4 -DEPS_B=3 -DIDXSHIFT=2 -DEDGEBITS=19") 140 | build_cuda_target("${AR_CUDA_SRC}" cuckaroo_cuda_29 "-DEDGEBITS=29") 141 | endif() 142 | 143 | ################################################################################## 144 | ### CUCKAROOD (Asic Resistant) ################################################## 145 | ################################################################################## 146 | 147 | ### AR CPU BUILDING ######################################### 148 | 149 | set (AR2_CPU_SRC 150 | cuckoo/src/cuckarood/cuckarood.hpp 151 | cuckoo/src/cuckarood/bitmap.hpp 152 | cuckoo/src/cuckarood/graph.hpp 153 | cuckoo/src/threads/barrier.hpp 154 | cuckoo/src/crypto/siphash.hpp 155 | cuckoo/src/crypto/siphashxN.h 156 | cuckoo/src/cuckarood/mean.hpp 157 | cuckoo/src/cuckarood/mean.cpp 158 | ${BLAKE_2B}) 159 | 160 | ### AR CPU TARGETS ######################################### 161 | 162 | build_cpu_target("${AR2_CPU_SRC}" cuckarood_cpu_compat_19 "-mno-avx2 -DXBITS=2 -DNSIPHASH=4 -DEDGEBITS=19 -DSAVEEDGES") 163 | build_cpu_target("${AR2_CPU_SRC}" cuckarood_cpu_avx2_19 "-mavx2 -DXBITS=2 -DNSIPHASH=8 -DEDGEBITS=19 -DSAVEEDGES") 164 | build_cpu_target("${AR2_CPU_SRC}" cuckarood_cpu_compat_29 "-mno-avx2 -DNSIPHASH=4 -DEDGEBITS=29 -DSAVEEDGES") 165 | build_cpu_target("${AR2_CPU_SRC}" cuckarood_cpu_avx2_29 "-mavx2 -DNSIPHASH=8 -DEDGEBITS=29 -DSAVEEDGES") 166 | 167 | ### AR CUDA TARGETS ######################################### 168 | 169 | set (AR2_CUDA_SRC cuckoo/src/cuckarood/kernel.cuh cuckoo/src/cuckarood/photon.cu ${BLAKE_2B} ) 170 | 171 | if (NOT SKIP_CUCKAROOD_GPU) 172 | build_cuda_target("${AR2_CUDA_SRC}" cuckarood_cuda_19 "-DXBITS=1 -DEPS_A=4 -DEPS_B=3 -DIDXSHIFT=8 -DEDGEBITS=19") 173 | build_cuda_target("${AR2_CUDA_SRC}" cuckarood_cuda_29 "-DEDGEBITS=29") 174 | endif() 175 | 176 | ################################################################################## 177 | ### CUCKAROOM (Asic Resistant) ################################################## 178 | ################################################################################## 179 | 180 | ### AR CPU BUILDING ######################################### 181 | 182 | set (AR2_CPU_SRC 183 | cuckoo/src/cuckaroom/cuckaroom.hpp 184 | cuckoo/src/cuckaroom/bitmap.hpp 185 | cuckoo/src/cuckaroom/graph.hpp 186 | cuckoo/src/threads/barrier.hpp 187 | cuckoo/src/crypto/siphash.hpp 188 | cuckoo/src/crypto/siphashxN.h 189 | cuckoo/src/cuckaroom/mean.hpp 190 | cuckoo/src/cuckaroom/mean.cpp 191 | ${BLAKE_2B}) 192 | 193 | ### AR CPU TARGETS ######################################### 194 | 195 | if (NOT SKIP_CUCKAROOM_CPU) 196 | build_cpu_target("${AR2_CPU_SRC}" cuckaroom_cpu_compat_19 "-mno-avx2 -DXBITS=2 -DNSIPHASH=4 -DEDGEBITS=19 -DSAVEEDGES") 197 | build_cpu_target("${AR2_CPU_SRC}" cuckaroom_cpu_avx2_19 "-mavx2 -DXBITS=2 -DNSIPHASH=8 -DEDGEBITS=19 -DSAVEEDGES") 198 | build_cpu_target("${AR2_CPU_SRC}" cuckaroom_cpu_compat_29 "-mno-avx2 -DNSIPHASH=4 -DEDGEBITS=29 -DSAVEEDGES") 199 | build_cpu_target("${AR2_CPU_SRC}" cuckaroom_cpu_avx2_29 "-mavx2 -DNSIPHASH=8 -DEDGEBITS=29 -DSAVEEDGES") 200 | endif() 201 | 202 | ### AR CUDA TARGETS ######################################### 203 | 204 | set (AR2_CUDA_SRC cuckoo/src/cuckaroom/meaner.cu ${BLAKE_2B} ) 205 | set (AR2_CUDA_SRC2 cuckoo/src/cuckaroom/kernel.cuh cuckoo/src/cuckaroom/mean.cu ${BLAKE_2B} ) 206 | 207 | if (NOT SKIP_CUCKAROOM_GPU) 208 | build_cuda_target("${AR2_CUDA_SRC}" cuckaroom_cuda_29 "-DEDGEBITS=29") 209 | build_cuda_target("${AR2_CUDA_SRC2}" cuckaroom_old_cuda_29 "-DEDGEBITS=29") 210 | build_cuda_target("${AR2_CUDA_SRC2}" cuckaroom_cuda_19 "-DXBITS=1 -DEPS_A=4 -DEPS_B=3 -DIDXSHIFT=8 -DEDGEBITS=19") 211 | endif() 212 | 213 | ################################################################################## 214 | ### CUCKAROOZ (Asic Resistant) ################################################## 215 | ################################################################################## 216 | 217 | ### AR CPU BUILDING ######################################### 218 | 219 | set (AR2_CPU_SRC 220 | cuckoo/src/cuckarooz/cuckarooz.hpp 221 | cuckoo/src/cuckarooz/bitmap.hpp 222 | cuckoo/src/cuckarooz/graph.hpp 223 | cuckoo/src/threads/barrier.hpp 224 | cuckoo/src/crypto/siphash.hpp 225 | cuckoo/src/crypto/siphashxN.h 226 | cuckoo/src/cuckarooz/mean.hpp 227 | cuckoo/src/cuckarooz/mean.cpp 228 | ${BLAKE_2B}) 229 | 230 | ### AR CPU TARGETS ######################################### 231 | 232 | if (NOT SKIP_CUCKAROOZ_CPU) 233 | build_cpu_target("${AR2_CPU_SRC}" cuckarooz_cpu_compat_19 "-mno-avx2 -DXBITS=2 -DNSIPHASH=4 -DEDGEBITS=19 -DSAVEEDGES") 234 | build_cpu_target("${AR2_CPU_SRC}" cuckarooz_cpu_avx2_19 "-mavx2 -DXBITS=2 -DNSIPHASH=8 -DEDGEBITS=19 -DSAVEEDGES") 235 | build_cpu_target("${AR2_CPU_SRC}" cuckarooz_cpu_compat_29 "-mno-avx2 -DNSIPHASH=4 -DEDGEBITS=29 -DSAVEEDGES") 236 | build_cpu_target("${AR2_CPU_SRC}" cuckarooz_cpu_avx2_29 "-mavx2 -DNSIPHASH=8 -DEDGEBITS=29 -DSAVEEDGES") 237 | endif() 238 | 239 | ### AR CUDA TARGETS ######################################### 240 | 241 | set (AR2_CUDA_SRC cuckoo/src/cuckarooz/mean.cu ${BLAKE_2B} ) 242 | 243 | if (NOT SKIP_CUCKAROOZ_GPU) 244 | build_cuda_target("${AR2_CUDA_SRC}" cuckarooz_cuda_29 "-DEDGEBITS=29") 245 | endif() 246 | -------------------------------------------------------------------------------- /cuckoo-miner/src/cuckoo_sys/plugins/cmake/CudaComputeTargetFlags.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Compute target flags macros by Anatoly Baksheev 3 | # 4 | # Usage in CmakeLists.txt: 5 | # include(CudaComputeTargetFlags.cmake) 6 | # APPEND_TARGET_ARCH_FLAGS() 7 | 8 | #compute flags macros 9 | MACRO(CUDA_COMPUTE_TARGET_FLAGS arch_bin arch_ptx cuda_nvcc_target_flags) 10 | string(REGEX REPLACE "\\." "" ARCH_BIN_WITHOUT_DOTS "${${arch_bin}}") 11 | string(REGEX REPLACE "\\." "" ARCH_PTX_WITHOUT_DOTS "${${arch_ptx}}") 12 | 13 | set(cuda_computer_target_flags_temp "") 14 | 15 | # Tell NVCC to add binaries for the specified GPUs 16 | string(REGEX MATCHALL "[0-9()]+" ARCH_LIST "${ARCH_BIN_WITHOUT_DOTS}") 17 | foreach(ARCH IN LISTS ARCH_LIST) 18 | if (ARCH MATCHES "([0-9]+)\\(([0-9]+)\\)") 19 | # User explicitly specified PTX for the concrete BIN 20 | set(cuda_computer_target_flags_temp ${cuda_computer_target_flags_temp} -gencode arch=compute_${CMAKE_MATCH_2},code=sm_${CMAKE_MATCH_1}) 21 | else() 22 | # User didn't explicitly specify PTX for the concrete BIN, we assume PTX=BIN 23 | set(cuda_computer_target_flags_temp ${cuda_computer_target_flags_temp} -gencode arch=compute_${ARCH},code=sm_${ARCH}) 24 | endif() 25 | endforeach() 26 | 27 | # Tell NVCC to add PTX intermediate code for the specified architectures 28 | string(REGEX MATCHALL "[0-9]+" ARCH_LIST "${ARCH_PTX_WITHOUT_DOTS}") 29 | foreach(ARCH IN LISTS ARCH_LIST) 30 | set(cuda_computer_target_flags_temp ${cuda_computer_target_flags_temp} -gencode arch=compute_${ARCH},code=compute_${ARCH}) 31 | endforeach() 32 | 33 | set(${cuda_nvcc_target_flags} ${cuda_computer_target_flags_temp}) 34 | ENDMACRO() 35 | 36 | MACRO(APPEND_TARGET_ARCH_FLAGS) 37 | set(cuda_nvcc_target_flags "") 38 | CUDA_COMPUTE_TARGET_FLAGS(CUDA_ARCH_BIN CUDA_ARCH_PTX cuda_nvcc_target_flags) 39 | if (cuda_nvcc_target_flags) 40 | message(STATUS "CUDA NVCC target flags: ${cuda_nvcc_target_flags}") 41 | list(APPEND CUDA_NVCC_FLAGS ${cuda_nvcc_target_flags}) 42 | endif() 43 | ENDMACRO() 44 | -------------------------------------------------------------------------------- /cuckoo-miner/src/cuckoo_sys/plugins/cmake/find_cuda.cmake: -------------------------------------------------------------------------------- 1 | #from: https://github.com/PointCloudLibrary/pcl/blob/master/cmake/pcl_find_cuda.cmake 2 | # Find CUDA 3 | if(MSVC) 4 | # Setting this to true brakes Visual Studio builds. 5 | set(CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE OFF CACHE BOOL "CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE") 6 | endif() 7 | 8 | set(CUDA_FIND_QUIETLY TRUE) 9 | find_package(CUDA 4) 10 | 11 | if(CUDA_FOUND) 12 | message(STATUS "Found CUDA Toolkit v${CUDA_VERSION_STRING}") 13 | 14 | # CUDA 9.1 + installs a symlink to its preferred compiler, so use that if it exists 15 | # Otherwise, try to default to /usr/bin/gcc if one hasn't been supplied at the command line 16 | # This will not override an existing cache 17 | # value if the user has passed CUDA_HOST_COMPILER_OVERRIDE on the command line. 18 | if (CUDA_HOST_COMPILER_OVERRIDE) 19 | set (CUDA_HOST_COMPILER ${CUDA_HOST_COMPILER_OVERRIDE}) 20 | elseif (EXISTS /opt/cuda/bin/gcc) 21 | set(CUDA_HOST_COMPILER /opt/cuda/bin/gcc) 22 | elseif (EXISTS /usr/bin/gcc) 23 | set(CUDA_HOST_COMPILER /usr/bin/gcc) 24 | elseif (EXISTS /usr/bin/cc) 25 | set(CUDA_HOST_COMPILER /usr/bin/cc) 26 | endif() 27 | 28 | message(STATUS "Setting CMAKE_HOST_COMPILER to ${CUDA_HOST_COMPILER}.") 29 | 30 | # Send a warning if CUDA_HOST_COMPILER is set to a compiler that is known 31 | # to be unsupported. 32 | if (CUDA_HOST_COMPILER STREQUAL CMAKE_C_COMPILER AND CMAKE_C_COMPILER_ID STREQUAL "Clang") 33 | message(WARNING "CUDA_HOST_COMPILER is set to an unsupported compiler: ${CMAKE_C_COMPILER}.") 34 | endif() 35 | 36 | # CUDA_ARCH_BIN is a space separated list of versions to include in output so-file. So you can set CUDA_ARCH_BIN = 10 11 12 13 20 37 | # Also user can specify virtual arch in parenthesis to limit instructions set, 38 | # for example CUDA_ARCH_BIN = 11(11) 12(11) 13(11) 20(11) 21(11) -> forces using only sm_11 instructions. 39 | # The CMake scripts interpret XX as XX (XX). This allows user to omit parenthesis. 40 | # Arch 21 is an exceptional case since it doesn't have own sm_21 instructions set. 41 | # So 21 = 21(21) is an invalid configuration and user has to explicitly force previous sm_20 instruction set via 21(20). 42 | # CUDA_ARCH_BIN adds support of only listed GPUs. As alternative CMake scripts also parse 'CUDA_ARCH_PTX' variable, 43 | # which is a list of intermediate PTX codes to include in final so-file. The PTX code can/will be JIT compiled for any current or future GPU. 44 | # To add support of older GPU for kinfu, I would embed PTX 11 and 12 into so-file. GPU with sm_13 will run PTX 12 code (no difference for kinfu) 45 | 46 | # Find a complete list for CUDA compute capabilities at http://developer.nvidia.com/cuda-gpus 47 | 48 | if(NOT ${CUDA_VERSION_STRING} VERSION_LESS "10.0") 49 | set(__cuda_arch_bin "3.5 3.7 5.0 5.2 6.0 6.1 7.0 7.2 7.5") 50 | elseif(NOT ${CUDA_VERSION_STRING} VERSION_LESS "9.0") 51 | set(__cuda_arch_bin "3.5 3.7 5.0 5.2 6.0 6.1 7.0") 52 | elseif(NOT ${CUDA_VERSION_STRING} VERSION_LESS "8.0") 53 | set(__cuda_arch_bin "3.5 5.0 5.2 5.3 6.0 6.1") 54 | else() 55 | set(__cuda_arch_bin "3.5") 56 | endif() 57 | 58 | set(CUDA_ARCH_BIN ${__cuda_arch_bin} CACHE STRING "Specify 'real' GPU architectures to build binaries for, BIN(PTX) format is supported") 59 | 60 | set(CUDA_ARCH_PTX "" CACHE STRING "Specify 'virtual' PTX arch to build PTX intermediate code for. Example: 1.0 1.2 or 10 12") 61 | #set(CUDA_ARCH_PTX "1.1 1.2" CACHE STRING "Specify 'virtual' PTX arch to build PTX intermediate code for. Example: 1.0 1.2 or 10 12") 62 | 63 | # Guess this macros will be included in cmake distributive 64 | include(cmake/CudaComputeTargetFlags.cmake) 65 | APPEND_TARGET_ARCH_FLAGS() 66 | 67 | # Prevent compilation issues between recent gcc versions and old CUDA versions 68 | list(APPEND CUDA_NVCC_FLAGS "-D_FORCE_INLINES") 69 | endif() 70 | -------------------------------------------------------------------------------- /cuckoo-miner/src/error/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Common error type used by all cuckoo-miner modules, as well as any exernal 16 | //! consumers of the cuckoo-miner crate. 17 | 18 | use std::io; 19 | use std::string; 20 | 21 | /// #Description 22 | /// 23 | /// Top level enum for all errors that the cuckoo-miner crate can return. 24 | /// 25 | 26 | #[derive(Debug)] 27 | pub enum CuckooMinerError { 28 | /// Occurs when trying to call a plugin function when a 29 | /// mining plugin is not loaded. 30 | PluginNotLoadedError(String), 31 | 32 | /// Occurs when trying to load plugin function that doesn't exist 33 | PluginSymbolNotFoundError(String), 34 | 35 | /// Occurs when attempting to load a plugin that doesn't exist 36 | PluginNotFoundError(String), 37 | 38 | /// Occurs when trying to load a plugin directory that doesn't 39 | /// contain any plugins 40 | NoPluginsFoundError(String), 41 | 42 | /// Unexpected return code from a plugin 43 | UnexpectedResultError(u32), 44 | 45 | /// Error setting a parameter 46 | ParameterError(String), 47 | 48 | /// IO Error 49 | PluginIOError(String), 50 | 51 | /// Plugin processing can't start 52 | PluginProcessingError(String), 53 | 54 | /// Error getting stats or stats not implemented 55 | StatsError(String), 56 | } 57 | 58 | impl From for CuckooMinerError { 59 | fn from(error: io::Error) -> Self { 60 | CuckooMinerError::PluginIOError(format!("Error loading plugin: {}", error)) 61 | } 62 | } 63 | 64 | impl From for CuckooMinerError { 65 | fn from(error: string::FromUtf8Error) -> Self { 66 | CuckooMinerError::PluginIOError(format!("Error loading plugin description: {}", error)) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /cuckoo-miner/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! # Overview 16 | //! 17 | //! cuckoo-miner is a Rust wrapper around John Tromp's Cuckoo Miner 18 | //! C implementations, intended primarily for use in the Grin MimbleWimble 19 | //! blockhain development project. However, it is also suitable for use as 20 | //! a standalone miner or by any other project needing to use the 21 | //! cuckoo cycle proof of work. cuckoo-miner is plugin based, and provides 22 | //! a high level interface to load and work with C mining implementations. 23 | 24 | #![deny(non_upper_case_globals)] 25 | #![deny(non_camel_case_types)] 26 | #![deny(non_snake_case)] 27 | #![deny(unused_mut)] 28 | #![warn(missing_docs)] 29 | 30 | extern crate grin_miner_plugin as plugin; 31 | extern crate grin_miner_util as util; 32 | 33 | extern crate serde; 34 | #[macro_use] 35 | extern crate serde_derive; 36 | extern crate serde_json; 37 | 38 | extern crate blake2_rfc as blake2; 39 | extern crate byteorder; 40 | extern crate crypto; 41 | extern crate rand; 42 | extern crate regex; 43 | 44 | extern crate libc; 45 | extern crate libloading as libloading; 46 | 47 | #[macro_use] 48 | extern crate slog; 49 | 50 | extern crate glob; 51 | 52 | mod config; 53 | mod cuckoo_sys; 54 | mod error; 55 | mod miner; 56 | 57 | pub use config::types::PluginConfig; 58 | pub use cuckoo_sys::ffi::PluginLibrary; 59 | pub use error::CuckooMinerError; 60 | pub use miner::miner::CuckooMiner; 61 | -------------------------------------------------------------------------------- /cuckoo-miner/src/miner/consensus.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Difficulty calculation as from Grin 16 | use blake2::blake2b::Blake2b; 17 | use byteorder::{BigEndian, ByteOrder}; 18 | use std::cmp::{max, min}; 19 | use std::fmt; 20 | 21 | // constants from grin 22 | const PROOF_SIZE: usize = 42; 23 | 24 | /// The difficulty is defined as the maximum target divided by the block hash. 25 | #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)] 26 | pub struct Difficulty { 27 | num: u64, 28 | } 29 | 30 | impl Difficulty { 31 | /// Convert a `u32` into a `Difficulty` 32 | pub fn from_num(num: u64) -> Difficulty { 33 | // can't have difficulty lower than 1 34 | Difficulty { num: max(num, 1) } 35 | } 36 | 37 | /// unscaled proof 38 | fn from_proof_unscaled(proof: &Proof) -> Difficulty { 39 | Difficulty::from_num(proof.scaled_difficulty(1u64)) 40 | } 41 | 42 | /// Converts the difficulty into a u64 43 | pub fn to_num(self) -> u64 { 44 | self.num 45 | } 46 | } 47 | 48 | impl fmt::Display for Difficulty { 49 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 50 | write!(f, "{}", self.num) 51 | } 52 | } 53 | 54 | /// A Cuck(at)oo Cycle proof of work, consisting of the edge_bits to get the graph 55 | /// size (i.e. the 2-log of the number of edges) and the nonces 56 | /// of the graph solution. While being expressed as u64 for simplicity, 57 | /// nonces a.k.a. edge indices range from 0 to (1 << edge_bits) - 1 58 | /// 59 | /// The hash of the `Proof` is the hash of its packed nonces when serializing 60 | /// them at their exact bit size. The resulting bit sequence is padded to be 61 | /// byte-aligned. 62 | 63 | #[derive(Clone, PartialOrd, PartialEq)] 64 | pub struct Proof { 65 | /// Power of 2 used for the size of the cuckoo graph 66 | pub edge_bits: u8, 67 | /// The nonces 68 | pub nonces: Vec, 69 | } 70 | 71 | impl fmt::Debug for Proof { 72 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 73 | write!(f, "Cuckoo{}(", self.edge_bits)?; 74 | for (i, val) in self.nonces[..].iter().enumerate() { 75 | write!(f, "{:x}", val)?; 76 | if i < self.nonces.len() - 1 { 77 | write!(f, " ")?; 78 | } 79 | } 80 | write!(f, ")") 81 | } 82 | } 83 | 84 | impl Eq for Proof {} 85 | 86 | impl Proof { 87 | /// Difficulty achieved by this proof with given scaling factor 88 | fn scaled_difficulty(&self, scale: u64) -> u64 { 89 | let diff = ((scale as u128) << 64) / (max(1, self.hash().to_u64()) as u128); 90 | min(diff, ::max_value() as u128) as u64 91 | } 92 | 93 | /// Hash, as in Grin 94 | fn hash(&self) -> Hash { 95 | let nonce_bits = self.edge_bits as usize; 96 | let mut bitvec = BitVec::new(nonce_bits * PROOF_SIZE); 97 | for (n, nonce) in self.nonces.iter().enumerate() { 98 | for bit in 0..nonce_bits { 99 | if nonce & (1 << bit) != 0 { 100 | bitvec.set_bit_at(n * nonce_bits + (bit as usize)) 101 | } 102 | } 103 | } 104 | let mut blake2b = Blake2b::new(32); 105 | blake2b.update(&bitvec.bits); 106 | let mut ret = [0; 32]; 107 | ret.copy_from_slice(blake2b.finalize().as_bytes()); 108 | Hash(ret) 109 | } 110 | 111 | /// unscaled difficulty 112 | pub fn to_difficulty_unscaled(&self) -> Difficulty { 113 | Difficulty::from_proof_unscaled(&self) 114 | } 115 | } 116 | 117 | struct BitVec { 118 | bits: Vec, 119 | } 120 | 121 | impl BitVec { 122 | /// Number of bytes required to store the provided number of bits 123 | fn bytes_len(bits_len: usize) -> usize { 124 | (bits_len + 7) / 8 125 | } 126 | 127 | fn new(bits_len: usize) -> BitVec { 128 | BitVec { 129 | bits: vec![0; BitVec::bytes_len(bits_len)], 130 | } 131 | } 132 | 133 | fn set_bit_at(&mut self, pos: usize) { 134 | self.bits[pos / 8] |= 1 << (pos % 8) as u8; 135 | } 136 | } 137 | 138 | impl Hash { 139 | /// to u64 140 | pub fn to_u64(&self) -> u64 { 141 | BigEndian::read_u64(&self.0) 142 | } 143 | 144 | /// to hex 145 | pub fn to_hex(&self) -> String { 146 | util::to_hex(self.0.to_vec()) 147 | } 148 | } 149 | 150 | /// A hash to uniquely (or close enough) identify one of the main blockchain 151 | /// constructs. Used pervasively for blocks, transactions and outputs. 152 | #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] 153 | pub struct Hash([u8; 32]); 154 | 155 | impl fmt::Debug for Hash { 156 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 157 | let hash_hex = self.to_hex(); 158 | const NUM_SHOW: usize = 12; 159 | 160 | write!(f, "{}", &hash_hex[..NUM_SHOW]) 161 | } 162 | } 163 | 164 | impl fmt::Display for Hash { 165 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 166 | fmt::Debug::fmt(self, f) 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /cuckoo-miner/src/miner/miner.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Main interface for callers into cuckoo-miner. Provides functionality 16 | //! to load a mining plugin, send it a Cuckoo Cycle POW problem, and 17 | //! return any resulting solutions. 18 | 19 | use std::ptr::NonNull; 20 | use std::sync::{mpsc, Arc, RwLock}; 21 | use std::{thread, time}; 22 | use util::LOGGER; 23 | 24 | use config::types::PluginConfig; 25 | use miner::types::{JobSharedData, JobSharedDataType, SolverInstance}; 26 | 27 | use miner::consensus::Proof; 28 | use miner::util; 29 | use plugin::{Solution, SolverCtxWrapper, SolverSolutions, SolverStats}; 30 | use {CuckooMinerError, PluginLibrary}; 31 | 32 | /// Miner control Messages 33 | #[derive(Debug)] 34 | enum ControlMessage { 35 | /// Stop everything, pull down, exis 36 | Stop, 37 | /// Stop current mining iteration, set solver threads to paused 38 | Pause, 39 | /// Resume 40 | Resume, 41 | /// Solver reporting stopped 42 | SolverStopped(usize), 43 | } 44 | 45 | /// An instance of a miner, which loads a cuckoo-miner plugin 46 | /// and calls its mine function according to the provided configuration 47 | 48 | pub struct CuckooMiner { 49 | /// Configurations 50 | configs: Vec, 51 | 52 | /// Data shared across threads 53 | pub shared_data: Arc>, 54 | 55 | /// Job control tx 56 | control_txs: Vec>, 57 | 58 | /// solver loop tx 59 | solver_loop_txs: Vec>, 60 | 61 | /// Solver has stopped and cleanly shutdown 62 | solver_stopped_rxs: Vec>, 63 | } 64 | 65 | impl CuckooMiner { 66 | /// Creates a new instance of a CuckooMiner with the given configuration. 67 | /// One PluginConfig per device 68 | 69 | pub fn new(configs: Vec) -> CuckooMiner { 70 | let len = configs.len(); 71 | CuckooMiner { 72 | configs, 73 | shared_data: Arc::new(RwLock::new(JobSharedData::new(len))), 74 | control_txs: vec![], 75 | solver_loop_txs: vec![], 76 | solver_stopped_rxs: vec![], 77 | } 78 | } 79 | 80 | /// Solver's instance of a thread 81 | fn solver_thread( 82 | mut solver: SolverInstance, 83 | instance: usize, 84 | shared_data: JobSharedDataType, 85 | control_rx: mpsc::Receiver, 86 | solver_loop_rx: mpsc::Receiver, 87 | solver_stopped_tx: mpsc::Sender, 88 | ) { 89 | { 90 | let mut s = shared_data.write().unwrap(); 91 | s.stats[instance].set_plugin_name(&solver.config.name); 92 | } 93 | // "Detach" a stop function from the solver, to let us keep a control thread going 94 | let ctx = solver.lib.create_solver_ctx(&mut solver.config.params); 95 | let control_ctx = SolverCtxWrapper(NonNull::new(ctx).unwrap()); 96 | 97 | let stop_fn = solver.lib.get_stop_solver_instance(); 98 | 99 | // monitor whether to send a stop signal to the solver, which should 100 | // end the current solve attempt below 101 | let stop_handle = thread::spawn(move || loop { 102 | let ctx_ptr = control_ctx.0.as_ptr(); 103 | while let Some(message) = control_rx.iter().next() { 104 | match message { 105 | ControlMessage::Stop => { 106 | PluginLibrary::stop_solver_from_instance(stop_fn.clone(), ctx_ptr); 107 | return; 108 | } 109 | ControlMessage::Pause => { 110 | PluginLibrary::stop_solver_from_instance(stop_fn.clone(), ctx_ptr); 111 | } 112 | _ => {} 113 | }; 114 | } 115 | }); 116 | 117 | let mut iter_count = 0; 118 | let mut paused = true; 119 | loop { 120 | if let Some(message) = solver_loop_rx.try_iter().next() { 121 | debug!( 122 | LOGGER, 123 | "solver_thread - solver_loop_rx got msg: {:?}", message 124 | ); 125 | match message { 126 | ControlMessage::Stop => break, 127 | ControlMessage::Pause => paused = true, 128 | ControlMessage::Resume => paused = false, 129 | _ => {} 130 | } 131 | } 132 | if paused { 133 | thread::sleep(time::Duration::from_micros(100)); 134 | continue; 135 | } 136 | { 137 | let mut s = shared_data.write().unwrap(); 138 | s.stats[instance].set_plugin_name(&solver.config.name); 139 | } 140 | let header_pre = { shared_data.read().unwrap().pre_nonce.clone() }; 141 | let header_post = { shared_data.read().unwrap().post_nonce.clone() }; 142 | let height = { shared_data.read().unwrap().height }; 143 | let job_id = { shared_data.read().unwrap().job_id }; 144 | let target_difficulty = { shared_data.read().unwrap().difficulty }; 145 | let header = util::get_next_header_data(&header_pre, &header_post); 146 | let nonce = header.0; 147 | //let sec_scaling = header.2; 148 | solver.lib.run_solver( 149 | ctx, 150 | header.1, 151 | 0, 152 | 1, 153 | &mut solver.solutions, 154 | &mut solver.stats, 155 | ); 156 | iter_count += 1; 157 | let still_valid = { height == shared_data.read().unwrap().height }; 158 | if still_valid { 159 | let mut s = shared_data.write().unwrap(); 160 | s.stats[instance] = solver.stats.clone(); 161 | s.stats[instance].iterations = iter_count; 162 | if solver.solutions.num_sols > 0 { 163 | // Filter solutions that don't meet difficulty check 164 | let mut filtered_sols: Vec = vec![]; 165 | for i in 0..solver.solutions.num_sols { 166 | filtered_sols.push(solver.solutions.sols[i as usize]); 167 | } 168 | let mut filtered_sols: Vec = filtered_sols 169 | .iter() 170 | .filter(|s| { 171 | let proof = Proof { 172 | edge_bits: solver.solutions.edge_bits as u8, 173 | nonces: s.proof.to_vec(), 174 | }; 175 | proof.to_difficulty_unscaled().to_num() >= target_difficulty 176 | }) 177 | .cloned() 178 | .collect(); 179 | for mut ss in filtered_sols.iter_mut() { 180 | ss.nonce = nonce; 181 | ss.id = job_id as u64; 182 | } 183 | solver.solutions.num_sols = filtered_sols.len() as u32; 184 | for (i, _) in filtered_sols 185 | .iter() 186 | .enumerate() 187 | .take(solver.solutions.num_sols as usize) 188 | { 189 | solver.solutions.sols[i] = filtered_sols[i]; 190 | } 191 | s.solutions.push(solver.solutions.clone()); 192 | } 193 | if s.stats[instance].has_errored { 194 | s.stats[instance].set_plugin_name(&solver.config.name); 195 | error!( 196 | LOGGER, 197 | "Plugin {} has errored, device: {}. Reason: {}", 198 | s.stats[instance].get_plugin_name(), 199 | s.stats[instance].get_device_name(), 200 | s.stats[instance].get_error_reason(), 201 | ); 202 | break; 203 | } 204 | } 205 | solver.solutions = SolverSolutions::default(); 206 | thread::sleep(time::Duration::from_micros(100)); 207 | } 208 | 209 | let _ = stop_handle.join(); 210 | solver.lib.destroy_solver_ctx(ctx); 211 | solver.unload(); 212 | let _ = solver_stopped_tx.send(ControlMessage::SolverStopped(instance)); 213 | } 214 | 215 | /// Starts solvers, ready for jobs via job control 216 | pub fn start_solvers(&mut self) -> Result<(), CuckooMinerError> { 217 | let mut solvers = Vec::new(); 218 | for c in self.configs.clone() { 219 | solvers.push(SolverInstance::new(c)?); 220 | } 221 | let mut i = 0; 222 | for s in solvers { 223 | let sd = self.shared_data.clone(); 224 | let (control_tx, control_rx) = mpsc::channel::(); 225 | let (solver_tx, solver_rx) = mpsc::channel::(); 226 | let (solver_stopped_tx, solver_stopped_rx) = mpsc::channel::(); 227 | self.control_txs.push(control_tx); 228 | self.solver_loop_txs.push(solver_tx); 229 | self.solver_stopped_rxs.push(solver_stopped_rx); 230 | thread::spawn(move || { 231 | CuckooMiner::solver_thread(s, i, sd, control_rx, solver_rx, solver_stopped_tx); 232 | }); 233 | i += 1; 234 | } 235 | Ok(()) 236 | } 237 | 238 | /// An asynchronous -esque version of the plugin miner, which takes 239 | /// parts of the header and the target difficulty as input, and begins 240 | /// asyncronous processing to find a solution. The loaded plugin is 241 | /// responsible 242 | /// for how it wishes to manage processing or distribute the load. Once 243 | /// called 244 | /// this function will continue to find solutions over the target difficulty 245 | /// for the given inputs and place them into its output queue until 246 | /// instructed to stop. 247 | 248 | pub fn notify( 249 | &mut self, 250 | job_id: u32, // Job id 251 | height: u64, // Job height 252 | pre_nonce: &str, // Pre-nonce portion of header 253 | post_nonce: &str, // Post-nonce portion of header 254 | difficulty: u64, /* The target difficulty, only sols greater than this difficulty will 255 | * be returned. */ 256 | ) -> Result<(), CuckooMinerError> { 257 | let mut sd = self.shared_data.write().unwrap(); 258 | let paused = if height != sd.height { 259 | // stop/pause any existing jobs if job is for a new 260 | // height 261 | self.pause_solvers(); 262 | true 263 | } else { 264 | false 265 | }; 266 | 267 | sd.job_id = job_id; 268 | sd.height = height; 269 | sd.pre_nonce = pre_nonce.to_owned(); 270 | sd.post_nonce = post_nonce.to_owned(); 271 | sd.difficulty = difficulty; 272 | if paused { 273 | self.resume_solvers(); 274 | } 275 | Ok(()) 276 | } 277 | 278 | /// Returns solutions if currently waiting. 279 | 280 | pub fn get_solutions(&self) -> Option { 281 | // just to prevent endless needless locking of this 282 | // when using fast test miners, in real cuckoo30 terms 283 | // this shouldn't be an issue 284 | // TODO: Make this less blocky 285 | // let time_pre_lock=Instant::now(); 286 | { 287 | let mut s = self.shared_data.write().unwrap(); 288 | // let time_elapsed=Instant::now()-time_pre_lock; 289 | // println!("Get_solution Time spent waiting for lock: {}", 290 | // time_elapsed.as_secs()*1000 +(time_elapsed.subsec_nanos()/1_000_000)as u64); 291 | if !s.solutions.is_empty() { 292 | let sol = s.solutions.pop().unwrap(); 293 | return Some(sol); 294 | } 295 | } 296 | None 297 | } 298 | 299 | /// get stats for all running solvers 300 | pub fn get_stats(&self) -> Result, CuckooMinerError> { 301 | let s = self.shared_data.read().unwrap(); 302 | Ok(s.stats.clone()) 303 | } 304 | 305 | /// #Description 306 | /// 307 | /// Stops the current job, and signals for the loaded plugin to stop 308 | /// processing and perform any cleanup it needs to do. 309 | /// 310 | /// #Returns 311 | /// 312 | /// Nothing 313 | 314 | pub fn stop_solvers(&self) { 315 | for t in self.control_txs.iter() { 316 | let _ = t.send(ControlMessage::Stop); 317 | } 318 | for t in self.solver_loop_txs.iter() { 319 | let _ = t.send(ControlMessage::Stop); 320 | } 321 | debug!(LOGGER, "Stop message sent"); 322 | } 323 | 324 | /// Tells current solvers to stop and wait 325 | pub fn pause_solvers(&self) { 326 | for t in self.control_txs.iter() { 327 | let _ = t.send(ControlMessage::Pause); 328 | } 329 | for t in self.solver_loop_txs.iter() { 330 | let _ = t.send(ControlMessage::Pause); 331 | } 332 | debug!(LOGGER, "Pause message sent"); 333 | } 334 | 335 | /// Tells current solvers to stop and wait 336 | pub fn resume_solvers(&self) { 337 | for t in self.control_txs.iter() { 338 | let _ = t.send(ControlMessage::Resume); 339 | } 340 | for t in self.solver_loop_txs.iter() { 341 | let _ = t.send(ControlMessage::Resume); 342 | } 343 | debug!(LOGGER, "Resume message sent"); 344 | } 345 | 346 | /// block until solvers have all exited 347 | pub fn wait_for_solver_shutdown(&self) { 348 | for r in self.solver_stopped_rxs.iter() { 349 | while let Some(message) = r.iter().next() { 350 | if let ControlMessage::SolverStopped(i) = message { 351 | debug!(LOGGER, "Solver stopped: {}", i); 352 | break; 353 | } 354 | } 355 | } 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /cuckoo-miner/src/miner/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! The main miner module of cuckoo-miner, which provides an interface 16 | //! for loading a mining plugin and performing a cuckoo mining call. 17 | 18 | #![deny(non_upper_case_globals)] 19 | #![deny(non_camel_case_types)] 20 | #![deny(non_snake_case)] 21 | #![deny(unused_mut)] 22 | #![warn(missing_docs)] 23 | 24 | pub mod consensus; 25 | pub mod miner; 26 | pub mod types; 27 | pub mod util; 28 | -------------------------------------------------------------------------------- /cuckoo-miner/src/miner/types.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Miner types 16 | use std::sync::{Arc, RwLock}; 17 | 18 | use error::CuckooMinerError; 19 | use plugin::{SolverSolutions, SolverStats}; 20 | use {PluginConfig, PluginLibrary}; 21 | 22 | pub type JobSharedDataType = Arc>; 23 | 24 | /// Holds a loaded lib + config + stats 25 | /// 1 instance = 1 device on 1 controlling thread 26 | pub struct SolverInstance { 27 | /// The loaded plugin 28 | pub lib: PluginLibrary, 29 | /// Associated config 30 | pub config: PluginConfig, 31 | /// Last stats output 32 | pub stats: SolverStats, 33 | /// Last solution output 34 | pub solutions: SolverSolutions, 35 | } 36 | 37 | impl SolverInstance { 38 | /// Create a new solver instance with the given config 39 | pub fn new(config: PluginConfig) -> Result { 40 | let l = PluginLibrary::new(&config.file)?; 41 | Ok(SolverInstance { 42 | lib: l, 43 | config, 44 | stats: SolverStats::default(), 45 | solutions: SolverSolutions::default(), 46 | }) 47 | } 48 | 49 | /// Release the lib 50 | pub fn unload(&mut self) { 51 | self.lib.unload(); 52 | } 53 | } 54 | 55 | /// Data intended to be shared across threads 56 | pub struct JobSharedData { 57 | /// ID of the current running job (not currently used) 58 | pub job_id: u32, 59 | 60 | /// block height of current running job 61 | pub height: u64, 62 | 63 | /// The part of the header before the nonce, which this 64 | /// module will mutate in search of a solution 65 | pub pre_nonce: String, 66 | 67 | /// The part of the header after the nonce 68 | pub post_nonce: String, 69 | 70 | /// The target difficulty. Only solutions >= this 71 | /// target will be put into the output queue 72 | pub difficulty: u64, 73 | 74 | /// Output solutions 75 | pub solutions: Vec, 76 | 77 | /// Current stats 78 | pub stats: Vec, 79 | } 80 | 81 | impl Default for JobSharedData { 82 | fn default() -> JobSharedData { 83 | JobSharedData { 84 | job_id: 0, 85 | height: 0, 86 | pre_nonce: String::from(""), 87 | post_nonce: String::from(""), 88 | difficulty: 0, 89 | solutions: Vec::new(), 90 | stats: vec![], 91 | } 92 | } 93 | } 94 | 95 | impl JobSharedData { 96 | pub fn new(num_solvers: usize) -> JobSharedData { 97 | JobSharedData { 98 | job_id: 0, 99 | height: 0, 100 | pre_nonce: String::from(""), 101 | post_nonce: String::from(""), 102 | difficulty: 1, 103 | solutions: Vec::new(), 104 | stats: vec![SolverStats::default(); num_solvers], 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /cuckoo-miner/src/miner/util.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! header manipulation utility functions 16 | 17 | use byteorder::{BigEndian, ByteOrder}; 18 | use rand::{self, Rng}; 19 | 20 | pub fn header_data(pre_nonce: &str, post_nonce: &str, nonce: u64) -> (Vec, u32) { 21 | // Turn input strings into vectors 22 | let mut pre_vec = from_hex_string(pre_nonce); 23 | let mut post_vec = from_hex_string(post_nonce); 24 | 25 | let sec_scaling_bytes = &pre_vec.clone()[pre_vec.len() - 4..pre_vec.len()]; 26 | let sec_scaling = BigEndian::read_u32(&sec_scaling_bytes); 27 | 28 | let mut nonce_bytes = [0; 8]; 29 | BigEndian::write_u64(&mut nonce_bytes, nonce); 30 | let mut nonce_vec = nonce_bytes.to_vec(); 31 | 32 | // Generate new header 33 | pre_vec.append(&mut nonce_vec); 34 | pre_vec.append(&mut post_vec); 35 | 36 | (pre_vec, sec_scaling) 37 | } 38 | 39 | pub fn get_next_header_data(pre_nonce: &str, post_nonce: &str) -> (u64, Vec, u32) { 40 | let nonce: u64 = rand::OsRng::new().unwrap().gen(); 41 | let (hd, sec_scaling) = header_data(pre_nonce, post_nonce, nonce); 42 | (nonce, hd, sec_scaling) 43 | } 44 | 45 | /// Helper to convert a hex string 46 | pub fn from_hex_string(in_str: &str) -> Vec { 47 | let mut bytes = Vec::new(); 48 | for i in 0..(in_str.len() / 2) { 49 | let res = u8::from_str_radix(&in_str[2 * i..2 * i + 2], 16); 50 | match res { 51 | Ok(v) => bytes.push(v), 52 | Err(e) => println!("Problem with hex: {}", e), 53 | } 54 | } 55 | bytes 56 | } 57 | -------------------------------------------------------------------------------- /etc/crate-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # check we're in the grin root 4 | if [ ! -f "LICENSE" ] ; then 5 | echo "Script must be run from Grin-miner's root directory" 6 | exit 1 7 | fi 8 | 9 | echo "Going to package and publish each crate, if you're not logged in crates.io (missing ~/.cargo/credentials, this will fail." 10 | 11 | read -p "Continue? " -n 1 -r 12 | if [[ ! $REPLY =~ ^[Yy]$ ]] 13 | then 14 | printf "\nbye\n" 15 | exit 1 16 | fi 17 | 18 | echo 19 | crates=( config cuckoo-miner plugin util ) 20 | 21 | for crate in "${crates[@]}" 22 | do 23 | echo "** Publishing $crate" 24 | cd $crate 25 | cargo package 26 | cargo publish 27 | cd .. 28 | done 29 | 30 | cargo package 31 | cargo publish 32 | 33 | echo "Done." 34 | -------------------------------------------------------------------------------- /grin-miner.toml: -------------------------------------------------------------------------------- 1 | # Sample Server Configuration File for Grin-Miner 2 | # 3 | # Grin-Miner will look for this file in these places: in the following 4 | # order: 5 | # 6 | # -The working directory 7 | # -The directory in which the executable resides 8 | 9 | ######################################### 10 | ### LOGGING CONFIGURATION ### 11 | ######################################### 12 | 13 | [logging] 14 | 15 | # Whether to log to stdout 16 | log_to_stdout = true 17 | 18 | # Log level for stdout: Critical, Error, Warning, Info, Debug, Trace 19 | stdout_log_level = "Info" 20 | 21 | # Whether to log to a file 22 | log_to_file = true 23 | 24 | # Log level for file: Critical, Error, Warning, Info, Debug, Trace 25 | file_log_level = "Debug" 26 | 27 | # Log file path 28 | log_file_path = "grin-miner.log" 29 | 30 | # Whether to append to the log file (true), or replace it on every run (false) 31 | log_file_append = true 32 | 33 | ######################################### 34 | ### MINING CLIENT CONFIGURATION ### 35 | ######################################### 36 | 37 | [mining] 38 | 39 | # whether to run the tui 40 | run_tui = true 41 | 42 | # listening grin stratum server url 43 | stratum_server_addr = "127.0.0.1:3416" 44 | 45 | # login for the stratum server (if required) 46 | #stratum_server_login = "http://192.168.1.100:3415" 47 | 48 | # password for the stratum server (if required) 49 | #stratum_server_password = "x" 50 | 51 | # whether tls is enabled for the stratum server 52 | stratum_server_tls_enabled = false 53 | 54 | #The directory in which mining plugins are installed 55 | #if not specified, grin miner will look in the directory /deps relative 56 | #to the executable 57 | 58 | #miner_plugin_dir = "target/debug/plugins" 59 | 60 | ################################################################ 61 | ### CUCKAROO* (i.e. GPU-Friendly) MINER PLUGIN CONFIGURATION ### 62 | ################################################################ 63 | 64 | # Multiple plugins can be specified, (e.g. a cpu 65 | # miner and a gpu miner running in parallel) 66 | # Use a single plugin instance per device, as 67 | # demonstrated below. 68 | 69 | # Multiple instances of the same plugin can be loaded 70 | # and used with different devices. On CPU plugins 71 | # you'll likely only be using a single instance 72 | # but in CUDA plugins the device number can be set 73 | # corresponding to the device ID. (use nvidia-smi to find this) 74 | 75 | ### CUCKAROO* CPU SOLVERS (Asic Resist, or GPU-Friendly) 76 | 77 | # cpu mean algorithm for processors supporting sse2 78 | 79 | #[[mining.miner_plugin_config]] 80 | #plugin_name = "cuckarood_cpu_compat_29" 81 | #[mining.miner_plugin_config.parameters] 82 | #nthreads = 4 83 | 84 | # As above, but for processors supporting avx2 85 | 86 | #[[mining.miner_plugin_config]] 87 | #plugin_name = "cuckarood_cpu_avx2_29" 88 | #[mining.miner_plugin_config.parameters] 89 | #nthreads = 4 90 | 91 | # CUCKAROO* CUDA SOLVER 92 | # 93 | # CUDA plugins are not built by default. To build: 94 | #1) Ensure the latest cuda toolkit is installed 95 | # (nvcc should be in your PATH) 96 | # Wrong gcc? install gcc-5 g++-5; export CC=`which gcc-5`; # then build 97 | #2) Ensure the 'build-cuda-plugin' feature is included in Cargo.toml, e.g: 98 | # cuckoo_miner = { path = "./cuckoo-miner", features = ["build-cuda-plugins"]} 99 | # 100 | # Parameters can be set individually for each device by using multiple 101 | # instance of each plugin. device 0 is used by default 102 | # 103 | 104 | # currently requires 6GB GPU memory 105 | [[mining.miner_plugin_config]] 106 | plugin_name = "cuckarooz_cuda_29" 107 | [mining.miner_plugin_config.parameters] 108 | device = 0 109 | #cpuload = 1 110 | #ntrims = 15 111 | #genablocks = 1024 112 | #recoverblocks = 2048 113 | #recovertpb = 256 114 | 115 | # e.g. To enable multiple devices (copy params from above as needed) 116 | 117 | #[[mining.miner_plugin_config]] 118 | #plugin_name = "cuckarooz_cuda_29" 119 | #[mining.miner_plugin_config.parameters] 120 | #device = 1 121 | 122 | 123 | # mean OpenCL supports both NVidia and AMD 124 | # to install run ./install_ocl_plugins.sh script 125 | #[[mining.miner_plugin_config]] 126 | #plugin_name = "ocl_cuckarood" 127 | #[mining.miner_plugin_config.parameters] 128 | # 0 for default, 1 for AMD, 2 for NVidia, specify if you have 129 | # cards from both vendors 130 | #platform = 0 131 | # ID withing the platform 132 | #device = 0 133 | 134 | ############################################################### 135 | ### CUCKATOO (i.e. ASIC-Friendly) MINER PLUGIN CONFIGURATION ## 136 | ############################################################### 137 | 138 | #mean cpu 139 | #[[mining.miner_plugin_config]] 140 | #plugin_name = "cuckatoo_mean_cpu_compat_31" 141 | #[mining.miner_plugin_config.parameters] 142 | #nthreads = 4 143 | 144 | #mean cpu avx2 145 | #[[mining.miner_plugin_config]] 146 | #plugin_name = "cuckatoo_mean_cpu_avx2_31" 147 | #[mining.miner_plugin_config.parameters] 148 | 149 | #mean cuda, will work on a 1080TI with expand rounds set to 2 150 | #memory requirements are tight, don't drive a display 151 | #off the same card while trying to mine with an 11GB card 152 | 153 | #[[mining.miner_plugin_config]] 154 | #plugin_name = "cuckatoo_mean_cuda_gtx_31" 155 | #[mining.miner_plugin_config.parameters] 156 | #device = 0 157 | #cpuload = 1 158 | #ntrims = 31 159 | #genablocks = 1024 160 | #recoverblocks = 2048 161 | #recovertpb = 256 162 | 163 | #mean cuda optimised to use slightly less memory, 164 | #will work on a 2080TI with expand rounds set to 2 165 | #as above, memory requirements are tight, don't drive a display 166 | #off the same card while trying to mine with an 11GB card 167 | 168 | #[[mining.miner_plugin_config]] 169 | #plugin_name = "cuckatoo_mean_cuda_rtx_31" 170 | #[mining.miner_plugin_config.parameters] 171 | #device = 0 172 | #cpuload = 1 173 | #ntrims = 31 174 | #genablocks = 1024 175 | #recoverblocks = 2048 176 | #recovertpb = 256 177 | 178 | #the C32 reference miner requires 20GB of memory 179 | #runs on the RTX Titan with 24GB 180 | 181 | #[[mining.miner_plugin_config]] 182 | #plugin_name = "cuckatoo_mean_cuda_rtx_32" 183 | #[mining.miner_plugin_config.parameters] 184 | #device = 0 185 | #cpuload = 1 186 | #ntrims = 31 187 | #genablocks = 1024 188 | #recoverblocks = 2048 189 | #recovertpb = 256 190 | 191 | #lean cuda 192 | #[[mining.miner_plugin_config]] 193 | #plugin_name = "cuckatoo_lean_cuda_31" 194 | #[mining.miner_plugin_config.parameters] 195 | #device = 0 196 | #cpuload = 1 197 | #ntrims = 176 198 | 199 | # lean OpenCL supports both NVidia and AMD 200 | # very slow but requires ~ 3GB of RAM 201 | # to install run ./install_ocl_plugins.sh script 202 | #[[mining.miner_plugin_config]] 203 | #plugin_name = "ocl_cuckatoo" 204 | #[mining.miner_plugin_config.parameters] 205 | # 0 for default, 1 for AMD, 2 for NVidia, specify if you have 206 | # cards from both vendors 207 | #platform = 0 208 | # ID withing the platform 209 | #device = 0 210 | #edge_bits = 31 211 | 212 | -------------------------------------------------------------------------------- /install_ocl_plugins.sh: -------------------------------------------------------------------------------- 1 | plugins_dir=$(egrep '^miner_plugin_dir' grin-miner.toml | awk '{ print $NF }' | xargs echo) 2 | if [ -z "$plugins_dir" ]; then 3 | plugins_dir="target/release/plugins" 4 | fi 5 | mkdir -p "$plugins_dir"; 6 | 7 | # Install ocl_cuckatoo 8 | cd ocl_cuckatoo 9 | cargo build --release 10 | cd .. 11 | if [ "$(uname)" = "Darwin" ]; then 12 | cp target/release/libocl_cuckatoo.dylib $plugins_dir/ocl_cuckatoo.cuckooplugin 13 | else 14 | cp target/release/libocl_cuckatoo.so $plugins_dir/ocl_cuckatoo.cuckooplugin 15 | fi 16 | 17 | # Install ocl_cuckaroo 18 | cd ocl_cuckaroo 19 | cargo build --release 20 | cd .. 21 | if [ "$(uname)" = "Darwin" ]; then 22 | cp target/release/libocl_cuckaroo.dylib $plugins_dir/ocl_cuckaroo.cuckooplugin 23 | else 24 | cp target/release/libocl_cuckaroo.so $plugins_dir/ocl_cuckaroo.cuckooplugin 25 | fi 26 | -------------------------------------------------------------------------------- /ocl_cuckaroo/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /ocl_cuckaroo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ocl_cuckaroo" 3 | version = "1.0.2" 4 | workspace = ".." 5 | edition = "2018" 6 | 7 | [features] 8 | profile = [] 9 | 10 | 11 | [dependencies] 12 | blake2-rfc = "0.2" 13 | byteorder = "1" 14 | grin_miner_plugin = { path = "../plugin", version = "4.0.0" } 15 | libc = "0.2" 16 | hashbrown = "0.7" 17 | ocl = "0.19" 18 | 19 | [lib] 20 | name = "ocl_cuckaroo" 21 | crate-type = ["cdylib", "rlib"] 22 | -------------------------------------------------------------------------------- /ocl_cuckaroo/src/finder.rs: -------------------------------------------------------------------------------- 1 | use hashbrown::HashMap; 2 | 3 | #[derive(Clone)] 4 | pub struct Solution { 5 | pub nodes: Vec, 6 | } 7 | 8 | pub struct Graph { 9 | adj_index: HashMap, 10 | adj_store: Vec, 11 | } 12 | 13 | struct Search { 14 | path: Vec, 15 | solutions: Vec, 16 | 17 | state: HashMap, 18 | node_visited: usize, 19 | } 20 | 21 | #[derive(Clone, Copy)] 22 | enum NodeState { 23 | NotVisited, 24 | Visited, 25 | } 26 | 27 | impl Search { 28 | fn new(node_count: usize) -> Search { 29 | Search { 30 | path: Vec::with_capacity(node_count), 31 | solutions: vec![], 32 | state: HashMap::with_capacity_and_hasher(node_count, Default::default()), 33 | node_visited: 0, 34 | } 35 | } 36 | 37 | #[inline] 38 | fn visit(&mut self, node: u32) { 39 | self.state.insert(node, NodeState::Visited); 40 | self.path.push(node); 41 | self.node_visited += 1; 42 | } 43 | 44 | #[inline] 45 | fn leave(&mut self, node: u32) { 46 | self.path.pop(); 47 | self.state.insert(node, NodeState::NotVisited); 48 | } 49 | 50 | #[inline] 51 | fn state(&self, node: u32) -> NodeState { 52 | match self.state.get(&node) { 53 | None => NodeState::NotVisited, 54 | Some(state) => *state, 55 | } 56 | } 57 | 58 | #[inline] 59 | fn is_visited(&self, node: u32) -> bool { 60 | match self.state(node) { 61 | NodeState::NotVisited => false, 62 | _ => true, 63 | } 64 | } 65 | } 66 | 67 | struct AdjNode { 68 | value: u32, 69 | next: Option, 70 | } 71 | 72 | impl AdjNode { 73 | #[inline] 74 | fn first(value: u32) -> AdjNode { 75 | AdjNode { value, next: None } 76 | } 77 | 78 | #[inline] 79 | fn next(value: u32, next: usize) -> AdjNode { 80 | AdjNode { 81 | value, 82 | next: Some(next), 83 | } 84 | } 85 | } 86 | 87 | struct AdjList<'a> { 88 | current: Option<&'a AdjNode>, 89 | adj_store: &'a Vec, 90 | } 91 | 92 | impl<'a> AdjList<'a> { 93 | #[inline] 94 | pub fn new(current: Option<&'a AdjNode>, adj_store: &'a Vec) -> AdjList<'a> { 95 | AdjList { current, adj_store } 96 | } 97 | } 98 | 99 | impl<'a> Iterator for AdjList<'a> { 100 | type Item = u32; 101 | 102 | fn next(&mut self) -> Option { 103 | match self.current { 104 | None => None, 105 | Some(node) => { 106 | let val = node.value; 107 | match node.next { 108 | None => self.current = None, 109 | Some(next_index) => self.current = Some(&self.adj_store[next_index]), 110 | } 111 | Some(val) 112 | } 113 | } 114 | } 115 | } 116 | 117 | impl Graph { 118 | pub fn search(nodes: &[u32]) -> Result, String> { 119 | let edge_count = nodes.len() / 2; 120 | let mut g = Graph { 121 | adj_index: HashMap::with_capacity_and_hasher(nodes.len(), Default::default()), 122 | adj_store: Vec::with_capacity(nodes.len()), 123 | }; 124 | let mut search = Search::new(nodes.len()); 125 | const STEP: usize = 2; 126 | for i in 0..edge_count { 127 | let n1 = nodes[i * STEP]; 128 | let n2 = nodes[i * STEP + 1]; 129 | g.walk_graph(n1, n2, &mut search)?; 130 | g.add_edge(n1, n2); 131 | } 132 | 133 | Ok(search.solutions.clone()) 134 | } 135 | 136 | #[inline] 137 | pub fn node_count(&self) -> usize { 138 | self.adj_index.len() 139 | } 140 | 141 | #[inline] 142 | pub fn edge_count(&self) -> usize { 143 | self.adj_store.len() / 2 144 | } 145 | 146 | #[inline] 147 | fn add_edge(&mut self, node1: u32, node2: u32) { 148 | self.add_half_edge(node1, node2); 149 | self.add_half_edge(node2, node1); 150 | } 151 | 152 | fn add_half_edge(&mut self, from: u32, to: u32) { 153 | if let Some(index) = self.adj_index.get(&from) { 154 | self.adj_store.push(AdjNode::next(to, *index)); 155 | } else { 156 | self.adj_store.push(AdjNode::first(to)); 157 | } 158 | self.adj_index.insert(from, self.adj_store.len() - 1); 159 | } 160 | 161 | fn neighbors(&self, node: u32) -> Option + '_> { 162 | let node = match self.adj_index.get(&node) { 163 | Some(index) => Some(&self.adj_store[*index]), 164 | None => return None, 165 | }; 166 | Some(AdjList::new(node, &self.adj_store)) 167 | } 168 | 169 | fn walk_graph(&self, current: u32, target: u32, search: &mut Search) -> Result<(), String> { 170 | if search.path.len() > 41 { 171 | return Ok(()); 172 | } 173 | 174 | let neighbors = match self.neighbors(current) { 175 | None => return Ok(()), 176 | Some(it) => it, 177 | }; 178 | search.visit(current); 179 | for ns in neighbors { 180 | if ns == target && search.path.len() == 41 { 181 | search.path.push(ns); 182 | search.solutions.push(Solution { 183 | nodes: search.path.clone(), 184 | }); 185 | search.leave(current); 186 | return Ok(()); 187 | } 188 | if !search.is_visited(ns) { 189 | self.walk_graph(ns, target, search)?; 190 | } 191 | } 192 | search.leave(current); 193 | Ok(()) 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /ocl_cuckaroo/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate blake2_rfc; 2 | extern crate byteorder; 3 | extern crate grin_miner_plugin as plugin; 4 | extern crate hashbrown; 5 | extern crate libc; 6 | extern crate ocl; 7 | 8 | use blake2_rfc::blake2b::blake2b; 9 | use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; 10 | use libc::*; 11 | use plugin::*; 12 | use std::io::Cursor; 13 | use std::io::Error; 14 | use std::mem; 15 | use std::ptr; 16 | use std::time::{Duration, SystemTime}; 17 | 18 | pub use self::finder::Graph; 19 | pub use self::trimmer::Trimmer; 20 | 21 | mod finder; 22 | mod trimmer; 23 | 24 | #[repr(C)] 25 | struct Solver { 26 | trimmer: Trimmer, 27 | graph: Option, 28 | mutate_nonce: bool, 29 | } 30 | 31 | #[no_mangle] 32 | pub unsafe extern "C" fn create_solver_ctx(params: *mut SolverParams) -> *mut SolverCtx { 33 | let platform = match (*params).platform { 34 | 1 => Some("AMD"), 35 | 2 => Some("NVIDIA"), 36 | _ => None, 37 | }; 38 | let device_id = Some((*params).device as usize); 39 | 40 | let trimmer = Trimmer::build(platform, device_id).expect("can't build trimmer"); 41 | let solver = Solver { 42 | trimmer: trimmer, 43 | graph: None, 44 | mutate_nonce: (*params).mutate_nonce, 45 | }; 46 | let solver_box = Box::new(solver); 47 | let solver_ref = Box::leak(solver_box); 48 | mem::transmute::<&mut Solver, *mut SolverCtx>(solver_ref) 49 | } 50 | 51 | #[no_mangle] 52 | pub unsafe extern "C" fn destroy_solver_ctx(solver_ctx_ptr: *mut SolverCtx) { 53 | // create box to clear memory 54 | let solver_ptr = mem::transmute::<*mut SolverCtx, *mut Solver>(solver_ctx_ptr); 55 | let _solver_box = Box::from_raw(solver_ptr); 56 | } 57 | 58 | #[no_mangle] 59 | pub unsafe extern "C" fn stop_solver(_solver_ctx_ptr: *mut SolverCtx) {} 60 | 61 | #[no_mangle] 62 | pub unsafe extern "C" fn fill_default_params(params: *mut SolverParams) { 63 | (*params).device = 0; 64 | (*params).platform = 0; 65 | (*params).edge_bits = 29; 66 | } 67 | 68 | #[no_mangle] 69 | pub unsafe extern "C" fn run_solver( 70 | ctx: *mut SolverCtx, 71 | header_ptr: *const c_uchar, 72 | header_length: u32, 73 | nonce: u64, 74 | _range: u32, 75 | solutions: *mut SolverSolutions, 76 | stats: *mut SolverStats, 77 | ) -> u32 { 78 | let start = SystemTime::now(); 79 | let solver_ptr = mem::transmute::<*mut SolverCtx, *mut Solver>(ctx); 80 | let solver = &*solver_ptr; 81 | let mut header = Vec::with_capacity(header_length as usize + 32); 82 | let r_ptr = header.as_mut_ptr(); 83 | ptr::copy_nonoverlapping(header_ptr, r_ptr, header_length as usize); 84 | header.set_len(header_length as usize); 85 | let n = nonce as u64; 86 | let k = match set_header_nonce(&header, Some(n), solver.mutate_nonce) { 87 | Err(_e) => { 88 | return 2; 89 | } 90 | Ok(v) => v, 91 | }; 92 | let res = solver.trimmer.run(&k).unwrap(); 93 | 94 | let sols = Graph::search(&res).unwrap(); 95 | let mut i = 0; 96 | (*solutions).edge_bits = 29; 97 | for sol in sols { 98 | let (nonces_cand, valid) = solver.trimmer.recover(sol.nodes, &k).unwrap(); 99 | if valid { 100 | let nonces = nonces_cand 101 | .into_iter() 102 | .map(|v| v as u64) 103 | .collect::>(); 104 | (*solutions).sols[i].nonce = nonce; 105 | (*solutions).sols[i].proof.copy_from_slice(&nonces[..]); 106 | i += 1; 107 | } 108 | } 109 | (*solutions).num_sols = i as u32; 110 | let end = SystemTime::now(); 111 | let elapsed = end.duration_since(start).unwrap(); 112 | (*stats).edge_bits = 29; 113 | (*stats).device_id = solver.trimmer.device_id as u32; 114 | let name_bytes = solver.trimmer.device_name.as_bytes(); 115 | let n = std::cmp::min((*stats).device_name.len(), name_bytes.len()); 116 | (*stats).device_name[..n].copy_from_slice(&solver.trimmer.device_name.as_bytes()[..n]); 117 | (*stats).last_solution_time = duration_to_u64(elapsed); 118 | (*stats).last_start_time = 119 | duration_to_u64(start.duration_since(SystemTime::UNIX_EPOCH).unwrap()); 120 | (*stats).last_end_time = duration_to_u64(end.duration_since(SystemTime::UNIX_EPOCH).unwrap()); 121 | 0 122 | } 123 | 124 | fn duration_to_u64(elapsed: Duration) -> u64 { 125 | elapsed.as_secs() * 1_000_000_000 + elapsed.subsec_nanos() as u64 126 | } 127 | 128 | pub fn set_header_nonce( 129 | header: &[u8], 130 | nonce: Option, 131 | mutate_nonce: bool, 132 | ) -> Result<[u64; 4], Error> { 133 | if let Some(n) = nonce { 134 | let len = header.len(); 135 | let mut header = header.to_owned(); 136 | if mutate_nonce { 137 | header.truncate(len - 4); 138 | header.write_u32::(n as u32)?; 139 | } 140 | create_siphash_keys(&header) 141 | } else { 142 | create_siphash_keys(&header) 143 | } 144 | } 145 | 146 | pub fn create_siphash_keys(header: &[u8]) -> Result<[u64; 4], Error> { 147 | let h = blake2b(32, &[], &header); 148 | let hb = h.as_bytes(); 149 | let mut rdr = Cursor::new(hb); 150 | Ok([ 151 | rdr.read_u64::()?, 152 | rdr.read_u64::()?, 153 | rdr.read_u64::()?, 154 | rdr.read_u64::()?, 155 | ]) 156 | } 157 | 158 | #[cfg(test)] 159 | mod tests { 160 | use super::*; 161 | #[ignore] 162 | // results in Error executing function: clEnqueueNDRangeKernel("LeanRound") 163 | // Status error code: CL_INVALID_WORK_GROUP_SIZE (-54) 164 | // on MacOSX 165 | #[test] 166 | fn test_solve() { 167 | let trimmer = Trimmer::build(None, None).expect("can't build trimmer"); 168 | let k = [ 169 | 0x27580576fe290177, 170 | 0xf9ea9b2031f4e76e, 171 | 0x1663308c8607868f, 172 | 0xb88839b0fa180d0e, 173 | ]; 174 | 175 | unsafe { 176 | let res = trimmer.run(&k).unwrap(); 177 | println!("Trimmed to {}", res.len()); 178 | 179 | let sols = Graph::search(&res).unwrap(); 180 | assert_eq!(1, sols.len()); 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /ocl_cuckaroo/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate ocl_cuckaroo; 2 | 3 | use ocl_cuckaroo::{Graph, Trimmer}; 4 | use std::time::SystemTime; 5 | 6 | fn main() -> Result<(), String> { 7 | let trimmer = Trimmer::build(None, None).expect("can't build trimmer"); 8 | let k = [ 9 | 0xf4956dc403730b01, 10 | 0xe6d45de39c2a5a3e, 11 | 0xcbf626a8afee35f6, 12 | 0x4307b94b1a0c9980, 13 | ]; 14 | 15 | unsafe { 16 | let mut start = SystemTime::now(); 17 | let res = trimmer.run(&k).unwrap(); 18 | let mut end = SystemTime::now(); 19 | let elapsed = end.duration_since(start).unwrap(); 20 | println!("Time: {:?}", elapsed); 21 | println!("Trimmed to {}", res.len()); 22 | 23 | start = SystemTime::now(); 24 | let sols = Graph::search(&res).unwrap(); 25 | end = SystemTime::now(); 26 | let elapsed = end.duration_since(start).unwrap(); 27 | println!("Finder: {:?}", elapsed); 28 | for sol in sols { 29 | println!("Solution: {:x?}", sol.nodes); 30 | start = SystemTime::now(); 31 | let (nonces_c, valid) = trimmer.recover(sol.nodes.clone(), &k).unwrap(); 32 | if valid { 33 | let nonces = nonces_c.into_iter().map(|v| v as u64).collect::>(); 34 | let end = SystemTime::now(); 35 | let elapsed = end.duration_since(start).unwrap(); 36 | println!("Recovering: {:?}", elapsed); 37 | println!("Nonces: {:?}", nonces); 38 | } else { 39 | println!("Not valid"); 40 | } 41 | } 42 | } 43 | Ok(()) 44 | } 45 | -------------------------------------------------------------------------------- /ocl_cuckatoo/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /ocl_cuckatoo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ocl_cuckatoo" 3 | version = "1.0.2" 4 | workspace = ".." 5 | 6 | [dependencies] 7 | blake2-rfc = "0.2" 8 | byteorder = "1" 9 | grin_miner_plugin = { path = "../plugin", version = "4.0.0" } 10 | libc = "0.2" 11 | ocl = "0.19" 12 | hashbrown = "0.7" 13 | 14 | [lib] 15 | name = "ocl_cuckatoo" 16 | crate-type = ["cdylib", "rlib"] 17 | -------------------------------------------------------------------------------- /ocl_cuckatoo/src/finder.rs: -------------------------------------------------------------------------------- 1 | use hashbrown::HashMap; 2 | 3 | #[derive(Clone)] 4 | pub struct Solution { 5 | pub nonces: Vec, 6 | } 7 | 8 | pub struct Graph { 9 | adj_index: HashMap, 10 | adj_store: Vec, 11 | nonces: HashMap<(u32, u32), u32>, 12 | } 13 | 14 | struct Search { 15 | length: usize, 16 | path: Vec, 17 | solutions: Vec, 18 | 19 | state: HashMap, 20 | node_visited: usize, 21 | node_explored: usize, 22 | } 23 | 24 | #[derive(Clone, Copy)] 25 | enum NodeState { 26 | NotVisited, 27 | Visited, 28 | Explored, 29 | } 30 | 31 | impl Search { 32 | fn new(node_count: usize, length: usize) -> Search { 33 | Search { 34 | path: Vec::with_capacity(node_count), 35 | solutions: vec![], 36 | length: length * 2, 37 | state: HashMap::with_capacity_and_hasher(node_count, Default::default()), 38 | node_visited: 0, 39 | node_explored: 0, 40 | } 41 | } 42 | 43 | #[inline] 44 | fn visit(&mut self, node: u32) { 45 | self.state.insert(node, NodeState::Visited); 46 | self.path.push(node); 47 | self.node_visited += 1; 48 | } 49 | 50 | #[inline] 51 | fn explore(&mut self, node: u32) { 52 | self.state.insert(node, NodeState::Explored); 53 | self.path.push(node); 54 | self.node_explored += 1; 55 | } 56 | 57 | #[inline] 58 | fn leave(&mut self, node: u32) { 59 | self.path.pop(); 60 | self.state.insert(node, NodeState::NotVisited); 61 | } 62 | 63 | #[inline] 64 | fn state(&self, node: u32) -> NodeState { 65 | match self.state.get(&node) { 66 | None => NodeState::NotVisited, 67 | Some(state) => *state, 68 | } 69 | } 70 | 71 | #[inline] 72 | fn is_visited(&self, node: u32) -> bool { 73 | match self.state(node) { 74 | NodeState::NotVisited => false, 75 | _ => true, 76 | } 77 | } 78 | 79 | #[inline] 80 | fn is_explored(&self, node: u32) -> bool { 81 | match self.state(node) { 82 | NodeState::Explored => true, 83 | _ => false, 84 | } 85 | } 86 | 87 | fn is_cycle(&mut self, node: u32, is_first: bool) -> bool { 88 | let res = 89 | self.path.len() > self.length - 1 && self.path[self.path.len() - self.length] == node; 90 | if res && !is_first { 91 | self.path.push(node); 92 | } 93 | res 94 | } 95 | } 96 | 97 | struct AdjNode { 98 | value: u32, 99 | next: Option, 100 | } 101 | 102 | impl AdjNode { 103 | #[inline] 104 | fn first(value: u32) -> AdjNode { 105 | AdjNode { value, next: None } 106 | } 107 | 108 | #[inline] 109 | fn next(value: u32, next: usize) -> AdjNode { 110 | AdjNode { 111 | value, 112 | next: Some(next), 113 | } 114 | } 115 | } 116 | 117 | struct AdjList<'a> { 118 | current: Option<&'a AdjNode>, 119 | adj_store: &'a Vec, 120 | } 121 | 122 | impl<'a> AdjList<'a> { 123 | #[inline] 124 | pub fn new(current: Option<&'a AdjNode>, adj_store: &'a Vec) -> AdjList<'a> { 125 | AdjList { current, adj_store } 126 | } 127 | } 128 | 129 | impl<'a> Iterator for AdjList<'a> { 130 | type Item = u32; 131 | 132 | fn next(&mut self) -> Option { 133 | match self.current { 134 | None => None, 135 | Some(node) => { 136 | let val = node.value; 137 | match node.next { 138 | None => self.current = None, 139 | Some(next_index) => self.current = Some(&self.adj_store[next_index]), 140 | } 141 | Some(val) 142 | } 143 | } 144 | } 145 | } 146 | 147 | fn nonce_key(node1: u32, node2: u32) -> (u32, u32) { 148 | if node1 < node2 { 149 | (node1, node2) 150 | } else { 151 | (node2, node1) 152 | } 153 | } 154 | 155 | impl Graph { 156 | pub fn search(edges: &[u32]) -> Result, String> { 157 | let edge_count = edges[1] as usize; 158 | let mut g = Graph { 159 | adj_index: HashMap::with_capacity_and_hasher(edge_count * 2, Default::default()), 160 | nonces: HashMap::with_capacity_and_hasher(edge_count, Default::default()), 161 | adj_store: Vec::with_capacity(edge_count * 2), 162 | }; 163 | let mut search = Search::new(edge_count * 2, 42); 164 | const STEP: usize = 4; 165 | for i in 1..=edge_count { 166 | let n1 = edges[i * STEP]; 167 | let n2 = edges[i * STEP + 1]; 168 | let nonce = edges[i * STEP + 2]; 169 | g.add_edge(n1, n2); 170 | g.nonces.insert(nonce_key(n1, n2), nonce); 171 | g.check_pair(n1, n2, &mut search)?; 172 | } 173 | 174 | // for i in 1..=edge_count { 175 | // let n1 = edges[i * STEP]; 176 | // let n2 = edges[i * STEP + 1]; 177 | // } 178 | Ok(search.solutions.clone()) 179 | } 180 | 181 | fn get_nonce(&self, node1: u32, node2: u32) -> Result { 182 | match self.nonces.get(&nonce_key(node1, node2)) { 183 | None => Err(format!("can not find a nonce for {}:{}", node1, node2)), 184 | Some(v) => Ok(*v as u64), 185 | } 186 | } 187 | 188 | #[inline] 189 | pub fn node_count(&self) -> usize { 190 | self.adj_index.len() 191 | } 192 | 193 | #[inline] 194 | pub fn edge_count(&self) -> usize { 195 | self.adj_store.len() / 2 196 | } 197 | 198 | #[inline] 199 | fn add_edge(&mut self, node1: u32, node2: u32) { 200 | self.add_half_edge(node1, node2); 201 | self.add_half_edge(node2, node1); 202 | } 203 | 204 | fn add_half_edge(&mut self, from: u32, to: u32) { 205 | if let Some(index) = self.adj_index.get(&from) { 206 | self.adj_store.push(AdjNode::next(to, *index)); 207 | } else { 208 | self.adj_store.push(AdjNode::first(to)); 209 | } 210 | self.adj_index.insert(from, self.adj_store.len() - 1); 211 | } 212 | 213 | fn neighbors(&self, node: u32) -> Option + '_> { 214 | let node = match self.adj_index.get(&node) { 215 | Some(index) => Some(&self.adj_store[*index]), 216 | None => return None, 217 | }; 218 | Some(AdjList::new(node, &self.adj_store)) 219 | } 220 | 221 | fn check_pair(&self, u: u32, _v: u32, search: &mut Search) -> Result<(), String> { 222 | self.walk_graph(u, search) 223 | //self.walk_graph(v, search) 224 | } 225 | 226 | fn add_solution(&self, s: &mut Search) -> Result<(), String> { 227 | let res: Result, _> = s.path[s.path.len() - s.length..] 228 | .chunks(2) 229 | .map(|pair| match pair { 230 | &[n1, n2] => self.get_nonce(n1, n2), 231 | _ => Err("not an edge".to_string()), 232 | }) 233 | .collect(); 234 | let mut nonces = match res { 235 | Ok(v) => v, 236 | Err(e) => { 237 | return Err(format!("Failed to get nonce {:?}", e)); 238 | } 239 | }; 240 | nonces.sort(); 241 | let sol = Solution { nonces }; 242 | s.solutions.push(sol); 243 | Ok(()) 244 | } 245 | 246 | fn walk_graph(&self, current: u32, search: &mut Search) -> Result<(), String> { 247 | if search.is_explored(current) || search.path.len() > 84 { 248 | if search.is_cycle(current, true) { 249 | self.add_solution(search)?; 250 | } 251 | return Ok(()); 252 | } 253 | 254 | let neighbors = match self.neighbors(current) { 255 | None => return Ok(()), 256 | Some(it) => it, 257 | }; 258 | search.explore(current); 259 | for ns in neighbors { 260 | if !search.is_visited(ns) { 261 | search.visit(ns); 262 | self.walk_graph(ns ^ 1, search)?; 263 | search.leave(ns); 264 | } else { 265 | if search.is_cycle(ns, false) { 266 | self.add_solution(search)?; 267 | } 268 | } 269 | } 270 | search.leave(current); 271 | Ok(()) 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /ocl_cuckatoo/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate blake2_rfc; 2 | extern crate byteorder; 3 | extern crate grin_miner_plugin as plugin; 4 | extern crate hashbrown; 5 | extern crate libc; 6 | extern crate ocl; 7 | 8 | use blake2_rfc::blake2b::blake2b; 9 | use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; 10 | use libc::*; 11 | use plugin::*; 12 | use std::io::Cursor; 13 | use std::io::Error; 14 | use std::mem; 15 | use std::ptr; 16 | use std::time::{Duration, SystemTime}; 17 | 18 | pub use self::finder::Graph; 19 | pub use self::trimmer::Trimmer; 20 | 21 | mod finder; 22 | mod trimmer; 23 | 24 | #[repr(C)] 25 | struct Solver { 26 | trimmer: Trimmer, 27 | graph: Option, 28 | mutate_nonce: bool, 29 | } 30 | 31 | #[no_mangle] 32 | pub unsafe extern "C" fn create_solver_ctx(params: *mut SolverParams) -> *mut SolverCtx { 33 | let platform = match (*params).platform { 34 | 1 => Some("AMD"), 35 | 2 => Some("NVIDIA"), 36 | _ => None, 37 | }; 38 | let device_id = Some((*params).device as usize); 39 | let mut edge_bits = (*params).edge_bits as u8; 40 | if edge_bits < 31 || edge_bits > 64 { 41 | edge_bits = 31; 42 | } 43 | let trimmer = Trimmer::build(platform, device_id, edge_bits).expect("can't build trimmer"); 44 | let solver = Solver { 45 | trimmer: trimmer, 46 | graph: None, 47 | mutate_nonce: (*params).mutate_nonce, 48 | }; 49 | let solver_box = Box::new(solver); 50 | let solver_ref = Box::leak(solver_box); 51 | mem::transmute::<&mut Solver, *mut SolverCtx>(solver_ref) 52 | } 53 | 54 | #[no_mangle] 55 | pub unsafe extern "C" fn destroy_solver_ctx(solver_ctx_ptr: *mut SolverCtx) { 56 | // create box to clear memory 57 | let solver_ptr = mem::transmute::<*mut SolverCtx, *mut Solver>(solver_ctx_ptr); 58 | let _solver_box = Box::from_raw(solver_ptr); 59 | } 60 | 61 | #[no_mangle] 62 | pub unsafe extern "C" fn stop_solver(_solver_ctx_ptr: *mut SolverCtx) {} 63 | 64 | #[no_mangle] 65 | pub unsafe extern "C" fn fill_default_params(params: *mut SolverParams) { 66 | (*params).device = 0; 67 | (*params).platform = 0; 68 | (*params).edge_bits = 31; 69 | } 70 | 71 | #[no_mangle] 72 | pub unsafe extern "C" fn run_solver( 73 | ctx: *mut SolverCtx, 74 | header_ptr: *const c_uchar, 75 | header_length: u32, 76 | nonce: u64, 77 | _range: u32, 78 | solutions: *mut SolverSolutions, 79 | stats: *mut SolverStats, 80 | ) -> u32 { 81 | let start = SystemTime::now(); 82 | let solver_ptr = mem::transmute::<*mut SolverCtx, *mut Solver>(ctx); 83 | let solver = &*solver_ptr; 84 | let mut header = Vec::with_capacity(header_length as usize); 85 | let r_ptr = header.as_mut_ptr(); 86 | ptr::copy_nonoverlapping(header_ptr, r_ptr, header_length as usize); 87 | header.set_len(header_length as usize); 88 | let n = nonce as u32; 89 | let k = match set_header_nonce(&header, Some(n), solver.mutate_nonce) { 90 | Err(_e) => { 91 | return 2; 92 | } 93 | Ok(v) => v, 94 | }; 95 | let res = solver.trimmer.run(&k).unwrap(); 96 | 97 | let sols = Graph::search(&res).unwrap(); 98 | let end = SystemTime::now(); 99 | let elapsed = end.duration_since(start).unwrap(); 100 | let mut i = 0; 101 | (*solutions).edge_bits = 31; 102 | (*solutions).num_sols = sols.len() as u32; 103 | for sol in sols { 104 | (*solutions).sols[i].nonce = nonce; 105 | (*solutions).sols[i] 106 | .proof 107 | .copy_from_slice(&sol.nonces[..sol.nonces.len()]); 108 | i += 1; 109 | } 110 | (*stats).edge_bits = 31; 111 | (*stats).device_id = solver.trimmer.device_id as u32; 112 | let name_bytes = solver.trimmer.device_name.as_bytes(); 113 | let n = std::cmp::min((*stats).device_name.len(), name_bytes.len()); 114 | (*stats).device_name[..n].copy_from_slice(&solver.trimmer.device_name.as_bytes()[..n]); 115 | (*stats).last_solution_time = duration_to_u64(elapsed); 116 | (*stats).last_start_time = 117 | duration_to_u64(start.duration_since(SystemTime::UNIX_EPOCH).unwrap()); 118 | (*stats).last_end_time = duration_to_u64(end.duration_since(SystemTime::UNIX_EPOCH).unwrap()); 119 | 0 120 | } 121 | 122 | fn duration_to_u64(elapsed: Duration) -> u64 { 123 | elapsed.as_secs() * 1_000_000_000 + elapsed.subsec_nanos() as u64 124 | } 125 | 126 | pub fn set_header_nonce( 127 | header: &[u8], 128 | nonce: Option, 129 | mutate_nonce: bool, 130 | ) -> Result<[u64; 4], Error> { 131 | if let Some(n) = nonce { 132 | let len = header.len(); 133 | let mut header = header.to_owned(); 134 | if mutate_nonce { 135 | header.truncate(len - 4); 136 | header.write_u32::(n)?; 137 | } 138 | create_siphash_keys(&header) 139 | } else { 140 | create_siphash_keys(&header) 141 | } 142 | } 143 | 144 | pub fn create_siphash_keys(header: &[u8]) -> Result<[u64; 4], Error> { 145 | let h = blake2b(32, &[], &header); 146 | let hb = h.as_bytes(); 147 | let mut rdr = Cursor::new(hb); 148 | Ok([ 149 | rdr.read_u64::()?, 150 | rdr.read_u64::()?, 151 | rdr.read_u64::()?, 152 | rdr.read_u64::()?, 153 | ]) 154 | } 155 | 156 | #[cfg(test)] 157 | mod tests { 158 | use super::*; 159 | #[ignore] 160 | // results in Error executing function: clEnqueueNDRangeKernel("LeanRound") 161 | // Status error code: CL_INVALID_WORK_GROUP_SIZE (-54) 162 | // on MacOSX 163 | #[test] 164 | fn test_solve() { 165 | let trimmer = Trimmer::build(None, None, 29).expect("can't build trimmer"); 166 | let k = [ 167 | 0x27580576fe290177, 168 | 0xf9ea9b2031f4e76e, 169 | 0x1663308c8607868f, 170 | 0xb88839b0fa180d0e, 171 | ]; 172 | 173 | let res = trimmer.run(&k).unwrap(); 174 | println!("Trimmed to {}", res.len()); 175 | 176 | let sols = Graph::search(&res).unwrap(); 177 | assert_eq!(1, sols.len()); 178 | for sol in sols { 179 | println!("Solution: {:x?}", sol.nonces); 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /ocl_cuckatoo/src/trimmer.rs: -------------------------------------------------------------------------------- 1 | use ocl; 2 | use ocl::{Buffer, Context, Device, Kernel, Platform, Program, Queue, SpatialDims}; 3 | 4 | const RES_BUFFER_SIZE: usize = 4_000_000; 5 | const LOCAL_WORK_SIZE: usize = 256; 6 | const GLOBAL_WORK_SIZE: usize = 1024 * LOCAL_WORK_SIZE; 7 | 8 | enum Mode { 9 | SetCnt = 1, 10 | Trim = 2, 11 | Extract = 3, 12 | } 13 | 14 | pub struct Trimmer { 15 | edge_bits: u8, 16 | q: Queue, 17 | program: Program, 18 | edges: Buffer, 19 | counters: Buffer, 20 | result: Buffer, 21 | res_buf: Vec, 22 | pub device_name: String, 23 | pub device_id: usize, 24 | } 25 | 26 | impl Trimmer { 27 | pub fn build( 28 | platform_name: Option<&str>, 29 | device_id: Option, 30 | edge_bits: u8, 31 | ) -> ocl::Result { 32 | let platform = find_platform(platform_name) 33 | .ok_or::("Can't find OpenCL platform".into())?; 34 | let device = find_device(&platform, device_id)?; 35 | 36 | let el_count = (1024 * 1024 * 16) << (edge_bits - 29); 37 | let res_buf: Vec = vec![0; RES_BUFFER_SIZE]; 38 | 39 | let context = Context::builder() 40 | .platform(platform) 41 | .devices(device) 42 | .build()?; 43 | 44 | let q = Queue::new(&context, device, None)?; 45 | 46 | let program = Program::builder() 47 | .devices(device) 48 | .src(SRC) 49 | .cmplr_def("EDGEBITS", edge_bits as i32) 50 | .build(&context)?; 51 | 52 | let edges = Buffer::::builder() 53 | .queue(q.clone()) 54 | .len(el_count) 55 | .fill_val(0xFFFFFFFF) 56 | .build()?; 57 | let counters = Buffer::::builder() 58 | .queue(q.clone()) 59 | .len(el_count) 60 | .fill_val(0) 61 | .build()?; 62 | let result = unsafe { 63 | Buffer::::builder() 64 | .queue(q.clone()) 65 | .len(RES_BUFFER_SIZE) 66 | .fill_val(0) 67 | .use_host_slice(&res_buf[..]) 68 | .build()? 69 | }; 70 | 71 | Ok(Trimmer { 72 | edge_bits, 73 | q, 74 | program, 75 | edges, 76 | counters, 77 | result, 78 | res_buf, 79 | device_name: device.name()?, 80 | device_id: device_id.unwrap_or(0), 81 | }) 82 | } 83 | 84 | pub fn run(&self, k: &[u64; 4]) -> ocl::Result> { 85 | let mut current_mode = Mode::SetCnt; 86 | let mut current_uorv: u32 = 0; 87 | let trims = if self.edge_bits >= 29 { 128 } else { 256 }; 88 | let enqs = 8 << (self.edge_bits - 29); 89 | 90 | let mut kernel = Kernel::builder() 91 | .name("LeanRound") 92 | .program(&self.program) 93 | .queue(self.q.clone()) 94 | .global_work_size(GLOBAL_WORK_SIZE) 95 | .local_work_size(SpatialDims::One(LOCAL_WORK_SIZE)) 96 | .arg(k[0]) 97 | .arg(k[1]) 98 | .arg(k[2]) 99 | .arg(k[3]) 100 | .arg(&self.edges) 101 | .arg(&self.counters) 102 | .arg(&self.result) 103 | .arg(current_mode as u32) 104 | .arg(current_uorv) 105 | .build()?; 106 | 107 | let mut offset; 108 | 109 | macro_rules! kernel_enq ( 110 | ($num:expr) => ( 111 | for i in 0..$num { 112 | offset = i * GLOBAL_WORK_SIZE; 113 | unsafe { 114 | kernel 115 | .set_default_global_work_offset(SpatialDims::One(offset)) 116 | .enq()?; 117 | } 118 | } 119 | )); 120 | 121 | for l in 0..trims { 122 | current_uorv = l & 1 as u32; 123 | current_mode = Mode::SetCnt; 124 | kernel.set_arg(7, current_mode as u32)?; 125 | kernel.set_arg(8, current_uorv)?; 126 | kernel_enq!(enqs); 127 | 128 | current_mode = if l == (trims - 1) { 129 | Mode::Extract 130 | } else { 131 | Mode::Trim 132 | }; 133 | kernel.set_arg(7, current_mode as u32)?; 134 | kernel_enq!(enqs); 135 | // prepare for the next round 136 | self.counters.cmd().fill(0, None).enq()?; 137 | } 138 | unsafe { 139 | self.result.map().enq()?; 140 | } 141 | self.q.finish()?; 142 | let ret = self.res_buf.clone(); 143 | self.edges.cmd().fill(0xFFFFFFFF, None).enq()?; 144 | self.result.cmd().fill(0, None).enq()?; 145 | self.q.finish()?; 146 | Ok(ret) 147 | } 148 | } 149 | 150 | fn find_platform(selector: Option<&str>) -> Option { 151 | match selector { 152 | None => Some(Platform::default()), 153 | Some(sel) => Platform::list().into_iter().find(|p| { 154 | if let Ok(vendor) = p.name() { 155 | vendor.contains(sel) 156 | } else { 157 | false 158 | } 159 | }), 160 | } 161 | } 162 | 163 | fn find_device(platform: &Platform, selector: Option) -> ocl::Result { 164 | match selector { 165 | None => Device::first(platform), 166 | Some(index) => Device::by_idx_wrap(platform, index), 167 | } 168 | } 169 | 170 | const SRC: &str = r#" 171 | typedef uint8 u8; 172 | typedef uint16 u16; 173 | typedef uint u32; 174 | typedef ulong u64; 175 | typedef u32 node_t; 176 | typedef u64 nonce_t; 177 | 178 | #define DEBUG 0 179 | 180 | // number of edges 181 | #define NEDGES ((u64)1 << EDGEBITS) 182 | // used to mask siphash output 183 | #define EDGEMASK (NEDGES - 1) 184 | 185 | #define SIPROUND \ 186 | do { \ 187 | v0 += v1; v2 += v3; v1 = rotate(v1,(ulong)13); \ 188 | v3 = rotate(v3,(ulong)16); v1 ^= v0; v3 ^= v2; \ 189 | v0 = rotate(v0,(ulong)32); v2 += v1; v0 += v3; \ 190 | v1 = rotate(v1,(ulong)17); v3 = rotate(v3,(ulong)21); \ 191 | v1 ^= v2; v3 ^= v0; v2 = rotate(v2,(ulong)32); \ 192 | } while(0) 193 | 194 | u64 dipnode(ulong v0i, ulong v1i, ulong v2i, ulong v3i, u64 nce, uint uorv) { 195 | ulong nonce = 2 * nce + uorv; 196 | ulong v0 = v0i, v1 = v1i, v2 = v2i, v3 = v3i ^ nonce; 197 | SIPROUND; SIPROUND; 198 | v0 ^= nonce; 199 | v2 ^= 0xff; 200 | SIPROUND; SIPROUND; SIPROUND; SIPROUND; 201 | return (v0 ^ v1 ^ v2 ^ v3) & EDGEMASK; 202 | } 203 | 204 | #define MODE_SETCNT 1 205 | #define MODE_TRIM 2 206 | #define MODE_EXTRACT 3 207 | 208 | // Minimalistic cuckatoo lean trimmer 209 | // This implementation is not optimal! 210 | // 211 | // 8 global kernel executions (hardcoded ATM) 212 | // 1024 thread blocks, 256 threads each, 256 edges for each thread 213 | // 8*1024*256*256 = 536 870 912 edges = cuckatoo29 214 | __attribute__((reqd_work_group_size(256, 1, 1))) 215 | __kernel void LeanRound(const u64 v0i, const u64 v1i, const u64 v2i, const u64 v3i, __global uint8 * edges, __global uint * counters, __global u32 * aux, const u32 mode, const u32 uorv) 216 | { 217 | const int blocks = NEDGES / 32; 218 | const int gid = get_global_id(0); 219 | const int lid = get_local_id(0); 220 | __local u32 el[256][8]; 221 | 222 | { 223 | int lCount = 0; 224 | // what 256 nit block of edges are we processing 225 | u64 index = gid; 226 | u64 start = index * 256; 227 | // load all 256 bits (edges) to registers 228 | uint8 load = edges[index]; 229 | // map to an array for easier indexing (depends on compiler/GPU, could be pushed out to cache) 230 | el[lid][0] = load.s0; 231 | el[lid][1] = load.s1; 232 | el[lid][2] = load.s2; 233 | el[lid][3] = load.s3; 234 | el[lid][4] = load.s4; 235 | el[lid][5] = load.s5; 236 | el[lid][6] = load.s6; 237 | el[lid][7] = load.s7; 238 | 239 | // process as 8 x 32bit segment, GPUs have 32bit ALUs 240 | for (short i = 0; i < 8; i++) 241 | { 242 | // shortcut to current 32bit value 243 | uint ee = el[lid][i]; 244 | // how many edges we process in the block 245 | short lEdges = popcount(ee); 246 | // whole warp will always execute worst case scenario, but it will help in the long run (not benched) 247 | 248 | // now a loop for every single living edge in current 32 edge block 249 | for (short e = 0; e < lEdges; e++) 250 | { 251 | // bit position of next living edge 252 | short pos = clz(ee); 253 | // position in the 256 edge block 254 | int subPos = (i * 32) + pos; 255 | // reconstruct value of noce for this edge 256 | int nonce = start + subPos; 257 | // calculate siphash24 for either U or V (host device control) 258 | u32 hash = dipnode(v0i, v1i, v2i, v3i, nonce, uorv); 259 | 260 | // this time we set edge bit counters - PASS 1 261 | if (mode == MODE_SETCNT) 262 | { 263 | // what global memory 32bit block we need to access 264 | int block = hash / 32; 265 | // what bit in the block we need to set 266 | u32 bit = hash % 32; 267 | // create a bitmask from that bit 268 | u32 mask = (u32)1 << bit; 269 | // global atomic or (set bit to 1 no matter what it was) 270 | atomic_or(&counters[block], mask); 271 | } 272 | // this time counters are already set so need to figure out if the edge lives - PASS 2 273 | else if ((mode == MODE_TRIM) || (mode == MODE_EXTRACT)) 274 | { 275 | // cuckatoo XOR thing 276 | hash = hash ^ 1; 277 | // what global memory 32bit block we need to read 278 | int block = hash / 32; 279 | // what bit in the block we need to read 280 | u32 bit = hash % 32; 281 | // create a bitmask from that bit 282 | u32 mask = (u32)1 << bit; 283 | // does the edge live or not 284 | bool lives = ((counters[block]) & mask) > 0; 285 | // if edge is not alive, kill it (locally in registers) 286 | if (!lives) 287 | { 288 | el[lid][i] ^= ((u32)1<<31) >> pos; // 1 XOR 1 is 0 289 | } 290 | else 291 | { 292 | // debug counter of alive edges 293 | if (DEBUG) 294 | lCount++; 295 | 296 | // if this is last lean round we do, store all edges in one long list 297 | if (mode == MODE_EXTRACT) // PASS N_rounds 298 | { 299 | // obtain global pointer to final edge list 300 | int edgePos = atomic_inc(aux+1); 301 | // position in output array as multiple of 128bits (32bits will be empty) 302 | int auxIndex = 4 + (edgePos * 4); 303 | // debug failsafe 304 | //if (!(DEBUG && (auxIndex > (1024 * 1024)))) 305 | { 306 | // store all information to global memory 307 | aux[auxIndex + 0] = dipnode(v0i, v1i, v2i, v3i, nonce, 0); 308 | aux[auxIndex + 1] = dipnode(v0i, v1i, v2i, v3i, nonce, 1); 309 | aux[auxIndex + 2] = nonce; 310 | aux[auxIndex + 3] = 0; // for clarity, competely useless operation 311 | } 312 | } 313 | } 314 | } 315 | // clear current edge position so that we can skip it in next run of ctz() 316 | ee ^= ((u32)1<<31) >> pos; // 1 XOR 1 is 0 317 | } 318 | } 319 | // return edge bits back to global memory if we are in second stage 320 | if (mode == MODE_TRIM) 321 | edges[index] = (uint8)(el[lid][0], el[lid][1], el[lid][2], el[lid][3], el[lid][4], el[lid][5], el[lid][6], el[lid][7]); 322 | // debug only, use aux buffer to count alive edges in this round 323 | if (DEBUG) 324 | atomic_add(aux, lCount); 325 | } 326 | } 327 | "#; 328 | -------------------------------------------------------------------------------- /plugin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "grin_miner_plugin" 3 | version = "4.0.0" 4 | authors = ["Grin Developers "] 5 | repository = "https://github.com/mimblewimble/grin-miner" 6 | description = "Device specific plugins for the grin miner" 7 | license = "Apache-2.0" 8 | workspace = ".." 9 | 10 | [dependencies] 11 | byteorder = "1" 12 | blake2-rfc = "0.2" 13 | libc = "0.2" 14 | serde = "1" 15 | serde_derive = "1" 16 | serde_json = "1" 17 | -------------------------------------------------------------------------------- /plugin/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Crate wrapping up the Grin miner plugins 16 | 17 | #![deny(non_upper_case_globals)] 18 | #![deny(non_camel_case_types)] 19 | #![deny(non_snake_case)] 20 | #![deny(unused_mut)] 21 | #![warn(missing_docs)] 22 | 23 | extern crate serde; 24 | #[macro_use] 25 | extern crate serde_derive; 26 | extern crate blake2_rfc as blake2; 27 | extern crate byteorder; 28 | extern crate libc; 29 | extern crate serde_json; 30 | 31 | use libc::*; 32 | use std::ffi::CString; 33 | use std::ptr::NonNull; 34 | use std::{cmp, fmt, marker}; 35 | 36 | use blake2::blake2b::Blake2b; 37 | use byteorder::{BigEndian, ByteOrder}; 38 | 39 | /// Size of proof 40 | pub const PROOFSIZE: usize = 42; 41 | /// Maximin length of plugin name w 42 | pub const MAX_NAME_LEN: usize = 256; 43 | /// Maximum number of solutions 44 | pub const MAX_SOLS: usize = 4; 45 | 46 | // Type definitions corresponding to each function that the plugin/solver implements 47 | /// Create solver function 48 | pub type CuckooCreateSolverCtx = unsafe extern "C" fn(*mut SolverParams) -> *mut SolverCtx; 49 | /// Destroy solver function 50 | pub type CuckooDestroySolverCtx = unsafe extern "C" fn(*mut SolverCtx); 51 | /// Run solver function 52 | pub type CuckooRunSolver = unsafe extern "C" fn( 53 | *mut SolverCtx, // Solver context 54 | *const c_uchar, // header 55 | u32, // header length 56 | u64, // nonce 57 | u32, // range 58 | *mut SolverSolutions, // reference to any found solutions 59 | *mut SolverStats, // solver stats 60 | ) -> u32; 61 | /// Stop solver function 62 | pub type CuckooStopSolver = unsafe extern "C" fn(*mut SolverCtx); 63 | /// Fill default params of solver 64 | pub type CuckooFillDefaultParams = unsafe extern "C" fn(*mut SolverParams); 65 | 66 | /// A solver context, opaque reference to C++ type underneath 67 | #[derive(Copy, Clone, Debug)] 68 | pub enum SolverCtx {} 69 | /// wrap ctx to send across threads 70 | pub struct SolverCtxWrapper(pub NonNull); 71 | unsafe impl marker::Send for SolverCtxWrapper {} 72 | 73 | /// Common parameters for a solver 74 | #[derive(Clone, Debug, Serialize, Deserialize)] 75 | #[repr(C)] 76 | pub struct SolverParams { 77 | /// threads 78 | pub nthreads: u32, 79 | /// trims 80 | pub ntrims: u32, 81 | /// Whether to show cycle (should be true to get solutions) 82 | pub showcycle: bool, 83 | /// allrounds 84 | pub allrounds: bool, 85 | /// whether to apply the nonce to the header, or leave as is, 86 | /// letting caller mutate nonce 87 | pub mutate_nonce: bool, 88 | /// reduce cpuload 89 | pub cpuload: bool, 90 | 91 | /// Common Cuda params 92 | pub device: u32, 93 | 94 | /// Lean cuda params 95 | pub blocks: u32, 96 | /// 97 | pub tpb: u32, 98 | 99 | /// Mean cuda params 100 | pub expand: u32, 101 | /// 102 | pub genablocks: u32, 103 | /// 104 | pub genatpb: u32, 105 | /// 106 | pub genbtpb: u32, 107 | /// 108 | pub trimtpb: u32, 109 | /// 110 | pub tailtpb: u32, 111 | /// 112 | pub recoverblocks: u32, 113 | /// 114 | pub recovertpb: u32, 115 | /// OCL platform ID, 0 - default, 1 - AMD, 2 - NVIDIA 116 | pub platform: u32, 117 | /// edge bits for OCL plugins 118 | pub edge_bits: u32, 119 | } 120 | 121 | impl Default for SolverParams { 122 | fn default() -> SolverParams { 123 | SolverParams { 124 | nthreads: 0, 125 | ntrims: 0, 126 | showcycle: true, 127 | allrounds: false, 128 | mutate_nonce: false, 129 | cpuload: true, 130 | device: 0, 131 | blocks: 0, 132 | tpb: 0, 133 | expand: 0, 134 | genablocks: 0, 135 | genatpb: 0, 136 | genbtpb: 0, 137 | trimtpb: 0, 138 | tailtpb: 0, 139 | recoverblocks: 0, 140 | recovertpb: 0, 141 | platform: 0, 142 | edge_bits: 31, 143 | } 144 | } 145 | } 146 | 147 | /// Common stats collected by solvers 148 | #[derive(Clone)] 149 | #[repr(C)] 150 | pub struct SolverStats { 151 | /// device Id 152 | pub device_id: u32, 153 | /// graph size 154 | pub edge_bits: u32, 155 | /// plugin name 156 | pub plugin_name: [c_uchar; MAX_NAME_LEN], 157 | /// device name 158 | pub device_name: [c_uchar; MAX_NAME_LEN], 159 | /// whether device has reported an error 160 | pub has_errored: bool, 161 | /// reason for error 162 | pub error_reason: [c_uchar; MAX_NAME_LEN], 163 | /// number of searched completed by device 164 | pub iterations: u32, 165 | /// last solution start time 166 | pub last_start_time: u64, 167 | /// last solution end time 168 | pub last_end_time: u64, 169 | /// last solution elapsed time 170 | pub last_solution_time: u64, 171 | } 172 | 173 | impl Default for SolverStats { 174 | fn default() -> SolverStats { 175 | SolverStats { 176 | device_id: 0, 177 | edge_bits: 0, 178 | plugin_name: [0; MAX_NAME_LEN], 179 | device_name: [0; MAX_NAME_LEN], 180 | has_errored: false, 181 | error_reason: [0; MAX_NAME_LEN], 182 | iterations: 0, 183 | last_start_time: 0, 184 | last_end_time: 0, 185 | last_solution_time: 0, 186 | } 187 | } 188 | } 189 | 190 | impl SolverStats { 191 | fn get_name(&self, c_str: &[u8; MAX_NAME_LEN]) -> String { 192 | // remove all null zeroes 193 | let v = c_str.clone().to_vec(); 194 | let mut i = 0; 195 | for j in 0..v.len() { 196 | if v.get(j) == Some(&0) { 197 | i = j; 198 | break; 199 | } 200 | } 201 | let v = v.split_at(i).0; 202 | match CString::new(v) { 203 | Ok(s) => s.to_str().unwrap().to_owned(), 204 | Err(_) => String::from("Unknown Device Name"), 205 | } 206 | } 207 | /// return device name as rust string 208 | pub fn get_device_name(&self) -> String { 209 | self.get_name(&self.device_name) 210 | } 211 | /// return plugin name as rust string 212 | pub fn get_plugin_name(&self) -> String { 213 | self.get_name(&self.plugin_name) 214 | } 215 | /// return plugin name as rust string 216 | pub fn get_error_reason(&self) -> String { 217 | self.get_name(&self.error_reason) 218 | } 219 | /// set plugin name 220 | pub fn set_plugin_name(&mut self, name: &str) { 221 | let c_vec = CString::new(name).unwrap().into_bytes(); 222 | for (i, _) in c_vec.iter().enumerate() { 223 | self.plugin_name[i] = c_vec[i]; 224 | } 225 | } 226 | } 227 | 228 | /// A single solution 229 | #[repr(C)] 230 | #[derive(Clone, Copy)] 231 | pub struct Solution { 232 | /// Optional ID 233 | pub id: u64, 234 | /// Nonce 235 | pub nonce: u64, 236 | /// Proof 237 | pub proof: [u64; PROOFSIZE], 238 | } 239 | 240 | impl Default for Solution { 241 | fn default() -> Solution { 242 | Solution { 243 | id: 0, 244 | nonce: 0, 245 | proof: [0u64; PROOFSIZE], 246 | } 247 | } 248 | } 249 | 250 | impl fmt::Display for Solution { 251 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 252 | let mut comma_separated = String::new(); 253 | 254 | for num in &self.proof[0..self.proof.len()] { 255 | comma_separated.push_str(&format!("0x{:X}", &num)); 256 | comma_separated.push_str(", "); 257 | } 258 | comma_separated.pop(); 259 | comma_separated.pop(); 260 | 261 | write!(f, "Nonce:{} [{}]", self.nonce, comma_separated) 262 | } 263 | } 264 | 265 | impl fmt::Debug for Solution { 266 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 267 | write!(f, "{:?}", &self.proof[..]) 268 | } 269 | } 270 | 271 | impl cmp::PartialEq for Solution { 272 | fn eq(&self, other: &Solution) -> bool { 273 | for i in 0..PROOFSIZE { 274 | if self.proof[i] != other.proof[i] { 275 | return false; 276 | } 277 | } 278 | true 279 | } 280 | } 281 | 282 | impl Solution { 283 | /// Converts the proof to a vector of u64s 284 | pub fn to_u64s(&self) -> Vec { 285 | let mut nonces = Vec::with_capacity(PROOFSIZE); 286 | for n in self.proof.iter() { 287 | nonces.push(*n as u64); 288 | } 289 | nonces 290 | } 291 | 292 | /// Returns the hash of the solution, as performed in 293 | /// grin 294 | /// TODO: Check whether grin sticks to u32s like this 295 | pub fn hash(&self) -> [u8; 32] { 296 | // Hash 297 | let mut blake2b = Blake2b::new(32); 298 | for n in 0..self.proof.len() { 299 | let mut bytes = [0; 4]; 300 | BigEndian::write_u32(&mut bytes, self.proof[n] as u32); 301 | blake2b.update(&bytes); 302 | } 303 | let mut ret = [0; 32]; 304 | ret.copy_from_slice(blake2b.finalize().as_bytes()); 305 | ret 306 | } 307 | } 308 | 309 | /// All solutions returned 310 | #[derive(Clone, Copy)] 311 | #[repr(C)] 312 | pub struct SolverSolutions { 313 | /// graph size 314 | pub edge_bits: u32, 315 | /// number of solutions 316 | pub num_sols: u32, 317 | /// solutions themselves 318 | pub sols: [Solution; MAX_SOLS], 319 | } 320 | 321 | impl Default for SolverSolutions { 322 | fn default() -> SolverSolutions { 323 | SolverSolutions { 324 | edge_bits: 0, 325 | num_sols: 0, 326 | sols: [Solution::default(); MAX_SOLS], 327 | } 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = true 2 | -------------------------------------------------------------------------------- /src/bin/grin_miner.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Stratum client implementation, for standalone mining against a running 16 | //! grin node 17 | extern crate cuckoo_miner as cuckoo; 18 | extern crate grin_miner_config as config; 19 | extern crate grin_miner_plugin as plugin; 20 | extern crate grin_miner_util as util; 21 | 22 | extern crate bufstream; 23 | extern crate native_tls; 24 | extern crate time; 25 | #[macro_use] 26 | extern crate serde_derive; 27 | extern crate serde_json; 28 | #[macro_use] 29 | extern crate slog; 30 | 31 | #[cfg(feature = "tui")] 32 | extern crate cursive; 33 | 34 | pub mod client; 35 | pub mod mining; 36 | pub mod stats; 37 | pub mod types; 38 | 39 | #[cfg(feature = "tui")] 40 | pub mod tui; 41 | 42 | use config::GlobalConfig; 43 | use std::sync::atomic::{AtomicBool, Ordering}; 44 | use std::sync::{Arc, RwLock}; 45 | use std::thread; 46 | 47 | use util::{init_logger, LOGGER}; 48 | 49 | // include build information 50 | pub mod built_info { 51 | include!(concat!(env!("OUT_DIR"), "/built.rs")); 52 | } 53 | 54 | pub fn info_strings() -> (String, String, String) { 55 | ( 56 | format!( 57 | "This is Grin-Miner version {}{}, built for {} by {}.", 58 | built_info::PKG_VERSION, 59 | built_info::GIT_VERSION.map_or_else(|| "".to_owned(), |v| format!(" (git {})", v)), 60 | built_info::TARGET, 61 | built_info::RUSTC_VERSION 62 | ), 63 | format!( 64 | "Built with profile \"{}\", features \"{}\" on {}.", 65 | built_info::PROFILE, 66 | built_info::FEATURES_STR, 67 | built_info::BUILT_TIME_UTC 68 | ), 69 | format!("Dependencies:\n {}", built_info::DEPENDENCIES_STR), 70 | ) 71 | } 72 | 73 | fn log_build_info() { 74 | let (basic_info, detailed_info, deps) = info_strings(); 75 | info!(LOGGER, "{}", basic_info); 76 | debug!(LOGGER, "{}", detailed_info); 77 | trace!(LOGGER, "{}", deps); 78 | } 79 | 80 | #[cfg(feature = "tui")] 81 | mod with_tui { 82 | use stats; 83 | use std::sync::atomic::{AtomicBool, Ordering}; 84 | use std::sync::{mpsc, Arc, RwLock}; 85 | use std::thread; 86 | use tui::ui; 87 | use types; 88 | 89 | pub fn start_tui( 90 | s: Arc>, 91 | client_tx: mpsc::Sender, 92 | miner_tx: mpsc::Sender, 93 | stop: Arc, 94 | ) { 95 | // Run the UI controller.. here for now for simplicity to access 96 | // everything it might need 97 | println!("Starting Grin Miner in UI mode..."); 98 | println!("Waiting for solvers to shutdown..."); 99 | let _ = thread::Builder::new() 100 | .name("ui".to_string()) 101 | .spawn(move || { 102 | let mut controller = ui::Controller::new().unwrap_or_else(|e| { 103 | panic!("Error loading UI controller: {}", e); 104 | }); 105 | controller.run(s.clone()); 106 | // Shut down everything else on tui exit 107 | let _ = client_tx.send(types::ClientMessage::Shutdown); 108 | let _ = miner_tx.send(types::MinerMessage::Shutdown); 109 | stop.store(true, Ordering::Relaxed); 110 | }); 111 | } 112 | } 113 | 114 | fn main() { 115 | // Init configuration 116 | let mut global_config = GlobalConfig::new(None).unwrap_or_else(|e| { 117 | panic!("Error parsing config file: {}", e); 118 | }); 119 | println!( 120 | "Starting Grin-Miner from config file at: {}", 121 | global_config.config_file_path.unwrap().to_str().unwrap() 122 | ); 123 | // Init logging 124 | let mut log_conf = global_config 125 | .members 126 | .as_mut() 127 | .unwrap() 128 | .logging 129 | .clone() 130 | .unwrap(); 131 | 132 | let mining_config = global_config.members.as_mut().unwrap().mining.clone(); 133 | 134 | if cfg!(feature = "tui") && mining_config.run_tui { 135 | log_conf.log_to_stdout = false; 136 | log_conf.tui_running = Some(true); 137 | } 138 | 139 | init_logger(Some(log_conf)); 140 | 141 | log_build_info(); 142 | let stats = Arc::new(RwLock::new(stats::Stats::default())); 143 | 144 | let mut mc = 145 | mining::Controller::new(mining_config.clone(), stats.clone()).unwrap_or_else(|e| { 146 | panic!("Error loading mining controller: {}", e); 147 | }); 148 | let cc = client::Controller::new( 149 | &mining_config.stratum_server_addr, 150 | mining_config.stratum_server_login.clone(), 151 | mining_config.stratum_server_password.clone(), 152 | mining_config.stratum_server_tls_enabled, 153 | mc.tx.clone(), 154 | stats.clone(), 155 | ) 156 | .unwrap_or_else(|e| { 157 | panic!("Error loading stratum client controller: {:?}", e); 158 | }); 159 | let tui_stopped = Arc::new(AtomicBool::new(false)); 160 | let miner_stopped = Arc::new(AtomicBool::new(false)); 161 | let client_stopped = Arc::new(AtomicBool::new(false)); 162 | 163 | // Load plugin configuration and start solvers first, 164 | // so we can exit pre-tui if something is obviously wrong 165 | debug!(LOGGER, "Starting solvers"); 166 | let result = config::read_configs( 167 | mining_config.miner_plugin_dir.clone(), 168 | mining_config.miner_plugin_config.clone(), 169 | ); 170 | let mut miner = match result { 171 | Ok(cfgs) => cuckoo::CuckooMiner::new(cfgs), 172 | Err(e) => { 173 | println!("Error loading plugins. Please check logs for further info."); 174 | println!("Error details:"); 175 | println!("{:?}", e); 176 | println!("Exiting"); 177 | return; 178 | } 179 | }; 180 | if let Err(e) = miner.start_solvers() { 181 | println!("Error starting plugins. Please check logs for further info."); 182 | println!("Error details:"); 183 | println!("{:?}", e); 184 | println!("Exiting"); 185 | return; 186 | } 187 | 188 | if mining_config.run_tui { 189 | #[cfg(feature = "tui")] 190 | with_tui::start_tui(stats, cc.tx.clone(), mc.tx.clone(), tui_stopped.clone()); 191 | 192 | #[cfg(not(feature = "tui"))] 193 | warn!(LOGGER, "Grin-miner was built with TUI support disabled!"); 194 | } else { 195 | tui_stopped.store(true, Ordering::Relaxed); 196 | } 197 | 198 | mc.set_client_tx(cc.tx.clone()); 199 | 200 | let miner_stopped_internal = miner_stopped.clone(); 201 | let _ = thread::Builder::new() 202 | .name("mining_controller".to_string()) 203 | .spawn(move || { 204 | if let Err(e) = mc.run(miner) { 205 | error!( 206 | LOGGER, 207 | "Error loading plugins. Please check logs for further info: {:?}", e 208 | ); 209 | return; 210 | } 211 | miner_stopped_internal.store(true, Ordering::Relaxed); 212 | }); 213 | 214 | let client_stopped_internal = client_stopped.clone(); 215 | let _ = thread::Builder::new() 216 | .name("client_controller".to_string()) 217 | .spawn(move || { 218 | cc.run(); 219 | client_stopped_internal.store(true, Ordering::Relaxed); 220 | }); 221 | 222 | loop { 223 | if miner_stopped.load(Ordering::Relaxed) 224 | && client_stopped.load(Ordering::Relaxed) 225 | && tui_stopped.load(Ordering::Relaxed) 226 | { 227 | thread::sleep(std::time::Duration::from_millis(100)); 228 | break; 229 | } 230 | thread::sleep(std::time::Duration::from_millis(100)); 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /src/bin/mining.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Plugin controller, listens for messages sent from the stratum 16 | /// server, controls plugins and responds appropriately 17 | use std::sync::{mpsc, Arc, RwLock}; 18 | use std::{self, thread}; 19 | use time; 20 | use util::LOGGER; 21 | use {config, stats, types}; 22 | 23 | use cuckoo::{CuckooMiner, CuckooMinerError}; 24 | 25 | use plugin::SolverStats; 26 | 27 | pub struct Controller { 28 | _config: config::MinerConfig, 29 | rx: mpsc::Receiver, 30 | pub tx: mpsc::Sender, 31 | client_tx: Option>, 32 | current_height: u64, 33 | current_job_id: u64, 34 | current_target_diff: u64, 35 | stats: Arc>, 36 | } 37 | 38 | impl Controller { 39 | pub fn new( 40 | config: config::MinerConfig, 41 | stats: Arc>, 42 | ) -> Result { 43 | { 44 | let mut stats_w = stats.write().unwrap(); 45 | stats_w.client_stats.server_url = config.stratum_server_addr.clone(); 46 | } 47 | let (tx, rx) = mpsc::channel::(); 48 | Ok(Controller { 49 | _config: config, 50 | rx, 51 | tx, 52 | client_tx: None, 53 | current_height: 0, 54 | current_job_id: 0, 55 | current_target_diff: 0, 56 | stats, 57 | }) 58 | } 59 | 60 | pub fn set_client_tx(&mut self, client_tx: mpsc::Sender) { 61 | self.client_tx = Some(client_tx); 62 | } 63 | 64 | /// Run the mining controller, solvers in miner should already be going 65 | pub fn run(&mut self, mut miner: CuckooMiner) -> Result<(), CuckooMinerError> { 66 | // how often to output stats 67 | let stat_output_interval = 2; 68 | let mut next_stat_output = time::get_time().sec + stat_output_interval; 69 | 70 | loop { 71 | while let Some(message) = self.rx.try_iter().next() { 72 | debug!(LOGGER, "Miner received message: {:?}", message); 73 | let result = match message { 74 | types::MinerMessage::ReceivedJob(height, job_id, diff, pre_pow) => { 75 | self.current_height = height; 76 | self.current_job_id = job_id; 77 | self.current_target_diff = diff; 78 | miner.notify( 79 | self.current_job_id as u32, 80 | self.current_height, 81 | &pre_pow, 82 | "", 83 | diff, 84 | ) 85 | } 86 | types::MinerMessage::StopJob => { 87 | debug!(LOGGER, "Stopping jobs"); 88 | miner.pause_solvers(); 89 | Ok(()) 90 | } 91 | types::MinerMessage::Shutdown => { 92 | debug!(LOGGER, "Stopping jobs and Shutting down mining controller"); 93 | miner.stop_solvers(); 94 | miner.wait_for_solver_shutdown(); 95 | return Ok(()); 96 | } 97 | }; 98 | if let Err(e) = result { 99 | error!(LOGGER, "Mining Controller Error {:?}", e); 100 | } 101 | } 102 | 103 | if time::get_time().sec > next_stat_output { 104 | self.output_job_stats(miner.get_stats().unwrap()); 105 | next_stat_output = time::get_time().sec + stat_output_interval; 106 | } 107 | 108 | let solutions = miner.get_solutions(); 109 | if let Some(ss) = solutions { 110 | let edge_bits = ss.edge_bits; 111 | for i in 0..ss.num_sols { 112 | let _ = 113 | self.client_tx 114 | .as_mut() 115 | .unwrap() 116 | .send(types::ClientMessage::FoundSolution( 117 | self.current_height, 118 | ss.sols[i as usize].id, 119 | edge_bits, 120 | ss.sols[i as usize].nonce, 121 | ss.sols[i as usize].proof.to_vec(), 122 | )); 123 | } 124 | let mut s_stats = self.stats.write().unwrap(); 125 | s_stats.mining_stats.solution_stats.num_solutions_found += ss.num_sols; 126 | } 127 | thread::sleep(std::time::Duration::from_millis(100)); 128 | } 129 | } 130 | 131 | fn output_job_stats(&mut self, stats: Vec) { 132 | let mut sps_total = 0.0; 133 | let mut i = 0; 134 | for s in stats.clone() { 135 | let last_solution_time_secs = s.last_solution_time as f64 / 1_000_000_000.0; 136 | let last_hashes_per_sec = 1.0 / last_solution_time_secs; 137 | let status = if s.has_errored { "ERRORED" } else { "OK" }; 138 | if !s.has_errored { 139 | debug!( 140 | LOGGER, 141 | "Mining: Plugin {} - Device {} ({}) at Cucka{}{} - Status: {} : Last Graph time: {}s; \ 142 | Graphs per second: {:.*} - Total Attempts: {}", 143 | i, 144 | s.device_id, 145 | s.get_device_name(), 146 | if s.edge_bits < 30 { "rooz" } else { "too" }, 147 | s.edge_bits, 148 | status, 149 | last_solution_time_secs, 150 | 3, 151 | last_hashes_per_sec, 152 | s.iterations 153 | ); 154 | if last_hashes_per_sec.is_finite() { 155 | sps_total += last_hashes_per_sec; 156 | } 157 | } else { 158 | debug!( 159 | LOGGER, 160 | "Mining: Plugin {} - Device {} ({}) Has ERRORED! Reason: {}", 161 | i, 162 | s.device_id, 163 | s.get_device_name(), 164 | s.get_error_reason(), 165 | ); 166 | } 167 | i += 1; 168 | } 169 | info!( 170 | LOGGER, 171 | "Mining: Cucka*oo* at {} gps (graphs per second)", sps_total 172 | ); 173 | 174 | if sps_total.is_finite() { 175 | let mut s_stats = self.stats.write().unwrap(); 176 | s_stats.mining_stats.add_combined_gps(sps_total); 177 | s_stats.mining_stats.target_difficulty = self.current_target_diff; 178 | s_stats.mining_stats.block_height = self.current_height; 179 | s_stats.mining_stats.device_stats = stats; 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/bin/stats.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Miner stats collection types, to be used by tests, logging or GUI/TUI 16 | //! to collect information about mining status 17 | 18 | /// Struct to return relevant information about the mining process 19 | /// back to interested callers (such as the TUI) 20 | use plugin; 21 | 22 | #[derive(Clone)] 23 | pub struct SolutionStats { 24 | /// total solutions found 25 | pub num_solutions_found: u32, 26 | /// total shares accepted 27 | pub num_shares_accepted: u32, 28 | /// total solutions rejected 29 | pub num_rejected: u32, 30 | /// total solutions staled 31 | pub num_staled: u32, 32 | /// total blocks found 33 | pub num_blocks_found: u32, 34 | } 35 | 36 | impl Default for SolutionStats { 37 | fn default() -> SolutionStats { 38 | SolutionStats { 39 | num_solutions_found: 0, 40 | num_shares_accepted: 0, 41 | num_rejected: 0, 42 | num_staled: 0, 43 | num_blocks_found: 0, 44 | } 45 | } 46 | } 47 | 48 | #[derive(Clone)] 49 | pub struct MiningStats { 50 | /// combined graphs per second 51 | combined_gps: Vec, 52 | /// what block height we're mining at 53 | pub block_height: u64, 54 | /// current target for share difficulty we're working on 55 | pub target_difficulty: u64, 56 | /// solution statistics 57 | pub solution_stats: SolutionStats, 58 | /// Individual device status from Cuckoo-Miner 59 | pub device_stats: Vec, 60 | } 61 | 62 | impl Default for MiningStats { 63 | fn default() -> MiningStats { 64 | MiningStats { 65 | combined_gps: vec![], 66 | block_height: 0, 67 | target_difficulty: 0, 68 | solution_stats: SolutionStats::default(), 69 | device_stats: vec![], 70 | } 71 | } 72 | } 73 | 74 | impl MiningStats { 75 | pub fn add_combined_gps(&mut self, val: f64) { 76 | self.combined_gps.insert(0, val); 77 | self.combined_gps.truncate(50); 78 | } 79 | 80 | pub fn combined_gps(&self) -> f64 { 81 | if self.combined_gps.is_empty() { 82 | 0.0 83 | } else { 84 | let sum: f64 = self.combined_gps.iter().sum(); 85 | sum / (self.combined_gps.len() as f64) 86 | } 87 | } 88 | } 89 | 90 | #[derive(Clone)] 91 | pub struct ClientStats { 92 | /// Server we're connected to 93 | pub server_url: String, 94 | /// whether we're connected 95 | pub connected: bool, 96 | /// Connection status 97 | pub connection_status: String, 98 | /// Last message sent to server 99 | pub last_message_sent: String, 100 | /// Last response/command received from server 101 | pub last_message_received: String, 102 | } 103 | 104 | impl Default for ClientStats { 105 | fn default() -> ClientStats { 106 | ClientStats { 107 | server_url: "".to_string(), 108 | connected: false, 109 | connection_status: "Connection Status: Starting".to_string(), 110 | last_message_sent: "Last Message Sent: None".to_string(), 111 | last_message_received: "Last Message Received: None".to_string(), 112 | } 113 | } 114 | } 115 | 116 | #[derive(Clone)] 117 | pub struct Stats { 118 | /// Client/networking stats 119 | pub client_stats: ClientStats, 120 | /// Mining stats 121 | pub mining_stats: MiningStats, 122 | } 123 | 124 | impl Default for Stats { 125 | fn default() -> Stats { 126 | Stats { 127 | client_stats: ClientStats::default(), 128 | mining_stats: MiningStats::default(), 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/bin/tui/constants.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Identifiers for various TUI elements, because they may be referenced 16 | //! from a few different places 17 | 18 | /// Mining View 19 | pub const VIEW_MINING: &str = "mining_view"; 20 | /// Mining status 21 | pub const TABLE_MINING_STATUS: &str = "mining_status_table"; 22 | 23 | // Mining View 24 | /// Version view 25 | pub const VIEW_VERSION: &str = "version_view"; 26 | 27 | // Menu and root elements 28 | /// Main menu 29 | pub const MAIN_MENU: &str = "main_menu"; 30 | /// root stack 31 | pub const ROOT_STACK: &str = "root_stack"; 32 | -------------------------------------------------------------------------------- /src/bin/tui/menu.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Main Menu definition 16 | 17 | use cursive::align::HAlign; 18 | use cursive::direction::Orientation; 19 | use cursive::event::Key; 20 | use cursive::view::Identifiable; 21 | use cursive::view::View; 22 | use cursive::views::{ 23 | LinearLayout, OnEventView, ResizedView, SelectView, StackView, TextView, ViewRef, 24 | }; 25 | use cursive::Cursive; 26 | 27 | use tui::constants::*; 28 | 29 | /// Create menu 30 | pub fn create() -> Box { 31 | let mut main_menu = SelectView::new().h_align(HAlign::Left).with_name(MAIN_MENU); 32 | main_menu.get_mut().add_item("Mining", VIEW_MINING); 33 | main_menu.get_mut().add_item("Version Info", VIEW_VERSION); 34 | let change_view = |s: &mut Cursive, v: &&str| { 35 | if *v == "" { 36 | return; 37 | } 38 | 39 | let _ = s.call_on_name(ROOT_STACK, |sv: &mut StackView| { 40 | let pos = sv.find_layer_from_name(v).unwrap(); 41 | sv.move_to_front(pos); 42 | }); 43 | }; 44 | 45 | main_menu.get_mut().set_on_select(change_view); 46 | let main_menu = OnEventView::new(main_menu) 47 | .on_pre_event('j', move |c| { 48 | let mut s: ViewRef> = c.find_name(MAIN_MENU).unwrap(); 49 | s.select_down(1)(c); 50 | }) 51 | .on_pre_event('k', move |c| { 52 | let mut s: ViewRef> = c.find_name(MAIN_MENU).unwrap(); 53 | s.select_up(1)(c); 54 | }) 55 | .on_pre_event(Key::Tab, move |c| { 56 | let mut s: ViewRef> = c.find_name(MAIN_MENU).unwrap(); 57 | if s.selected_id().unwrap() == s.len() - 1 { 58 | s.set_selection(0)(c); 59 | } else { 60 | s.select_down(1)(c); 61 | } 62 | }); 63 | let main_menu = LinearLayout::new(Orientation::Vertical) 64 | .child(ResizedView::with_full_height(main_menu)) 65 | .child(TextView::new("------------------")) 66 | .child(TextView::new("Tab/Arrow : Cycle ")) 67 | .child(TextView::new("Enter : Select")) 68 | .child(TextView::new("Q : Quit ")); 69 | Box::new(main_menu) 70 | } 71 | -------------------------------------------------------------------------------- /src/bin/tui/mining.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Mining status view definition 16 | 17 | use std::cmp::Ordering; 18 | use std::sync::{Arc, RwLock}; 19 | 20 | use cursive::direction::Orientation; 21 | use cursive::traits::*; 22 | use cursive::view::View; 23 | use cursive::views::{Dialog, LinearLayout, ResizedView, StackView, TextView}; 24 | use cursive::Cursive; 25 | 26 | use tui::constants::*; 27 | use tui::types::*; 28 | 29 | use plugin::SolverStats; 30 | use stats; 31 | use tui::table::{TableView, TableViewItem}; 32 | 33 | #[derive(Copy, Clone, PartialEq, Eq, Hash)] 34 | enum MiningDeviceColumn { 35 | Plugin, 36 | DeviceId, 37 | DeviceName, 38 | EdgeBits, 39 | ErrorStatus, 40 | LastGraphTime, 41 | GraphsPerSecond, 42 | } 43 | 44 | impl MiningDeviceColumn { 45 | fn _as_str(&self) -> &str { 46 | match *self { 47 | MiningDeviceColumn::Plugin => "Plugin", 48 | MiningDeviceColumn::DeviceId => "Device ID", 49 | MiningDeviceColumn::DeviceName => "Name", 50 | MiningDeviceColumn::EdgeBits => "Graph Size", 51 | MiningDeviceColumn::ErrorStatus => "Status", 52 | MiningDeviceColumn::LastGraphTime => "Last Graph Time", 53 | MiningDeviceColumn::GraphsPerSecond => "GPS", 54 | } 55 | } 56 | } 57 | 58 | impl TableViewItem for SolverStats { 59 | fn to_column(&self, column: MiningDeviceColumn) -> String { 60 | let last_solution_time_secs = self.last_solution_time as f64 / 1_000_000_000.0; 61 | match column { 62 | MiningDeviceColumn::Plugin => self.get_plugin_name(), 63 | MiningDeviceColumn::DeviceId => format!("{}", self.device_id), 64 | MiningDeviceColumn::DeviceName => self.get_device_name(), 65 | MiningDeviceColumn::EdgeBits => format!("{}", self.edge_bits), 66 | MiningDeviceColumn::ErrorStatus => { 67 | if self.has_errored { 68 | String::from("Errored") 69 | } else { 70 | String::from("OK") 71 | } 72 | } 73 | MiningDeviceColumn::LastGraphTime => format!("{}s", last_solution_time_secs), 74 | MiningDeviceColumn::GraphsPerSecond => { 75 | format!("{:.*}", 4, 1.0 / last_solution_time_secs) 76 | } 77 | } 78 | } 79 | 80 | fn cmp(&self, other: &Self, column: MiningDeviceColumn) -> Ordering 81 | where 82 | Self: Sized, 83 | { 84 | let last_solution_time_secs_self = self.last_solution_time as f64 / 1_000_000_000.0; 85 | let gps_self = 1.0 / last_solution_time_secs_self; 86 | let last_solution_time_secs_other = other.last_solution_time as f64 / 1_000_000_000.0; 87 | let gps_other = 1.0 / last_solution_time_secs_other; 88 | match column { 89 | MiningDeviceColumn::Plugin => self.plugin_name.cmp(&other.plugin_name), 90 | MiningDeviceColumn::DeviceId => self.device_id.cmp(&other.device_id), 91 | MiningDeviceColumn::DeviceName => self.device_name.cmp(&other.device_name), 92 | MiningDeviceColumn::EdgeBits => self.edge_bits.cmp(&other.edge_bits), 93 | MiningDeviceColumn::ErrorStatus => self.has_errored.cmp(&other.has_errored), 94 | MiningDeviceColumn::LastGraphTime => { 95 | self.last_solution_time.cmp(&other.last_solution_time) 96 | } 97 | MiningDeviceColumn::GraphsPerSecond => gps_self.partial_cmp(&gps_other).unwrap(), 98 | } 99 | } 100 | } 101 | 102 | /// Mining status view 103 | pub struct TUIMiningView; 104 | 105 | impl TUIStatusListener for TUIMiningView { 106 | /// Create the mining view 107 | fn create() -> Box { 108 | let table_view = TableView::::new() 109 | .column(MiningDeviceColumn::Plugin, "Plugin", |c| { 110 | c.width_percent(20) 111 | }) 112 | .column(MiningDeviceColumn::DeviceId, "Device ID", |c| { 113 | c.width_percent(5) 114 | }) 115 | .column(MiningDeviceColumn::DeviceName, "Device Name", |c| { 116 | c.width_percent(20) 117 | }) 118 | .column(MiningDeviceColumn::EdgeBits, "Size", |c| c.width_percent(5)) 119 | .column(MiningDeviceColumn::ErrorStatus, "Status", |c| { 120 | c.width_percent(8) 121 | }) 122 | .column(MiningDeviceColumn::LastGraphTime, "Graph Time", |c| { 123 | c.width_percent(10) 124 | }) 125 | .column(MiningDeviceColumn::GraphsPerSecond, "GPS", |c| { 126 | c.width_percent(10) 127 | }); 128 | 129 | let status_view = LinearLayout::new(Orientation::Vertical) 130 | .child(LinearLayout::new(Orientation::Horizontal).child( 131 | TextView::new("Connection Status: Starting...").with_name("mining_server_status"), 132 | )) 133 | .child( 134 | LinearLayout::new(Orientation::Horizontal) 135 | .child(TextView::new("Mining Status: ").with_name("mining_status")), 136 | ) 137 | .child( 138 | LinearLayout::new(Orientation::Horizontal) 139 | .child(TextView::new(" ").with_name("network_info")), 140 | ) 141 | .child( 142 | LinearLayout::new(Orientation::Horizontal) 143 | .child(TextView::new(" ").with_name("mining_statistics")), 144 | ) 145 | .child( 146 | LinearLayout::new(Orientation::Horizontal) 147 | .child(TextView::new("Last Message Sent: ").with_name("last_message_sent")), 148 | ) 149 | .child(LinearLayout::new(Orientation::Horizontal).child( 150 | TextView::new("Last Message Received: ").with_name("last_message_received"), 151 | )); 152 | 153 | let mining_device_view = LinearLayout::new(Orientation::Vertical) 154 | .child(status_view) 155 | .child(ResizedView::with_full_screen( 156 | Dialog::around(table_view.with_name(TABLE_MINING_STATUS).min_size((50, 20))) 157 | .title("Mining Devices"), 158 | )) 159 | .with_name("mining_device_view"); 160 | 161 | let view_stack = StackView::new() 162 | .layer(mining_device_view) 163 | .with_name("mining_stack_view"); 164 | 165 | let mining_view = LinearLayout::new(Orientation::Vertical).child(view_stack); 166 | 167 | Box::new(mining_view.with_name(VIEW_MINING)) 168 | } 169 | 170 | /// update 171 | fn update(c: &mut Cursive, stats: Arc>) { 172 | let (client_stats, mining_stats) = { 173 | let stats = stats.read().unwrap(); 174 | (stats.client_stats.clone(), stats.mining_stats.clone()) 175 | }; 176 | 177 | c.call_on_name("mining_server_status", |t: &mut TextView| { 178 | t.set_content(client_stats.connection_status.clone()); 179 | }); 180 | 181 | let (basic_mining_status, basic_network_info) = { 182 | if client_stats.connected { 183 | if mining_stats.combined_gps() == 0.0 { 184 | ( 185 | "Mining Status: Starting miner and awaiting first graph time..." 186 | .to_string(), 187 | " ".to_string(), 188 | ) 189 | } else { 190 | ( 191 | format!( 192 | "Mining Status: Mining at height {} at {:.*} GPS", 193 | mining_stats.block_height, 194 | 4, 195 | mining_stats.combined_gps() 196 | ), 197 | format!( 198 | "Cucka*oo* - Target Share Difficulty {}", 199 | mining_stats.target_difficulty.to_string() 200 | ), 201 | ) 202 | } 203 | } else { 204 | ( 205 | "Mining Status: Waiting for server".to_string(), 206 | " ".to_string(), 207 | ) 208 | } 209 | }; 210 | 211 | // device 212 | c.call_on_name("mining_status", |t: &mut TextView| { 213 | t.set_content(basic_mining_status); 214 | }); 215 | c.call_on_name("network_info", |t: &mut TextView| { 216 | t.set_content(basic_network_info); 217 | }); 218 | 219 | c.call_on_name("last_message_sent", |t: &mut TextView| { 220 | t.set_content(client_stats.last_message_sent.clone()); 221 | }); 222 | c.call_on_name("last_message_received", |t: &mut TextView| { 223 | t.set_content(client_stats.last_message_received.clone()); 224 | }); 225 | 226 | if mining_stats.solution_stats.num_solutions_found > 0 { 227 | let sol_stat = format!( 228 | "Solutions found: {}. Accepted: {}, Rejected: {}, Stale: {}, Blocks found: {}", 229 | mining_stats.solution_stats.num_solutions_found, 230 | mining_stats.solution_stats.num_shares_accepted, 231 | mining_stats.solution_stats.num_rejected, 232 | mining_stats.solution_stats.num_staled, 233 | mining_stats.solution_stats.num_blocks_found, 234 | ); 235 | c.call_on_name("mining_statistics", |t: &mut TextView| { 236 | t.set_content(sol_stat); 237 | }); 238 | } 239 | 240 | let _ = c.call_on_name( 241 | TABLE_MINING_STATUS, 242 | |t: &mut TableView| { 243 | t.set_items(mining_stats.device_stats); 244 | }, 245 | ); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/bin/tui/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Grin Miner TUI 16 | 17 | #![deny(non_upper_case_globals)] 18 | #![deny(non_camel_case_types)] 19 | #![deny(non_snake_case)] 20 | #![deny(unused_mut)] 21 | #![warn(missing_docs)] 22 | 23 | pub mod constants; 24 | pub mod menu; 25 | pub mod mining; 26 | pub mod table; 27 | pub mod types; 28 | pub mod ui; 29 | pub mod version; 30 | -------------------------------------------------------------------------------- /src/bin/tui/types.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Types specific to the UI module 16 | 17 | use std::sync::{Arc, RwLock}; 18 | 19 | use cursive::view::View; 20 | use cursive::Cursive; 21 | use stats::Stats; 22 | 23 | /// Main message struct to communicate between the UI and 24 | /// the main process 25 | pub enum UIMessage { 26 | /// Update mining status 27 | UpdateStatus(Arc>), 28 | } 29 | 30 | /// Trait for a UI element that recieves status update messages 31 | /// and updates itself 32 | 33 | pub trait TUIStatusListener { 34 | /// create the view, to return to the main UI controller 35 | fn create() -> Box; 36 | /// Update according to status update contents 37 | fn update(c: &mut Cursive, stats: Arc>); 38 | } 39 | -------------------------------------------------------------------------------- /src/bin/tui/ui.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Basic TUI to better output the overall system status and status 16 | //! of various subsystems 17 | 18 | use std::sync::{mpsc, Arc, RwLock}; 19 | use std::{self, thread}; 20 | use time; 21 | 22 | use cursive::direction::Orientation; 23 | use cursive::theme::BaseColor::*; 24 | use cursive::theme::Color::*; 25 | use cursive::theme::PaletteColor::*; 26 | use cursive::theme::{BaseColor, BorderStyle, Color, Theme}; 27 | use cursive::traits::*; 28 | use cursive::utils::markup::StyledString; 29 | use cursive::views::{BoxedView, LinearLayout, Panel, StackView, TextView}; 30 | use cursive::Cursive; 31 | 32 | use tui::constants::*; 33 | use tui::types::*; 34 | use tui::{menu, mining, version}; 35 | 36 | use stats; 37 | 38 | use built_info; 39 | 40 | /// Main UI 41 | pub struct UI { 42 | cursive: Cursive, 43 | ui_rx: mpsc::Receiver, 44 | ui_tx: mpsc::Sender, 45 | controller_tx: mpsc::Sender, 46 | } 47 | 48 | fn modify_theme(theme: &mut Theme) { 49 | theme.shadow = false; 50 | theme.borders = BorderStyle::Simple; 51 | theme.palette[Background] = Dark(Black); 52 | theme.palette[Shadow] = Dark(Black); 53 | theme.palette[View] = Dark(Black); 54 | theme.palette[Primary] = Dark(White); 55 | theme.palette[Highlight] = Dark(Cyan); 56 | theme.palette[HighlightInactive] = Dark(Blue); 57 | // also secondary, tertiary, TitlePrimary, TitleSecondary 58 | } 59 | 60 | impl UI { 61 | /// Create a new UI 62 | pub fn new(controller_tx: mpsc::Sender) -> UI { 63 | let (ui_tx, ui_rx) = mpsc::channel::(); 64 | let mut grin_ui = UI { 65 | cursive: Cursive::default(), 66 | ui_tx, 67 | ui_rx, 68 | controller_tx, 69 | }; 70 | 71 | // Create UI objects, etc 72 | let mining_view = mining::TUIMiningView::create(); 73 | let version_view = version::TUIVersionView::create(); 74 | 75 | let main_menu = menu::create(); 76 | 77 | let root_stack = StackView::new() 78 | .layer(version_view) 79 | .layer(mining_view) 80 | .with_name(ROOT_STACK); 81 | 82 | let mut title_string = StyledString::new(); 83 | title_string.append(StyledString::styled( 84 | format!("Grin Miner Version {}", built_info::PKG_VERSION), 85 | Color::Dark(BaseColor::Yellow), 86 | )); 87 | 88 | let main_layer = LinearLayout::new(Orientation::Vertical) 89 | .child(Panel::new(TextView::new(title_string))) 90 | .child( 91 | LinearLayout::new(Orientation::Horizontal) 92 | .child(Panel::new(BoxedView::new(main_menu))) 93 | .child(Panel::new(root_stack)), 94 | ); 95 | 96 | //set theme 97 | let mut theme = grin_ui.cursive.current_theme().clone(); 98 | modify_theme(&mut theme); 99 | grin_ui.cursive.set_theme(theme); 100 | grin_ui.cursive.add_layer(main_layer); 101 | 102 | // Configure a callback (shutdown, for the first test) 103 | let controller_tx_clone = grin_ui.controller_tx.clone(); 104 | grin_ui.cursive.add_global_callback('q', move |_| { 105 | controller_tx_clone 106 | .send(ControllerMessage::Shutdown) 107 | .unwrap(); 108 | }); 109 | grin_ui.cursive.set_fps(4); 110 | grin_ui 111 | } 112 | 113 | /// Step the UI by calling into Cursive's step function, then 114 | /// processing any UI messages 115 | pub fn step(&mut self) -> bool { 116 | if !self.cursive.is_running() { 117 | return false; 118 | } 119 | 120 | // Process any pending UI messages 121 | while let Some(message) = self.ui_rx.try_iter().next() { 122 | match message { 123 | UIMessage::UpdateStatus(update) => { 124 | mining::TUIMiningView::update(&mut self.cursive, update.clone()); 125 | version::TUIVersionView::update(&mut self.cursive, update.clone()); 126 | } 127 | } 128 | } 129 | 130 | // Step the UI 131 | self.cursive.step(); 132 | true 133 | } 134 | 135 | /// Stop the UI 136 | pub fn stop(&mut self) { 137 | self.cursive.quit(); 138 | } 139 | } 140 | 141 | /// Controller message 142 | 143 | pub struct Controller { 144 | rx: mpsc::Receiver, 145 | ui: UI, 146 | } 147 | 148 | /// Controller Message 149 | pub enum ControllerMessage { 150 | /// Shutdown 151 | Shutdown, 152 | } 153 | 154 | impl Controller { 155 | /// Create a new controller 156 | pub fn new() -> Result { 157 | let (tx, rx) = mpsc::channel::(); 158 | Ok(Controller { 159 | rx, 160 | ui: UI::new(tx), 161 | }) 162 | } 163 | /// Run the controller 164 | pub fn run(&mut self, stats: Arc>) { 165 | let stat_update_interval = 1; 166 | let mut next_stat_update = time::get_time().sec + stat_update_interval; 167 | while self.ui.step() { 168 | if let Some(message) = self.rx.try_iter().next() { 169 | match message { 170 | ControllerMessage::Shutdown => { 171 | self.ui.stop(); 172 | return; 173 | } 174 | } 175 | } 176 | if time::get_time().sec > next_stat_update { 177 | self.ui 178 | .ui_tx 179 | .send(UIMessage::UpdateStatus(stats.clone())) 180 | .unwrap(); 181 | next_stat_update = time::get_time().sec + stat_update_interval; 182 | } 183 | thread::sleep(std::time::Duration::from_millis(100)); 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/bin/tui/version.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Version and build info 16 | 17 | use std::sync::{Arc, RwLock}; 18 | 19 | use cursive::direction::Orientation; 20 | use cursive::traits::*; 21 | use cursive::view::View; 22 | use cursive::views::{LinearLayout, ResizedView, TextView}; 23 | use cursive::Cursive; 24 | 25 | use tui::constants::*; 26 | use tui::types::*; 27 | 28 | use info_strings; 29 | use stats::Stats; 30 | 31 | /// Version view 32 | pub struct TUIVersionView; 33 | 34 | impl TUIStatusListener for TUIVersionView { 35 | /// Create basic status view 36 | fn create() -> Box { 37 | let (basic_info, detailed_info, _) = info_strings(); 38 | let basic_status_view = ResizedView::with_full_screen( 39 | LinearLayout::new(Orientation::Vertical) 40 | .child(TextView::new(basic_info)) 41 | .child(TextView::new(" ")) 42 | .child(TextView::new(detailed_info)), 43 | ); 44 | Box::new(basic_status_view.with_name(VIEW_VERSION)) 45 | } 46 | 47 | /// update 48 | fn update(_c: &mut Cursive, _stats: Arc>) {} 49 | } 50 | -------------------------------------------------------------------------------- /src/bin/types.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use serde_json::Value; 16 | 17 | /// Types used for stratum 18 | 19 | #[derive(Serialize, Deserialize, Debug)] 20 | pub struct JobTemplate { 21 | pub height: u64, 22 | pub job_id: u64, 23 | pub difficulty: u64, 24 | pub pre_pow: String, 25 | } 26 | 27 | #[derive(Serialize, Deserialize, Debug)] 28 | pub struct RpcRequest { 29 | pub id: String, 30 | pub jsonrpc: String, 31 | pub method: String, 32 | pub params: Option, 33 | } 34 | 35 | #[derive(Serialize, Deserialize, Debug)] 36 | pub struct RpcResponse { 37 | pub id: String, 38 | pub method: String, 39 | pub jsonrpc: String, 40 | pub result: Option, 41 | pub error: Option, 42 | } 43 | 44 | #[derive(Serialize, Deserialize, Debug)] 45 | pub struct RpcError { 46 | pub code: i32, 47 | pub message: String, 48 | } 49 | 50 | #[derive(Serialize, Deserialize, Debug)] 51 | pub struct LoginParams { 52 | pub login: String, 53 | pub pass: String, 54 | pub agent: String, 55 | } 56 | 57 | #[derive(Serialize, Deserialize, Debug)] 58 | pub struct SubmitParams { 59 | pub height: u64, 60 | pub job_id: u64, 61 | pub edge_bits: u32, 62 | pub nonce: u64, 63 | pub pow: Vec, 64 | } 65 | 66 | #[derive(Serialize, Deserialize, Debug)] 67 | pub struct WorkerStatus { 68 | pub id: String, 69 | pub height: u64, 70 | pub difficulty: u64, 71 | pub accepted: u64, 72 | pub rejected: u64, 73 | pub stale: u64, 74 | } 75 | 76 | /// Types used for internal communication from stratum client to miner 77 | #[derive(Serialize, Deserialize, Debug)] 78 | pub enum MinerMessage { 79 | // Height, difficulty, pre_pow 80 | ReceivedJob(u64, u64, u64, String), 81 | StopJob, 82 | Shutdown, 83 | } 84 | 85 | #[derive(Serialize, Deserialize, Debug)] 86 | pub enum ClientMessage { 87 | // height, job_id, edge_bits, nonce, pow 88 | FoundSolution(u64, u64, u32, u64, Vec), 89 | Shutdown, 90 | } 91 | -------------------------------------------------------------------------------- /src/build/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Build hooks to spit out version+build time info 16 | 17 | extern crate built; 18 | 19 | fn main() { 20 | let mut opts = built::Options::default(); 21 | opts.set_dependencies(true); 22 | let manifest_location = std::env::var("CARGO_MANIFEST_DIR").unwrap(); 23 | let dst = std::path::Path::new(&std::env::var("OUT_DIR").unwrap()).join("built.rs"); 24 | built::write_built_file_with_opts(&opts, manifest_location.as_ref(), &dst) 25 | .expect("Failed to acquire build-time information"); 26 | } 27 | -------------------------------------------------------------------------------- /util/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "grin_miner_util" 3 | version = "4.0.0" 4 | authors = ["Grin Developers "] 5 | description = "Utilities for the grin miner" 6 | repository = "https://github.com/mimblewimble/grin-miner" 7 | license = "Apache-2.0" 8 | workspace = ".." 9 | 10 | [dependencies] 11 | backtrace = "0.3" 12 | byteorder = "1" 13 | lazy_static = "1" 14 | rand = "0.7" 15 | serde = "1" 16 | serde_derive = "1" 17 | slog = { version = "2", features = ["max_level_trace", "release_max_level_trace"] } 18 | slog-term = "2" 19 | slog-async = "2" 20 | -------------------------------------------------------------------------------- /util/src/hex.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Implements hex-encoding from bytes to string and decoding of strings 16 | /// to bytes. Given that rustc-serialize is deprecated and serde doesn't 17 | /// provide easy hex encoding, hex is a bit in limbo right now in Rust- 18 | /// land. It's simple enough that we can just have our own. 19 | use std::fmt::Write; 20 | use std::num; 21 | 22 | /// Encode the provided bytes into a hex string 23 | pub fn to_hex(bytes: Vec) -> String { 24 | let mut s = String::new(); 25 | for byte in bytes { 26 | write!(&mut s, "{:02x}", byte).expect("Unable to write"); 27 | } 28 | s 29 | } 30 | 31 | /// Decode a hex string into bytes. 32 | pub fn from_hex(hex_str: String) -> Result, num::ParseIntError> { 33 | let hex_trim = if &hex_str[..2] == "0x" { 34 | hex_str[2..].to_owned() 35 | } else { 36 | hex_str 37 | }; 38 | split_n(&hex_trim.trim()[..], 2) 39 | .iter() 40 | .map(|b| u8::from_str_radix(b, 16)) 41 | .collect::, _>>() 42 | } 43 | 44 | fn split_n(s: &str, n: usize) -> Vec<&str> { 45 | (0..(s.len() - n + 1) / 2 + 1) 46 | .map(|i| &s[2 * i..2 * i + n]) 47 | .collect() 48 | } 49 | 50 | #[cfg(test)] 51 | mod test { 52 | use super::*; 53 | 54 | #[test] 55 | fn test_to_hex() { 56 | assert_eq!(to_hex(vec![0, 0, 0, 0]), "00000000"); 57 | assert_eq!(to_hex(vec![10, 11, 12, 13]), "0a0b0c0d"); 58 | assert_eq!(to_hex(vec![0, 0, 0, 255]), "000000ff"); 59 | } 60 | 61 | #[test] 62 | fn test_from_hex() { 63 | assert_eq!(from_hex("00000000".to_string()).unwrap(), vec![0, 0, 0, 0]); 64 | assert_eq!( 65 | from_hex("0a0b0c0d".to_string()).unwrap(), 66 | vec![10, 11, 12, 13] 67 | ); 68 | assert_eq!( 69 | from_hex("000000ff".to_string()).unwrap(), 70 | vec![0, 0, 0, 255] 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /util/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Logging, as well as various low-level utilities that factor Rust 16 | //! patterns that are frequent within the grin codebase. 17 | 18 | #![deny(non_upper_case_globals)] 19 | #![deny(non_camel_case_types)] 20 | #![deny(non_snake_case)] 21 | #![deny(unused_mut)] 22 | #![warn(missing_docs)] 23 | 24 | extern crate backtrace; 25 | extern crate byteorder; 26 | extern crate rand; 27 | #[macro_use] 28 | extern crate slog; 29 | extern crate slog_async; 30 | extern crate slog_term; 31 | 32 | #[macro_use] 33 | extern crate lazy_static; 34 | 35 | extern crate serde; 36 | #[macro_use] 37 | extern crate serde_derive; 38 | 39 | // Logging related 40 | pub mod logger; 41 | pub use logger::{init_logger, init_test_logger, LOGGER}; 42 | 43 | pub mod types; 44 | pub use types::{LogLevel, LoggingConfig}; 45 | 46 | // other utils 47 | #[allow(unused_imports)] 48 | use std::ops::Deref; 49 | 50 | mod hex; 51 | pub use hex::*; 52 | -------------------------------------------------------------------------------- /util/src/logger.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //! Logging wrapper to be used throughout all crates in the workspace 15 | use slog::{Discard, Drain, Duplicate, Level, LevelFilter, Logger}; 16 | use slog_async; 17 | use slog_term; 18 | use std::fs::OpenOptions; 19 | use std::ops::Deref; 20 | use std::sync::Mutex; 21 | 22 | use backtrace::Backtrace; 23 | use std::{panic, thread}; 24 | 25 | use types::{LogLevel, LoggingConfig}; 26 | 27 | fn convert_log_level(in_level: &LogLevel) -> Level { 28 | match *in_level { 29 | LogLevel::Info => Level::Info, 30 | LogLevel::Critical => Level::Critical, 31 | LogLevel::Warning => Level::Warning, 32 | LogLevel::Debug => Level::Debug, 33 | LogLevel::Trace => Level::Trace, 34 | LogLevel::Error => Level::Error, 35 | } 36 | } 37 | 38 | lazy_static! { 39 | /// Flag to observe whether logging was explicitly initialised (don't output otherwise) 40 | static ref WAS_INIT: Mutex = Mutex::new(false); 41 | /// Flag to observe whether tui is running, and we therefore don't want to attempt to write 42 | /// panics to stdout 43 | static ref TUI_RUNNING: Mutex = Mutex::new(false); 44 | /// Static Logging configuration, should only be set once, before first logging call 45 | static ref LOGGING_CONFIG: Mutex = Mutex::new(LoggingConfig::default()); 46 | 47 | /// And a static reference to the logger itself, accessible from all crates 48 | pub static ref LOGGER: Logger = { 49 | let was_init = *WAS_INIT.lock().unwrap(); 50 | let config = LOGGING_CONFIG.lock().unwrap(); 51 | let slog_level_stdout = convert_log_level(&config.stdout_log_level); 52 | let slog_level_file = convert_log_level(&config.file_log_level); 53 | if config.tui_running.is_some() && config.tui_running.unwrap() { 54 | let mut tui_running_ref = TUI_RUNNING.lock().unwrap(); 55 | *tui_running_ref = true; 56 | } 57 | 58 | //Terminal output drain 59 | let terminal_decorator = slog_term::TermDecorator::new().build(); 60 | let terminal_drain = slog_term::FullFormat::new(terminal_decorator).build().fuse(); 61 | let terminal_drain = LevelFilter::new(terminal_drain, slog_level_stdout).fuse(); 62 | let mut terminal_drain = slog_async::Async::new(terminal_drain).build().fuse(); 63 | if !config.log_to_stdout || !was_init { 64 | terminal_drain = slog_async::Async::new(Discard{}).build().fuse(); 65 | } 66 | 67 | //File drain 68 | let mut file_drain_final = slog_async::Async::new(Discard{}).build().fuse(); 69 | if config.log_to_file && was_init { 70 | let file = OpenOptions::new() 71 | .create(true) 72 | .write(true) 73 | .append(config.log_file_append) 74 | .truncate(!config.log_file_append) 75 | .open(&config.log_file_path) 76 | .unwrap(); 77 | 78 | let file_decorator = slog_term::PlainDecorator::new(file); 79 | let file_drain = slog_term::FullFormat::new(file_decorator).build().fuse(); 80 | let file_drain = LevelFilter::new(file_drain, slog_level_file).fuse(); 81 | file_drain_final = slog_async::Async::new(file_drain).build().fuse(); 82 | } 83 | 84 | //Compose file and terminal drains 85 | let composite_drain = Duplicate::new(terminal_drain, file_drain_final).fuse(); 86 | 87 | Logger::root(composite_drain, o!()) 88 | }; 89 | } 90 | 91 | /// Initialises the logger with the given configuration 92 | pub fn init_logger(config: Option) { 93 | if let Some(c) = config { 94 | let mut config_ref = LOGGING_CONFIG.lock().unwrap(); 95 | *config_ref = c; 96 | // Logger configuration successfully injected into LOGGING_CONFIG... 97 | let mut was_init_ref = WAS_INIT.lock().unwrap(); 98 | *was_init_ref = true; 99 | // .. allow logging, having ensured that paths etc are immutable 100 | } 101 | send_panic_to_log(); 102 | } 103 | 104 | /// Initializes the logger for unit and integration tests 105 | pub fn init_test_logger() { 106 | let mut was_init_ref = WAS_INIT.lock().unwrap(); 107 | if *was_init_ref.deref() { 108 | return; 109 | } 110 | let mut config_ref = LOGGING_CONFIG.lock().unwrap(); 111 | *config_ref = LoggingConfig::default(); 112 | *was_init_ref = true; 113 | send_panic_to_log(); 114 | } 115 | 116 | /// hook to send panics to logs as well as stderr 117 | fn send_panic_to_log() { 118 | panic::set_hook(Box::new(|info| { 119 | let backtrace = Backtrace::new(); 120 | 121 | let thread = thread::current(); 122 | let thread = thread.name().unwrap_or("unnamed"); 123 | 124 | let msg = match info.payload().downcast_ref::<&'static str>() { 125 | Some(s) => *s, 126 | None => match info.payload().downcast_ref::() { 127 | Some(s) => &**s, 128 | None => "Box", 129 | }, 130 | }; 131 | 132 | match info.location() { 133 | Some(location) => { 134 | error!( 135 | LOGGER, 136 | "\nthread '{}' panicked at '{}': {}:{}{:?}\n\n", 137 | thread, 138 | msg, 139 | location.file(), 140 | location.line(), 141 | backtrace 142 | ); 143 | } 144 | None => error!( 145 | LOGGER, 146 | "thread '{}' panicked at '{}'{:?}", thread, msg, backtrace 147 | ), 148 | } 149 | //also print to stderr 150 | let tui_running = *TUI_RUNNING.lock().unwrap(); 151 | if !tui_running { 152 | eprintln!( 153 | "Thread '{}' panicked with message:\n\"{}\"\nSee grin.log for further details.", 154 | thread, msg 155 | ); 156 | } 157 | })); 158 | } 159 | -------------------------------------------------------------------------------- /util/src/types.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Grin Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Logging configuration types 16 | 17 | /// Log level types, as slog's don't implement serialize 18 | #[derive(Debug, Clone, Serialize, Deserialize)] 19 | pub enum LogLevel { 20 | /// Critical 21 | Critical, 22 | /// Error 23 | Error, 24 | /// Warning 25 | Warning, 26 | /// Info 27 | Info, 28 | /// Debug 29 | Debug, 30 | /// Trace 31 | Trace, 32 | } 33 | 34 | /// Logging config 35 | #[derive(Debug, Clone, Serialize, Deserialize)] 36 | pub struct LoggingConfig { 37 | /// whether to log to stdout 38 | pub log_to_stdout: bool, 39 | /// logging level for stdout 40 | pub stdout_log_level: LogLevel, 41 | /// whether to log to file 42 | pub log_to_file: bool, 43 | /// log file level 44 | pub file_log_level: LogLevel, 45 | /// Log file path 46 | pub log_file_path: String, 47 | /// Whether to append to log or replace 48 | pub log_file_append: bool, 49 | /// Whether the tui is running (optional) 50 | pub tui_running: Option, 51 | } 52 | 53 | impl Default for LoggingConfig { 54 | fn default() -> LoggingConfig { 55 | LoggingConfig { 56 | log_to_stdout: true, 57 | stdout_log_level: LogLevel::Debug, 58 | log_to_file: false, 59 | file_log_level: LogLevel::Trace, 60 | log_file_path: String::from("grin.log"), 61 | log_file_append: false, 62 | tui_running: None, 63 | } 64 | } 65 | } 66 | --------------------------------------------------------------------------------