├── .gitignore ├── .gitmodules ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── doc └── build.md ├── rustfmt.toml ├── src ├── build.rs ├── cuckoo_sys │ ├── manager.rs │ ├── mod.rs │ └── plugins │ │ ├── CMakeLists.txt │ │ └── cmake │ │ ├── CudaComputeTargetFlags.cmake │ │ └── find_cuda.cmake ├── error │ ├── error.rs │ └── mod.rs ├── lib.rs ├── manager │ ├── manager.rs │ └── mod.rs ├── miner │ ├── delegator.rs │ ├── miner.rs │ └── mod.rs └── sanity.rs └── tests ├── async_mode.rs ├── common └── mod.rs ├── performance.rs ├── plugins.rs └── sync_mode.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | #VS code files 13 | launch.json 14 | settings.json 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/cuckoo_sys/plugins/cuckoo"] 2 | path = src/cuckoo_sys/plugins/cuckoo 3 | url = https://github.com/mimblewimble/cuckoo 4 | branch = cuckoo-miner-mods 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | dist: trusty 4 | sudo: true 5 | rust: 6 | - stable 7 | 8 | addons: 9 | apt: 10 | sources: 11 | - ubuntu-toolchain-r-test 12 | packages: 13 | - g++-5 14 | - cmake 15 | 16 | env: 17 | global: 18 | - RUST_BACKTRACE="1" 19 | matrix: 20 | - TEST_DIR=. 21 | 22 | script: 23 | - RUST_TEST_THREADS=1 cargo test --verbose on_commit 24 | - cargo test --doc 25 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cuckoo_miner" 3 | version = "0.4.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 | readme = "README.md" 8 | build = "src/build.rs" 9 | 10 | [features] 11 | default = [] 12 | #feature to allow turing off plugin builds 13 | no-plugin-build = [] 14 | #feature which defines whether to build cuda libs 15 | build-cuda-plugins = [] 16 | 17 | [dependencies] 18 | time = "^0.1" 19 | env_logger="^0.3.5" 20 | rust-crypto = "0.2.36" 21 | log = "^0.3" 22 | rand = "^0.3.16" 23 | byteorder = "^0.5" 24 | blake2-rfc = "~0.2.17" 25 | regex = "~0.2.2" 26 | glob = "0.2.11" 27 | serde = "~1.0.8" 28 | serde_derive = "~1.0.8" 29 | serde_json = "~1.0.2" 30 | libc = "0.2.24" 31 | libloading = "0.4.1" 32 | 33 | [dev-dependencies] 34 | const-cstr = "0.2" 35 | 36 | [build-dependencies] 37 | cmake = "0.1.24" 38 | fs_extra = "1.1.0" 39 | -------------------------------------------------------------------------------- /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 | **This repository is now integrated directly into [grin-miner](https://github.com/mimblewimble/grin-miner) and obsolete** 2 | 3 | [![Build Status](https://travis-ci.org/mimblewimble/cuckoo-miner.svg?branch=master)](https://travis-ci.org/mimblewimble/cuckoo-miner) [![Gitter chat](https://badges.gitter.im/grin_community/Lobby.png)](https://gitter.im/grin_community/Lobby) 4 | # cuckoo-miner 5 | 6 | Cuckoo-miner is Rust crate which provides various implementations of the Cuckoo Cycle algorithm. It is primarily intended for 7 | integration into the Grin/Mimblewimble blockchain's proof-of-work system, however it aims to eventually be suitable for other 8 | purposes, such as for the creation of a standalone Cuckoo Cycle mining client or integration into other blockchains. 9 | 10 | Cuckoo-miner uses a plugin architecture, and provides a common interface for plugins wishing to provide a Cuckoo Cycle 11 | implementation. Full details of this plugin interface can be found in the crate's API documentation, but a plugin is 12 | essentially a DLL that (currently) provides implementations of several functions, for example: 13 | 14 | * call_cuckoo - Synchronous function Which accepts an array of bytes to use as a seed for the Cuckoo Cycle algorithm, and returns a solution set, 15 | if found. 16 | 17 | * cuckoo_start_processing - starts asyncronous processing, reading hashes from a queue and returning any results found to an output queue. 18 | 19 | * cuckoo_description - Which provides details about the plugin's capabilities, such as it's name, cuckoo size, description, 20 | and will be expanded to include details such as whether a plugin can be run on a host system. 21 | 22 | Cuckoo-miner can be run in either of two modes. Syncronous mode takes a single hash, searches it via the cuckoo cycle algorithm in the loaded 23 | plugin, and returns a result. Asynchronous mode, based on a Stratum-esque notifiy function, takes the required parts of a block header, and mutates 24 | a hash of the header with random nonces until it finds a solution. This is performed asyncronously by the loaded plugin, which reads hashes 25 | from a thread-safe queue and returns results on another until told to stop. 26 | 27 | The main interface into cuckoo-miner is the 'CuckooMiner' struct, which handles selection of a plugin and running the selected 28 | Cuckoo Cycle implementation at a high level. Cuckoo-miner also provides a helper 'CuckooPluginManager' struct, which assists with loading a 29 | directory full of mining plugins, returning useful information to the caller about each available plugin. Full details 30 | are found in the crate's documentation. 31 | 32 | ## Plugins 33 | 34 | Currently, cuckoo-miner provides a set of pre-built plugins directly adapted from the latest implementations in 35 | [John Tromp's github repository](https://github.com/tromp/cuckoo), with each plugin optimised for different sizes of Cuckoo Graph. 36 | Currently, the provided (and planned plugins are:) 37 | 38 | * lean_cpu (cuckoo_miner.cpp) (Cuckoo Sizes 16 and 30), the baseline CPU algorithm, which constrains memory use at the expense of speed 39 | * mean_cpu (matrix_miner.cpp) (Cuckoo size 30), currently the fastest CPU solver, but with much larger memory requirements 40 | * lean_cuda (cuda_miner.cu) (Cuckoo Size 30), lean cuckoo algorithm optimised for NVidia GPUs, (only built if cuda build environment is installed) 41 | * mean_cuda (PLANNED) (Cuckoo Size 30) cuda version of the mean algorithm, should be the fastest solver when implemented 42 | 43 | These plugins are currently built by cmake as part of the cuckoo-sys module. The cmake scripts will attempt to detect the underlying environment 44 | as well as possible and build plugins accordingly (WIP) 45 | 46 | ## Installation and Building 47 | 48 | A tag of cuckoo miner is intergrated into the master of Grin, but for instructions on how to build cuckoo-miner and integrate it into 49 | Grin locally, see the see the [build docs](doc/build.md). 50 | 51 | ## Architecture 52 | 53 | The reasoning behind the plugin architecture are several fold. John Tromp's implementations are likely to remain the fastest 54 | and most robust Cuckoo Cycle implementations for quite some time, and it was desirable to come up a method of exposing them 55 | to Grin in a manner that changes them as little as possible. As written, they are optimised with a lot of static 56 | array initialisation and intrinsics in some cases, and a development of a dynamic version of them would incur tradeoffs 57 | and likely be far slower. The 'plugins' included in cuckoo-miner are mostly redefinitions of the original main functions 58 | exposed as DLL symbols, and thus change them as little as possible. This approach also allows for quick and painless 59 | merging of future updates to the various implementations. 60 | 61 | Further, the inclusion of intrisics mean that some variants of the algorithm may not run 62 | on certain architectures, and a plugin-architecture which can query the host system for its capabilites is desirable. 63 | 64 | ## Status 65 | 66 | Cuckoo-miner is very much in experimental alpha phase, and will be developed more fully alongside Grin. The many 67 | variations on cuckoo size included are mostly for Grin testing, and will likely be reduced once Grin's POW parameters 68 | become more established. 69 | 70 | ## Further Reading 71 | 72 | The Cuckoo Cycle POW algorithm is the work of John Tromp, and the most up-to-date documentation and implementations 73 | can be found in [his github repository](https://github.com/tromp/cuckoo). The 74 | [white paper](https://github.com/tromp/cuckoo/blob/master/doc/cuckoo.pdf) is the best source of 75 | further technical details. 76 | 77 | A higher level overview of Cuckoo Cycle and how it relates to Grin's Proof-of-work system can be found in 78 | [Grin's POW Documentation](https://github.com/ignopeverell/grin/blob/master/doc/pow/pow.md). 79 | 80 | -------------------------------------------------------------------------------- /doc/build.md: -------------------------------------------------------------------------------- 1 | # Build and Installation 2 | 3 | ### Platforms 4 | 5 | The plugins currently only build on linux and OSX. The library will compile under Windows, but no plugins will be available. 6 | 7 | ### cmake 8 | 9 | cmake is a build requirement for the included plugins 10 | 11 | ### output 12 | 13 | All build plugins will be placed into ${OUT_DIR}/plugins, e.g. target/debug/plugins 14 | 15 | ## Integration into Grin 16 | 17 | Cuckoo miner is integrated into grin, and can be turned on and off via grin's grin.toml file. All options are documented 18 | within the configuration file. 19 | 20 | ## Building Cuckoo CUDA Libraries (Highly experimental, of course) 21 | 22 | If the cuda build environment is installed, the build will attempt to build CUDA versions of the plugin. If they work for you, 23 | they should give the best known solution times, with cuckoo30 generally finding a solution within a couple of seconds 24 | (on a 980ti, at least). 25 | 26 | Instructions on how to set up the nvcc tool chain won't be provided here, but this will generally be installed 27 | as part of a 'cuda' package on your distribution, and obviously depends on the correct nvidia driver package 28 | being installed as well. 29 | 30 | Once the libraries are built, you can experiment with calling them via the unit tests, or 31 | experiment with dropping them into grin's target/debug/deps directory, and calling them by modifying 32 | grin.toml as directed in the configuration file's documentation. 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = true 2 | wrap_comments = true 3 | write_mode = "Overwrite" 4 | -------------------------------------------------------------------------------- /src/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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 | mod sanity; 21 | 22 | use cmake::Config; 23 | use std::{env, fs}; 24 | use std::path::PathBuf; 25 | use fs_extra::dir::*; 26 | use sanity::Finder; 27 | 28 | 29 | #[cfg(feature = "build-cuda-plugins")] 30 | const BUILD_CUDA_PLUGINS:&str="TRUE"; 31 | #[cfg(not(feature = "build-cuda-plugins"))] 32 | const BUILD_CUDA_PLUGINS:&str="FALSE"; 33 | 34 | /// Tests whether source cuckoo directory exists 35 | 36 | pub fn fail_on_empty_directory(name: &str) { 37 | if fs::read_dir(name).unwrap().count() == 0 { 38 | println!( 39 | "The `{}` directory is empty. Did you forget to pull the submodules?", 40 | name 41 | ); 42 | println!("Try `git submodule update --init --recursive`"); 43 | panic!(); 44 | } 45 | } 46 | 47 | fn main() { 48 | #[cfg(feature = "no-plugin-build")] 49 | return; 50 | let mut command_finder = Finder::new(); 51 | // dumb and quick test for windows, can parse later 52 | let windows_sysinfo = command_finder.maybe_have("systeminfo"); 53 | if let Some(_) = windows_sysinfo { 54 | // Windows plugins not supported for now.. bye! 55 | return; 56 | } 57 | 58 | 59 | fail_on_empty_directory("src/cuckoo_sys/plugins/cuckoo"); 60 | let path_str = env::var("OUT_DIR").unwrap(); 61 | let mut out_path = PathBuf::from(&path_str); 62 | out_path.pop(); 63 | out_path.pop(); 64 | out_path.pop(); 65 | let mut plugin_path = PathBuf::from(&path_str); 66 | plugin_path.push("build"); 67 | plugin_path.push("plugins"); 68 | // Collect the files and directories we care about 69 | let p = PathBuf::from("src/cuckoo_sys/plugins"); 70 | let dir_content = match get_dir_content(p) { 71 | Ok(c) => c, 72 | Err(e) => panic!("Error getting directory content: {}", e), 73 | }; 74 | for d in dir_content.directories { 75 | let file_content = get_dir_content(d).unwrap(); 76 | for f in file_content.files { 77 | println!("cargo:rerun-if-changed={}", f); 78 | } 79 | } 80 | for f in dir_content.files { 81 | println!("cargo:rerun-if-changed={}", f); 82 | } 83 | 84 | let dst = Config::new("src/cuckoo_sys/plugins") 85 | .define("BUILD_CUDA_PLUGINS",BUILD_CUDA_PLUGINS) //whatever flags go here 86 | //.cflag("-foo") //and here 87 | .build_target("") 88 | .build(); 89 | 90 | println!("Plugin path: {:?}", plugin_path); 91 | println!("OUT PATH: {:?}", out_path); 92 | let mut options = CopyOptions::new(); 93 | options.overwrite = true; 94 | if let Err(e) = copy(plugin_path, out_path, &options) { 95 | println!("{:?}", e); 96 | } 97 | 98 | println!("cargo:rustc-link-search=native={}", dst.display()); 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/cuckoo_sys/manager.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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 | //! Note that plugins are shared libraries, not objects. You can have multiple 22 | //! instances of a PluginLibrary, but all of them will reference the same 23 | //! loaded code. Plugins aren't threadsafe, so only one thread should ever 24 | //! be calling a particular plugin at a time. 25 | 26 | use std::sync::Mutex; 27 | 28 | use libloading; 29 | use libc::*; 30 | 31 | use error::error::CuckooMinerError; 32 | 33 | // PRIVATE MEMBERS 34 | 35 | // Type definitions corresponding to each function that the plugin implements 36 | 37 | type CuckooInit = unsafe extern "C" fn(); 38 | type CuckooCall = unsafe extern "C" fn(*const c_uchar, uint32_t, *mut uint32_t, *mut uint32_t) -> uint32_t; 39 | type CuckooParameterList = unsafe extern "C" fn(*mut c_uchar, *mut uint32_t) -> uint32_t; 40 | type CuckooSetParameter = unsafe extern "C" fn(*const c_uchar, uint32_t, uint32_t, uint32_t) -> uint32_t; 41 | type CuckooGetParameter = unsafe extern "C" fn(*const c_uchar, uint32_t, uint32_t, *mut uint32_t) -> uint32_t; 42 | type CuckooIsQueueUnderLimit = unsafe extern "C" fn() -> uint32_t; 43 | type CuckooPushToInputQueue = unsafe extern "C" fn(uint32_t, *const c_uchar, uint32_t, *const c_uchar) 44 | -> uint32_t; 45 | type CuckooReadFromOutputQueue = unsafe extern "C" fn(*mut uint32_t, *mut uint32_t, *mut uint32_t, *mut c_uchar) -> uint32_t; 46 | type CuckooClearQueues = unsafe extern "C" fn(); 47 | type CuckooStartProcessing = unsafe extern "C" fn() -> uint32_t; 48 | type CuckooStopProcessing = unsafe extern "C" fn() -> uint32_t; 49 | type CuckooResetProcessing = unsafe extern "C" fn() -> uint32_t; 50 | type CuckooHasProcessingStopped = unsafe extern "C" fn() -> uint32_t; 51 | type CuckooGetStats = unsafe extern "C" fn(*mut c_uchar, *mut uint32_t) -> uint32_t; 52 | 53 | /// Struct to hold instances of loaded plugins 54 | 55 | pub struct PluginLibrary { 56 | ///The full file path to the plugin loaded by this instance 57 | pub lib_full_path: String, 58 | 59 | loaded_library: Mutex, 60 | cuckoo_init: Mutex, 61 | cuckoo_call: Mutex, 62 | cuckoo_parameter_list: Mutex, 63 | cuckoo_get_parameter: Mutex, 64 | cuckoo_set_parameter: Mutex, 65 | cuckoo_is_queue_under_limit: Mutex, 66 | cuckoo_clear_queues: Mutex, 67 | cuckoo_push_to_input_queue: Mutex, 68 | cuckoo_read_from_output_queue: Mutex, 69 | cuckoo_start_processing: Mutex, 70 | cuckoo_stop_processing: Mutex, 71 | cuckoo_reset_processing: Mutex, 72 | cuckoo_has_processing_stopped: Mutex, 73 | cuckoo_get_stats: Mutex, 74 | } 75 | 76 | impl PluginLibrary { 77 | //Loads the library at the specified path 78 | 79 | /// #Description 80 | /// 81 | /// Loads the specified library, readying it for use 82 | /// via the exposed wrapper functions. A plugin can be 83 | /// loaded into multiple PluginLibrary instances, however 84 | /// they will all reference the same loaded library. One 85 | /// should only exist per library in a given thread. 86 | /// 87 | /// #Arguments 88 | /// 89 | /// * `lib_full_path` The full path to the library that is 90 | /// to be loaded. 91 | /// 92 | /// #Returns 93 | /// 94 | /// * `Ok()` is the library was successfully loaded. 95 | /// * a [CuckooMinerError](enum.CuckooMinerError.html) 96 | /// with specific detail if an error was encountered. 97 | /// 98 | /// #Example 99 | /// 100 | /// ``` 101 | /// # use cuckoo_miner::PluginLibrary; 102 | /// # use std::env; 103 | /// # use std::path::PathBuf; 104 | /// # static DLL_SUFFIX: &str = ".cuckooplugin"; 105 | /// # let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 106 | /// # d.push(format!("./target/debug/plugins/lean_cpu_16{}", DLL_SUFFIX).as_str()); 107 | /// # let plugin_path = d.to_str().unwrap(); 108 | /// let pl=PluginLibrary::new(plugin_path).unwrap(); 109 | /// pl.call_cuckoo_init(); 110 | /// ``` 111 | /// 112 | 113 | pub fn new(lib_full_path: &str) -> Result { 114 | debug!("Loading miner plugin: {}", &lib_full_path); 115 | 116 | let result = libloading::Library::new(lib_full_path); 117 | 118 | if let Err(e) = result { 119 | return Err(CuckooMinerError::PluginNotFoundError( 120 | String::from(format!("{} - {:?}", lib_full_path, e)), 121 | )); 122 | } 123 | 124 | let loaded_library = result.unwrap(); 125 | PluginLibrary::load_symbols(loaded_library, lib_full_path) 126 | } 127 | 128 | fn load_symbols( 129 | loaded_library: libloading::Library, 130 | path: &str 131 | ) -> Result { 132 | unsafe { 133 | let ret_val = PluginLibrary { 134 | lib_full_path: String::from(path), 135 | cuckoo_init: { 136 | let cuckoo_init: libloading::Symbol = 137 | loaded_library.get(b"cuckoo_init\0").unwrap(); 138 | Mutex::new(*cuckoo_init.into_raw()) 139 | }, 140 | 141 | cuckoo_call: { 142 | let cuckoo_call: libloading::Symbol = 143 | loaded_library.get(b"cuckoo_call\0").unwrap(); 144 | Mutex::new(*cuckoo_call.into_raw()) 145 | }, 146 | 147 | cuckoo_parameter_list: { 148 | let cuckoo_parameter_list:libloading::Symbol = 149 | loaded_library.get(b"cuckoo_parameter_list\0").unwrap(); 150 | Mutex::new(*cuckoo_parameter_list.into_raw()) 151 | }, 152 | 153 | cuckoo_get_parameter: { 154 | let cuckoo_get_parameter:libloading::Symbol = 155 | loaded_library.get(b"cuckoo_get_parameter\0").unwrap(); 156 | Mutex::new(*cuckoo_get_parameter.into_raw()) 157 | }, 158 | 159 | cuckoo_set_parameter: { 160 | let cuckoo_set_parameter:libloading::Symbol = 161 | loaded_library.get(b"cuckoo_set_parameter\0").unwrap(); 162 | Mutex::new(*cuckoo_set_parameter.into_raw()) 163 | }, 164 | 165 | cuckoo_is_queue_under_limit: { 166 | let cuckoo_is_queue_under_limit:libloading::Symbol = 167 | loaded_library.get(b"cuckoo_is_queue_under_limit\0").unwrap(); 168 | Mutex::new(*cuckoo_is_queue_under_limit.into_raw()) 169 | }, 170 | 171 | cuckoo_clear_queues: { 172 | let cuckoo_clear_queues:libloading::Symbol = 173 | loaded_library.get(b"cuckoo_clear_queues\0").unwrap(); 174 | Mutex::new(*cuckoo_clear_queues.into_raw()) 175 | }, 176 | 177 | cuckoo_push_to_input_queue: { 178 | let cuckoo_push_to_input_queue:libloading::Symbol = 179 | loaded_library.get(b"cuckoo_push_to_input_queue\0").unwrap(); 180 | Mutex::new(*cuckoo_push_to_input_queue.into_raw()) 181 | }, 182 | 183 | cuckoo_read_from_output_queue: { 184 | let cuckoo_read_from_output_queue:libloading::Symbol = 185 | loaded_library.get(b"cuckoo_read_from_output_queue\0").unwrap(); 186 | Mutex::new(*cuckoo_read_from_output_queue.into_raw()) 187 | }, 188 | 189 | cuckoo_start_processing: { 190 | let cuckoo_start_processing:libloading::Symbol = 191 | loaded_library.get(b"cuckoo_start_processing\0").unwrap(); 192 | Mutex::new(*cuckoo_start_processing.into_raw()) 193 | }, 194 | 195 | cuckoo_stop_processing: { 196 | let cuckoo_stop_processing:libloading::Symbol = 197 | loaded_library.get(b"cuckoo_stop_processing\0").unwrap(); 198 | Mutex::new(*cuckoo_stop_processing.into_raw()) 199 | }, 200 | 201 | cuckoo_reset_processing: { 202 | let cuckoo_reset_processing:libloading::Symbol = 203 | loaded_library.get(b"cuckoo_reset_processing\0").unwrap(); 204 | Mutex::new(*cuckoo_reset_processing.into_raw()) 205 | }, 206 | 207 | cuckoo_has_processing_stopped: { 208 | let cuckoo_has_processing_stopped:libloading::Symbol = 209 | loaded_library.get(b"cuckoo_has_processing_stopped\0").unwrap(); 210 | Mutex::new(*cuckoo_has_processing_stopped.into_raw()) 211 | }, 212 | 213 | cuckoo_get_stats: { 214 | let cuckoo_get_stats: libloading::Symbol = 215 | loaded_library.get(b"cuckoo_get_stats\0").unwrap(); 216 | Mutex::new(*cuckoo_get_stats.into_raw()) 217 | }, 218 | 219 | loaded_library: Mutex::new(loaded_library), 220 | }; 221 | 222 | ret_val.call_cuckoo_init(); 223 | return Ok(ret_val); 224 | } 225 | } 226 | 227 | /// #Description 228 | /// 229 | /// Unloads the currently loaded plugin and all symbols. 230 | /// 231 | /// #Arguments 232 | /// 233 | /// None 234 | /// 235 | /// #Returns 236 | /// 237 | /// Nothing 238 | /// 239 | 240 | pub fn unload(&self) { 241 | let cuckoo_get_parameter_ref = self.cuckoo_get_parameter.lock().unwrap(); 242 | drop(cuckoo_get_parameter_ref); 243 | 244 | let cuckoo_set_parameter_ref = self.cuckoo_set_parameter.lock().unwrap(); 245 | drop(cuckoo_set_parameter_ref); 246 | 247 | let cuckoo_parameter_list_ref = self.cuckoo_parameter_list.lock().unwrap(); 248 | drop(cuckoo_parameter_list_ref); 249 | 250 | let cuckoo_call_ref = self.cuckoo_call.lock().unwrap(); 251 | drop(cuckoo_call_ref); 252 | 253 | let cuckoo_is_queue_under_limit_ref = self.cuckoo_is_queue_under_limit.lock().unwrap(); 254 | drop(cuckoo_is_queue_under_limit_ref); 255 | 256 | let cuckoo_clear_queues_ref = self.cuckoo_clear_queues.lock().unwrap(); 257 | drop(cuckoo_clear_queues_ref); 258 | 259 | let cuckoo_push_to_input_queue_ref = self.cuckoo_push_to_input_queue.lock().unwrap(); 260 | drop(cuckoo_push_to_input_queue_ref); 261 | 262 | let cuckoo_read_from_output_queue_ref = self.cuckoo_read_from_output_queue.lock().unwrap(); 263 | drop(cuckoo_read_from_output_queue_ref); 264 | 265 | let cuckoo_start_processing_ref = self.cuckoo_start_processing.lock().unwrap(); 266 | drop(cuckoo_start_processing_ref); 267 | 268 | let cuckoo_stop_processing_ref = self.cuckoo_stop_processing.lock().unwrap(); 269 | drop(cuckoo_stop_processing_ref); 270 | 271 | let cuckoo_reset_processing_ref = self.cuckoo_reset_processing.lock().unwrap(); 272 | drop(cuckoo_reset_processing_ref); 273 | 274 | let cuckoo_has_processing_stopped_ref = self.cuckoo_has_processing_stopped.lock().unwrap(); 275 | drop(cuckoo_has_processing_stopped_ref); 276 | 277 | let cuckoo_get_stats_ref = self.cuckoo_get_stats.lock().unwrap(); 278 | drop(cuckoo_get_stats_ref); 279 | 280 | let loaded_library_ref = self.loaded_library.lock().unwrap(); 281 | drop(loaded_library_ref); 282 | } 283 | 284 | /// #Description 285 | /// 286 | /// Initialises the cuckoo plugin, mostly allowing it to write a list of 287 | /// its accepted parameters. This should be called just after the plugin 288 | /// is loaded, and before anything else is called. 289 | /// 290 | /// #Arguments 291 | /// 292 | /// * None 293 | /// 294 | /// #Returns 295 | /// 296 | /// * Nothing 297 | /// 298 | /// #Example 299 | /// 300 | /// ``` 301 | /// # use cuckoo_miner::PluginLibrary; 302 | /// # use std::env; 303 | /// # use std::path::PathBuf; 304 | /// # static DLL_SUFFIX: &str = ".cuckooplugin"; 305 | /// # let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 306 | /// # d.push(format!("./target/debug/plugins/lean_cpu_16{}", DLL_SUFFIX).as_str()); 307 | /// # let plugin_path = d.to_str().unwrap(); 308 | /// let pl=PluginLibrary::new(plugin_path).unwrap(); 309 | /// pl.call_cuckoo_init(); 310 | /// ``` 311 | /// 312 | 313 | pub fn call_cuckoo_init(&self) { 314 | let cuckoo_init_ref = self.cuckoo_init.lock().unwrap(); 315 | unsafe { 316 | cuckoo_init_ref(); 317 | }; 318 | } 319 | 320 | /// #Description 321 | /// 322 | /// Call to the cuckoo_call function of the currently loaded plugin, which 323 | /// will perform a Cuckoo Cycle on the given seed, returning the first 324 | /// solution (a length 42 cycle) that is found. The implementation details 325 | /// are dependent on particular loaded plugin. 326 | /// 327 | /// #Arguments 328 | /// 329 | /// * `header` (IN) A reference to a block of [u8] bytes to use for the 330 | /// seed to the internal SIPHASH function which generates edge locations 331 | /// in the graph. In practice, this is a Grin blockheader, 332 | /// but from the plugin's perspective this can be anything. 333 | /// 334 | /// * `solutions` (OUT) A caller-allocated array of 42 unsigned bytes. This 335 | /// currently must be of size 42, corresponding to a conventional 336 | /// cuckoo-cycle solution length. If a solution is found, the solution 337 | /// nonces will be stored in this array, otherwise, they will be left 338 | /// untouched. 339 | /// 340 | /// #Returns 341 | /// 342 | /// 1 if a solution is found, with the 42 solution nonces contained 343 | /// within `sol_nonces`. 0 if no solution is found and `sol_nonces` 344 | /// remains untouched. 345 | /// 346 | /// #Example 347 | /// 348 | /// ``` 349 | /// # use cuckoo_miner::PluginLibrary; 350 | /// # use std::env; 351 | /// # use std::path::PathBuf; 352 | /// # static DLL_SUFFIX: &str = ".cuckooplugin"; 353 | /// # let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 354 | /// # d.push(format!("./target/debug/plugins/lean_cpu_16{}", DLL_SUFFIX).as_str()); 355 | /// # let plugin_path = d.to_str().unwrap(); 356 | /// let pl = PluginLibrary::new(plugin_path).unwrap(); 357 | /// let header:[u8;40] = [0;40]; 358 | /// let mut solution:[u32; 42] = [0;42]; 359 | /// let mut cuckoo_size = 0; 360 | /// let result=pl.call_cuckoo(&header, &mut cuckoo_size, &mut solution); 361 | /// if result==0 { 362 | /// println!("Solution Found!"); 363 | /// } else { 364 | /// println!("No Solution Found"); 365 | /// } 366 | /// 367 | /// ``` 368 | /// 369 | 370 | pub fn call_cuckoo(&self, header: &[u8], cuckoo_size: &mut u32, solutions: &mut [u32; 42]) -> u32 { 371 | let cuckoo_call_ref = self.cuckoo_call.lock().unwrap(); 372 | unsafe { cuckoo_call_ref(header.as_ptr(), header.len() as u32, cuckoo_size, solutions.as_mut_ptr()) } 373 | } 374 | 375 | /// #Description 376 | /// 377 | /// Call to the cuckoo_call_parameter_list function of the currently loaded 378 | /// plugin, which will provide an informative JSON array of the parameters that the 379 | /// plugin supports, as well as their descriptions and range of values. 380 | /// 381 | /// #Arguments 382 | /// 383 | /// * `param_list_bytes` (OUT) A reference to a block of [u8] bytes to fill 384 | /// with the JSON result array 385 | /// 386 | /// * `param_list_len` (IN-OUT) When called, this should contain the 387 | /// maximum number of bytes the plugin should write to `param_list_bytes`. 388 | /// Upon return, this is filled with the number of bytes that were written to 389 | /// `param_list_bytes`. 390 | /// 391 | /// #Returns 392 | /// 393 | /// 0 if okay, with the result is stored in `param_list_bytes` 394 | /// 3 if the provided array is too short 395 | /// 396 | /// #Example 397 | /// 398 | /// ``` 399 | /// # use cuckoo_miner::PluginLibrary; 400 | /// # use std::env; 401 | /// # use std::path::PathBuf; 402 | /// # static DLL_SUFFIX: &str = ".cuckooplugin"; 403 | /// # let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 404 | /// # d.push(format!("./target/debug/plugins/lean_cpu_16{}", DLL_SUFFIX).as_str()); 405 | /// # let plugin_path = d.to_str().unwrap(); 406 | /// let pl=PluginLibrary::new(plugin_path).unwrap(); 407 | /// pl.call_cuckoo_init(); 408 | /// let mut param_list_bytes:[u8;1024]=[0;1024]; 409 | /// let mut param_list_len=param_list_bytes.len() as u32; 410 | /// //get a list of json parameters 411 | /// let parameter_list=pl.call_cuckoo_parameter_list(&mut param_list_bytes, 412 | /// &mut param_list_len); 413 | /// ``` 414 | /// 415 | 416 | pub fn call_cuckoo_parameter_list( 417 | &self, 418 | param_list_bytes: &mut [u8], 419 | param_list_len: &mut u32, 420 | ) -> u32 { 421 | let cuckoo_parameter_list_ref = self.cuckoo_parameter_list.lock().unwrap(); 422 | unsafe { cuckoo_parameter_list_ref(param_list_bytes.as_mut_ptr(), param_list_len) } 423 | } 424 | 425 | /// #Description 426 | /// 427 | /// Retrieves the value of a parameter from the currently loaded plugin 428 | /// 429 | /// #Arguments 430 | /// 431 | /// * `name_bytes` (IN) A reference to a block of [u8] bytes storing the 432 | /// parameter name 433 | /// 434 | /// * `device_id` (IN) The device ID to which the parameter applies (if applicable) 435 | /// * `value` (OUT) A reference where the parameter value will be stored 436 | /// 437 | /// #Returns 438 | /// 439 | /// 0 if the parameter was retrived, and the result is stored in `value` 440 | /// 1 if the parameter does not exist 441 | /// 4 if the provided parameter name was too long 442 | /// 443 | /// #Example 444 | /// ``` 445 | /// # use cuckoo_miner::PluginLibrary; 446 | /// # use std::env; 447 | /// # use std::path::PathBuf; 448 | /// # static DLL_SUFFIX: &str = ".cuckooplugin"; 449 | /// # let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 450 | /// # d.push(format!("./target/debug/plugins/lean_cpu_16{}", DLL_SUFFIX).as_str()); 451 | /// # let plugin_path = d.to_str().unwrap(); 452 | /// let pl=PluginLibrary::new(plugin_path).unwrap(); 453 | /// pl.call_cuckoo_init(); 454 | /// let name = "NUM_THREADS"; 455 | /// let mut num_threads:u32 = 0; 456 | /// let ret_val = pl.call_cuckoo_get_parameter(name.as_bytes(), 0, &mut num_threads); 457 | /// ``` 458 | /// 459 | 460 | pub fn call_cuckoo_get_parameter(&self, name_bytes: &[u8], device_id: u32, value: &mut u32) -> u32 { 461 | let cuckoo_get_parameter_ref = self.cuckoo_get_parameter.lock().unwrap(); 462 | unsafe { cuckoo_get_parameter_ref(name_bytes.as_ptr(), name_bytes.len() as u32, device_id, value) } 463 | } 464 | 465 | /// Sets the value of a parameter in the currently loaded plugin 466 | /// 467 | /// #Arguments 468 | /// 469 | /// * `name_bytes` (IN) A reference to a block of [u8] bytes storing the 470 | /// parameter name 471 | /// 472 | /// * `device_id` (IN) The deviceID to which the parameter applies (if applicable) 473 | /// * `value` (IN) The value to which to set the parameter 474 | /// 475 | /// #Returns 476 | /// 477 | /// 0 if the parameter was retrieved, and the result is stored in `value` 478 | /// 1 if the parameter does not exist 479 | /// 2 if the parameter exists, but the provided value is outside the 480 | /// allowed range determined by the plugin 481 | /// 4 if the provided parameter name is too long 482 | /// 483 | /// #Example 484 | /// 485 | /// ``` 486 | /// # use cuckoo_miner::PluginLibrary; 487 | /// # use std::env; 488 | /// # use std::path::PathBuf; 489 | /// # static DLL_SUFFIX: &str = ".cuckooplugin"; 490 | /// # let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 491 | /// # d.push(format!("./target/debug/plugins/lean_cpu_16{}", DLL_SUFFIX).as_str()); 492 | /// # let plugin_path = d.to_str().unwrap(); 493 | /// let pl=PluginLibrary::new(plugin_path).unwrap(); 494 | /// let name = "NUM_THREADS"; 495 | /// let return_code = pl.call_cuckoo_set_parameter(name.as_bytes(), 0, 8); 496 | /// ``` 497 | /// 498 | 499 | pub fn call_cuckoo_set_parameter(&self, name_bytes: &[u8], device_id: u32, value: u32) -> u32 { 500 | let cuckoo_set_parameter_ref = self.cuckoo_set_parameter.lock().unwrap(); 501 | unsafe { cuckoo_set_parameter_ref(name_bytes.as_ptr(), name_bytes.len() as u32, device_id, value) } 502 | } 503 | 504 | /// #Description 505 | /// 506 | /// For Async/Queued mode, check whether the plugin is ready 507 | /// to accept more headers. 508 | /// 509 | /// #Arguments 510 | /// 511 | /// * None 512 | /// 513 | /// #Returns 514 | /// 515 | /// * 1 if the queue can accept more hashes, 0 otherwise 516 | /// 517 | 518 | pub fn call_cuckoo_is_queue_under_limit(&self) -> u32 { 519 | let cuckoo_is_queue_under_limit_ref = self.cuckoo_is_queue_under_limit.lock().unwrap(); 520 | unsafe { cuckoo_is_queue_under_limit_ref() } 521 | } 522 | 523 | /// #Description 524 | /// 525 | /// Pushes header data to the loaded plugin for later processing in 526 | /// asyncronous/queued mode. 527 | /// 528 | /// #Arguments 529 | /// 530 | /// * `data` (IN) A block of bytes to use for the seed to the internal 531 | /// SIPHASH function which generates edge locations in the graph. In 532 | /// practice, this is a Grin blockheader, but from the 533 | /// plugin's perspective this can be anything. 534 | /// 535 | /// * `nonce` (IN) The nonce that was used to generate this data, for 536 | /// identification purposes in the solution queue 537 | /// 538 | /// #Returns 539 | /// 540 | /// 0 if the hash was successfully added to the queue 541 | /// 1 if the queue is full 542 | /// 2 if the length of the data is greater than the plugin allows 543 | /// 4 if the plugin has been told to shutdown 544 | /// 545 | /// #Unsafe 546 | /// 547 | /// Provided values are copied within the plugin, and will not be 548 | /// modified 549 | /// 550 | /// #Example 551 | /// ``` 552 | /// # use cuckoo_miner::PluginLibrary; 553 | /// # use std::env; 554 | /// # use std::path::PathBuf; 555 | /// # static DLL_SUFFIX: &str = ".cuckooplugin"; 556 | /// # let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 557 | /// # d.push(format!("./target/debug/plugins/lean_cpu_16{}", DLL_SUFFIX).as_str()); 558 | /// # let plugin_path = d.to_str().unwrap(); 559 | /// let pl=PluginLibrary::new(plugin_path).unwrap(); 560 | /// //Processing started after call to cuckoo_start_processing() 561 | /// //a test hash of zeroes 562 | /// let hash:[u8;32]=[0;32]; 563 | /// //test nonce (u64, basically) should be unique 564 | /// let nonce:[u8;8]=[0;8]; 565 | /// let result=pl.call_cuckoo_push_to_input_queue(&hash, &nonce); 566 | /// ``` 567 | /// 568 | 569 | pub fn call_cuckoo_push_to_input_queue(&self, id: u32, data: &[u8], nonce: &[u8;8]) -> u32 { 570 | let cuckoo_push_to_input_queue_ref = self.cuckoo_push_to_input_queue.lock().unwrap(); 571 | unsafe { cuckoo_push_to_input_queue_ref(id, data.as_ptr(), data.len() as u32, nonce.as_ptr()) } 572 | } 573 | 574 | /// #Description 575 | /// 576 | /// Clears internal queues of all data 577 | /// 578 | /// #Arguments 579 | /// 580 | /// * None 581 | /// 582 | /// #Returns 583 | /// 584 | /// * Nothing 585 | /// 586 | /// #Example 587 | /// ``` 588 | /// # use cuckoo_miner::PluginLibrary; 589 | /// # use std::env; 590 | /// # use std::path::PathBuf; 591 | /// # static DLL_SUFFIX: &str = ".cuckooplugin"; 592 | /// # let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 593 | /// # d.push(format!("./target/debug/plugins/lean_cpu_16{}", DLL_SUFFIX).as_str()); 594 | /// # let plugin_path = d.to_str().unwrap(); 595 | /// let pl=PluginLibrary::new(plugin_path).unwrap(); 596 | /// //Processing started after call to cuckoo_start_processing() 597 | /// //a test hash of zeroes 598 | /// let hash:[u8;32]=[0;32]; 599 | /// //test nonce (u64, basically) should be unique 600 | /// let nonce:[u8;8]=[0;8]; 601 | /// let result=pl.call_cuckoo_push_to_input_queue(&hash, &nonce); 602 | /// //clear queues 603 | /// pl.call_cuckoo_clear_queues(); 604 | /// ``` 605 | /// 606 | 607 | pub fn call_cuckoo_clear_queues(&self) { 608 | let cuckoo_clear_queues_ref = self.cuckoo_clear_queues.lock().unwrap(); 609 | unsafe { cuckoo_clear_queues_ref() } 610 | } 611 | 612 | 613 | /// #Description 614 | /// 615 | /// Reads the next solution from the output queue, if one exists. Only 616 | /// solutions which meet the target difficulty specified in the preceeding 617 | /// call to 'notify' will be placed in the output queue. Read solutions 618 | /// are popped from the queue. Does not block, and intended to be called 619 | /// continually as part of a mining loop. 620 | /// 621 | /// #Arguments 622 | /// 623 | /// * `sol_nonces` (OUT) A block of 42 u32s in which the solution nonces 624 | /// will be stored, if any exist. 625 | /// 626 | /// * `nonce` (OUT) A block of 8 u8s representing a Big-Endian u64, used 627 | /// for identification purposes so the caller can reconstruct the header 628 | /// used to generate the solution. 629 | /// 630 | /// #Returns 631 | /// 632 | /// 1 if a solution was popped from the queue 633 | /// 0 if a solution is not available 634 | /// 635 | /// #Example 636 | /// ``` 637 | /// # use cuckoo_miner::PluginLibrary; 638 | /// # use std::env; 639 | /// # use std::path::PathBuf; 640 | /// # static DLL_SUFFIX: &str = ".cuckooplugin"; 641 | /// # let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 642 | /// # d.push(format!("./target/debug/plugins/lean_cpu_16{}", DLL_SUFFIX).as_str()); 643 | /// # let plugin_path = d.to_str().unwrap(); 644 | /// let pl=PluginLibrary::new(plugin_path).unwrap(); 645 | /// //Processing started after call to cuckoo_start_processing() 646 | /// //a test hash of zeroes 647 | /// let hash:[u8;32]=[0;32]; 648 | /// //test nonce (u64, basically) should be unique 649 | /// let nonce:[u8;8]=[0;8]; 650 | /// let result=pl.call_cuckoo_push_to_input_queue(&hash, &nonce); 651 | /// 652 | /// //within loop 653 | /// let mut sols:[u32; 42] = [0; 42]; 654 | /// let mut nonce: [u8; 8] = [0;8]; 655 | /// let mut cuckoo_size = 0; 656 | /// let found = pl.call_cuckoo_read_from_output_queue(&mut sols, &mut cuckoo_size, &mut nonce); 657 | /// ``` 658 | /// 659 | 660 | pub fn call_cuckoo_read_from_output_queue( 661 | &self, 662 | id: &mut u32, 663 | solutions: &mut [u32; 42], 664 | cuckoo_size: &mut u32, 665 | nonce: &mut [u8; 8], 666 | ) -> u32 { 667 | let cuckoo_read_from_output_queue_ref = self.cuckoo_read_from_output_queue.lock().unwrap(); 668 | let ret = unsafe { cuckoo_read_from_output_queue_ref(id, solutions.as_mut_ptr(), cuckoo_size, nonce.as_mut_ptr()) }; 669 | ret 670 | } 671 | 672 | /// #Description 673 | /// 674 | /// Starts asyncronous processing. The plugin will start reading hashes 675 | /// from the input queue, delegate them internally as it sees fit, and 676 | /// put solutions into the output queue. It is up to the plugin 677 | /// implementation to manage how the workload is spread across 678 | /// devices/threads. Once processing is started, communication with 679 | /// the started process happens via reading and writing from the 680 | /// input and output queues. 681 | /// 682 | /// #Arguments 683 | /// 684 | /// * None 685 | /// 686 | /// #Returns 687 | /// 688 | /// * 1 if processing was successfully started 689 | /// * Another value if procesing failed to start (return codes TBD) 690 | /// 691 | /// #Unsafe 692 | /// 693 | /// The caller is reponsible for calling call_cuckoo_stop_processing() 694 | /// before exiting its thread, which will signal the internally detached 695 | /// thread to stop processing, clean up, and exit. 696 | /// 697 | /// #Example 698 | /// ``` 699 | /// # use cuckoo_miner::PluginLibrary; 700 | /// # use std::env; 701 | /// # use std::path::PathBuf; 702 | /// # static DLL_SUFFIX: &str = ".cuckooplugin"; 703 | /// # let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 704 | /// # d.push(format!("./target/debug/plugins/lean_cpu_16{}", DLL_SUFFIX).as_str()); 705 | /// 706 | /// # let plugin_path = d.to_str().unwrap(); 707 | /// let pl=PluginLibrary::new(plugin_path).unwrap(); 708 | /// let ret_val=pl.call_cuckoo_start_processing(); 709 | /// ``` 710 | 711 | pub fn call_cuckoo_start_processing(&self) -> u32 { 712 | let cuckoo_start_processing_ref = self.cuckoo_start_processing.lock().unwrap(); 713 | unsafe { cuckoo_start_processing_ref() } 714 | } 715 | 716 | /// #Description 717 | /// 718 | /// Stops asyncronous processing. The plugin should signal to shut down 719 | /// processing, as quickly as possible, clean up all threads/devices/memory 720 | /// it may have allocated, and clear its queues. Note this merely sets 721 | /// a flag indicating that the threads started by 'cuckoo_start_processing' 722 | /// should shut down, and will return instantly. Use 'cuckoo_has_processing_stopped' 723 | /// to check on the shutdown status. 724 | /// 725 | /// #Arguments 726 | /// 727 | /// * None 728 | /// 729 | /// #Returns 730 | /// 731 | /// * 1 in all cases, indicating the stop flag was set.. 732 | /// 733 | /// #Example 734 | /// ``` 735 | /// # use cuckoo_miner::PluginLibrary; 736 | /// # use std::env; 737 | /// # use std::path::PathBuf; 738 | /// # static DLL_SUFFIX: &str = ".cuckooplugin"; 739 | /// # let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 740 | /// # d.push(format!("./target/debug/plugins/lean_cpu_16{}", DLL_SUFFIX).as_str()); 741 | /// # let plugin_path = d.to_str().unwrap(); 742 | /// let pl=PluginLibrary::new(plugin_path).unwrap(); 743 | /// let mut ret_val=pl.call_cuckoo_start_processing(); 744 | /// //Send data into queue, read results, etc 745 | /// ret_val=pl.call_cuckoo_stop_processing(); 746 | /// while pl.call_cuckoo_has_processing_stopped() == 0 { 747 | /// //don't continue/exit thread until plugin is stopped 748 | /// } 749 | /// ``` 750 | 751 | pub fn call_cuckoo_stop_processing(&self) -> u32 { 752 | let cuckoo_stop_processing_ref = self.cuckoo_stop_processing.lock().unwrap(); 753 | unsafe { cuckoo_stop_processing_ref() } 754 | } 755 | 756 | /// #Description 757 | /// 758 | /// Resets the internal processing flag so that processing may begin again. 759 | /// 760 | /// #Arguments 761 | /// 762 | /// * None 763 | /// 764 | /// #Returns 765 | /// 766 | /// * 1 in all cases, indicating the stop flag was reset 767 | /// 768 | /// #Example 769 | /// ``` 770 | /// # use cuckoo_miner::PluginLibrary; 771 | /// # use std::env; 772 | /// # use std::path::PathBuf; 773 | /// # static DLL_SUFFIX: &str = ".cuckooplugin"; 774 | /// # let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 775 | /// # d.push(format!("./target/debug/plugins/lean_cpu_16{}", DLL_SUFFIX).as_str()); 776 | /// # let plugin_path = d.to_str().unwrap(); 777 | /// let pl=PluginLibrary::new(plugin_path).unwrap(); 778 | /// let mut ret_val=pl.call_cuckoo_start_processing(); 779 | /// //Send data into queue, read results, etc 780 | /// ret_val=pl.call_cuckoo_stop_processing(); 781 | /// while pl.call_cuckoo_has_processing_stopped() == 0 { 782 | /// //don't continue/exit thread until plugin is stopped 783 | /// } 784 | /// // later on 785 | /// pl.call_cuckoo_reset_processing(); 786 | /// //restart 787 | /// ``` 788 | 789 | pub fn call_cuckoo_reset_processing(&self) -> u32 { 790 | let cuckoo_reset_processing_ref = self.cuckoo_reset_processing.lock().unwrap(); 791 | unsafe { cuckoo_reset_processing_ref() } 792 | } 793 | 794 | /// #Description 795 | /// 796 | /// Returns whether all internal processing within the plugin has stopped, 797 | /// meaning it's safe to exit the calling thread after a call to 798 | /// cuckoo_stop_processing() 799 | /// 800 | /// #Arguments 801 | /// 802 | /// * None 803 | /// 804 | /// #Returns 805 | /// 806 | /// 1 if all internal processing has been stopped. 807 | /// 0 if processing activity is still in progress 808 | /// 809 | /// #Example 810 | /// ``` 811 | /// # use cuckoo_miner::PluginLibrary; 812 | /// # use std::env; 813 | /// # use std::path::PathBuf; 814 | /// # static DLL_SUFFIX: &str = ".cuckooplugin"; 815 | /// # let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 816 | /// # d.push(format!("./target/debug/plugins/lean_cpu_16{}", DLL_SUFFIX).as_str()); 817 | /// # let plugin_path = d.to_str().unwrap(); 818 | /// let pl=PluginLibrary::new(plugin_path).unwrap(); 819 | /// let ret_val=pl.call_cuckoo_start_processing(); 820 | /// //Things happen in between, within a loop 821 | /// pl.call_cuckoo_stop_processing(); 822 | /// while pl.call_cuckoo_has_processing_stopped() == 0 { 823 | /// //don't continue/exit thread until plugin is stopped 824 | /// } 825 | /// ``` 826 | 827 | pub fn call_cuckoo_has_processing_stopped(&self) -> u32 { 828 | let cuckoo_has_processing_stopped_ref = self.cuckoo_has_processing_stopped.lock().unwrap(); 829 | unsafe { cuckoo_has_processing_stopped_ref() } 830 | } 831 | 832 | /// #Description 833 | /// 834 | /// Retrieves a JSON list of the plugin's current stats for all running 835 | /// devices. In the case of a plugin running GPUs in parallel, it should 836 | /// be a list of running devices. In the case of a CPU plugin, it will 837 | /// most likely be a single CPU. e.g: 838 | /// 839 | /// ```text 840 | /// [{ 841 | /// device_id:"0", 842 | /// device_name:"NVIDIA GTX 1080", 843 | /// last_start_time: "23928329382", 844 | /// last_end_time: "23928359382", 845 | /// last_solution_time: "3382", 846 | /// }, 847 | /// { 848 | /// device_id:"1", 849 | /// device_name:"NVIDIA GTX 1080ti", 850 | /// last_start_time: "23928329382", 851 | /// last_end_time: "23928359382", 852 | /// last_solution_time: "3382", 853 | /// }] 854 | /// ``` 855 | /// #Arguments 856 | /// 857 | /// * `stat_bytes` (OUT) A reference to a block of [u8] bytes to fill with 858 | /// the JSON result array 859 | /// 860 | /// * `stat_bytes_len` (IN-OUT) When called, this should contain the 861 | /// maximum number of bytes the plugin should write to `stat_bytes`. Upon return, 862 | /// this is filled with the number of bytes that were written to `stat_bytes`. 863 | /// 864 | /// #Returns 865 | /// 866 | /// 0 if okay, with the result is stored in `stat_bytes` 867 | /// 3 if the provided array is too short 868 | /// 869 | /// #Example 870 | /// 871 | /// ``` 872 | /// # use cuckoo_miner::PluginLibrary; 873 | /// # use std::env; 874 | /// # use std::path::PathBuf; 875 | /// # static DLL_SUFFIX: &str = ".cuckooplugin"; 876 | /// # let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 877 | /// # d.push(format!("./target/debug/plugins/lean_cpu_16{}", DLL_SUFFIX).as_str()); 878 | /// # let plugin_path = d.to_str().unwrap(); 879 | /// let pl=PluginLibrary::new(plugin_path).unwrap(); 880 | /// pl.call_cuckoo_init(); 881 | /// ///start plugin+processing, and then within the loop: 882 | /// let mut stat_bytes:[u8;1024]=[0;1024]; 883 | /// let mut stat_len=stat_bytes.len() as u32; 884 | /// //get a list of json parameters 885 | /// let parameter_list=pl.call_cuckoo_get_stats(&mut stat_bytes, 886 | /// &mut stat_len); 887 | /// ``` 888 | /// 889 | 890 | pub fn call_cuckoo_get_stats(&self, stat_bytes: &mut [u8], stat_bytes_len: &mut u32) -> u32 { 891 | let cuckoo_get_stats_ref = self.cuckoo_get_stats.lock().unwrap(); 892 | unsafe { cuckoo_get_stats_ref(stat_bytes.as_mut_ptr(), stat_bytes_len) } 893 | } 894 | } 895 | -------------------------------------------------------------------------------- /src/cuckoo_sys/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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 containing the low level calls to cuckoo-miner plugins, including 16 | //! functions 17 | //! for loading and unloading plugins, querying what plugins are installed on 18 | //! the system, 19 | //! as well as the actual mining calls to a plugin. This crate should be used 20 | //! by other 21 | //! cuckoo-miner crates, but should not be exposed to external consumers of the 22 | //! crate. 23 | 24 | pub mod manager; 25 | -------------------------------------------------------------------------------- /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 | 17 | #blake2b prerequisite 18 | set (BLAKE_2B "cuckoo/src/crypto/blake2b-ref.c") 19 | 20 | #cuckoo_miner (lean_cpu) sources 21 | set (CUCKOO_LEAN_MINER_SOURCES cuckoo/src/crypto/siphash.h cuckoo/src/cuckoo/cuckoo.h cuckoo/src/cuckoo/lean.hpp cuckoo/src/cuckoo/lean.cpp ${BLAKE_2B}) 22 | 23 | #cuckatoo_miner (lean_cpu) sources 24 | set (CUCKATOO_LEAN_MINER_SOURCES cuckoo/src/crypto/siphash.h cuckoo/src/cuckatoo/cuckatoo.h cuckoo/src/cuckatoo/lean.hpp cuckoo/src/cuckatoo/lean.cpp ${BLAKE_2B}) 25 | 26 | #cuckoo mean miner sources (mean_cpu) 27 | set (CUCKOO_MEAN_MINER_SOURCES cuckoo/src/crypto/siphash.h cuckoo/src/cuckoo/cuckoo.h cuckoo/src/cuckoo/mean.hpp cuckoo/src/cuckoo/mean.cpp ${BLAKE_2B}) 28 | 29 | #cuckatoo mean miner sources (mean_cpu) 30 | set (CUCKATOO_MEAN_MINER_SOURCES cuckoo/src/crypto/siphash.h cuckoo/src/cuckatoo/cuckatoo.h cuckoo/src/cuckatoo/mean.hpp cuckoo/src/cuckatoo/mean.cpp ${BLAKE_2B}) 31 | 32 | #cuckoo cuda miner source (mean_miner.cu) 33 | set (CUCKOO_CUDA_MINER_SOURCES cuckoo/src/cuckoo/mean.cu ${BLAKE_2B} ) 34 | 35 | #cuckoo cuda miner source (mean_miner.cu) 36 | set (CUCKATOO_CUDA_MINER_SOURCES cuckoo/src/cuckatoo/mean.cu ${BLAKE_2B} ) 37 | 38 | ###cuckoo lean_cpu miner targets 39 | foreach(CUCKOO_SIZE 16 30 31) 40 | add_library(cuckoo_lean_cpu_${CUCKOO_SIZE} SHARED ${CUCKOO_LEAN_MINER_SOURCES}) 41 | #get edgebits 42 | math (EXPR EDGEBITS ${CUCKOO_SIZE}-1) 43 | set_target_properties(cuckoo_lean_cpu_${CUCKOO_SIZE} PROPERTIES COMPILE_FLAGS "${GPP_FLAGS} -DATOMIC -DEDGEBITS=${EDGEBITS}" PREFIX "" SUFFIX ".cuckooplugin" ) 44 | endforeach(CUCKOO_SIZE) 45 | 46 | ###cuckatoo lean_cpu miner targets 47 | foreach(CUCKATOO_SIZE 19 29 30) 48 | add_library(cuckatoo_lean_cpu_${CUCKATOO_SIZE} SHARED ${CUCKATOO_LEAN_MINER_SOURCES}) 49 | #get edgebits 50 | math (EXPR EDGEBITS ${CUCKATOO_SIZE}) 51 | set_target_properties(cuckatoo_lean_cpu_${CUCKATOO_SIZE} PROPERTIES COMPILE_FLAGS "${GPP_FLAGS} -DATOMIC -DEDGEBITS=${EDGEBITS}" PREFIX "" SUFFIX ".cuckooplugin" ) 52 | endforeach(CUCKATOO_SIZE) 53 | 54 | #cuckoo mean (mean_cpu) targets 55 | foreach(CUCKOO_SIZE 16 30 31) 56 | if (CUCKOO_SIZE EQUAL 16) 57 | set (DXBITSFLAG "-DXBITS=0") 58 | else() 59 | set (DXBITSFLAG "") 60 | endif() 61 | 62 | add_library(cuckoo_mean_cpu_${CUCKOO_SIZE} SHARED ${CUCKOO_MEAN_MINER_SOURCES}) 63 | #get edgebits 64 | math (EXPR EDGEBITS ${CUCKOO_SIZE}-1) 65 | set_target_properties(cuckoo_mean_cpu_${CUCKOO_SIZE} PROPERTIES COMPILE_FLAGS "${GPP_FLAGS} -mavx2 ${DXBITSFLAG} -DNSIPHASH=8 -DSAVEEDGES -DEDGEBITS=${EDGEBITS}" PREFIX "" SUFFIX ".cuckooplugin") 66 | endforeach(CUCKOO_SIZE) 67 | 68 | #matrix miner (mean_cpu) compatible mode targets, for older processors 69 | foreach(CUCKOO_SIZE 16 30 31) 70 | if (CUCKOO_SIZE EQUAL 16) 71 | set (DXBITSFLAG "-DXBITS=0") 72 | else() 73 | set (DXBITSFLAG "") 74 | endif() 75 | add_library(cuckoo_mean_compat_cpu_${CUCKOO_SIZE} SHARED ${CUCKOO_MEAN_MINER_SOURCES}) 76 | #get edgebits 77 | math (EXPR EDGEBITS ${CUCKOO_SIZE}-1) 78 | set_target_properties(cuckoo_mean_compat_cpu_${CUCKOO_SIZE} PROPERTIES COMPILE_FLAGS "${GPP_FLAGS} ${DXBITSFLAG} -DNSIPHASH=1 -DSAVEEDGES -DEDGEBITS=${EDGEBITS}" PREFIX "" SUFFIX ".cuckooplugin") 79 | endforeach(CUCKOO_SIZE) 80 | 81 | #cuckatoo mean (cuckatoo_mean_cpu) targets 82 | foreach(CUCKATOO_SIZE 19 29 30) 83 | if (CUCKATOO_SIZE EQUAL 19) 84 | set (DXBITSFLAG "-DXBITS=2") 85 | else() 86 | set (DXBITSFLAG "") 87 | endif() 88 | 89 | add_library(cuckatoo_mean_cpu_${CUCKATOO_SIZE} SHARED ${CUCKATOO_MEAN_MINER_SOURCES}) 90 | #get edgebits 91 | math (EXPR EDGEBITS ${CUCKATOO_SIZE}) 92 | set_target_properties(cuckatoo_mean_cpu_${CUCKATOO_SIZE} PROPERTIES COMPILE_FLAGS "${GPP_FLAGS} -mavx2 ${DXBITSFLAG} -DNSIPHASH=8 -DSAVEEDGES -DEDGEBITS=${EDGEBITS}" PREFIX "" SUFFIX ".cuckooplugin") 93 | endforeach(CUCKATOO_SIZE) 94 | 95 | #matrix miner (cuckatoo_mean_compat_cpu) compatible mode targets, for older processors 96 | foreach(CUCKATOO_SIZE 19 29 30) 97 | if (CUCKATOO_SIZE EQUAL 19) 98 | set (DXBITSFLAG "-DXBITS=2") 99 | else() 100 | set (DXBITSFLAG "") 101 | endif() 102 | add_library(cuckatoo_mean_compat_cpu_${CUCKATOO_SIZE} SHARED ${CUCKATOO_MEAN_MINER_SOURCES}) 103 | #get edgebits 104 | math (EXPR EDGEBITS ${CUCKATOO_SIZE}) 105 | set_target_properties(cuckatoo_mean_compat_cpu_${CUCKATOO_SIZE} PROPERTIES COMPILE_FLAGS "${GPP_FLAGS} ${DXBITSFLAG} -DNSIPHASH=1 -DSAVEEDGES -DEDGEBITS=${EDGEBITS}" PREFIX "" SUFFIX ".cuckooplugin") 106 | endforeach(CUCKATOO_SIZE) 107 | 108 | # Cuda 109 | if(BUILD_CUDA_PLUGINS) 110 | include("cmake/find_cuda.cmake") 111 | IF (CUDA_FOUND) 112 | set (CUDA_PROPAGATE_HOST_FLAGS ON) 113 | ##cuckoo cuda miner targets 114 | foreach(CUCKOO_SIZE 30 31) 115 | #get edgebits 116 | math (EXPR EDGEBITS ${CUCKOO_SIZE}-1) 117 | cuda_add_library (cuckoo_cuda_${CUCKOO_SIZE} SHARED ${CUCKOO_CUDA_MINER_SOURCES} OPTIONS "-DEDGEBITS=${EDGEBITS}") 118 | set_target_properties(cuckoo_cuda_${CUCKOO_SIZE} PROPERTIES PREFIX "" SUFFIX ".cuckooplugin") 119 | endforeach(CUCKOO_SIZE) 120 | ##cuckatoo cuda miner targets 121 | foreach(CUCKATOO_SIZE 19 29 30) 122 | #get edgebits 123 | math (EXPR EDGEBITS ${CUCKATOO_SIZE}) 124 | cuda_add_library (cuckatoo_cuda_${CUCKATOO_SIZE} SHARED ${CUCKATOO_CUDA_MINER_SOURCES} OPTIONS "-DEDGEBITS=${EDGEBITS}") 125 | set_target_properties(cuckatoo_cuda_${CUCKATOO_SIZE} PROPERTIES PREFIX "" SUFFIX ".cuckooplugin") 126 | endforeach(CUCKATOO_SIZE) 127 | endif(CUDA_FOUND) 128 | endif(BUILD_CUDA_PLUGINS) 129 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 "9.0") 49 | set(__cuda_arch_bin "3.5 3.7 5.0 5.2 6.0 6.1 7.0") 50 | elseif(NOT ${CUDA_VERSION_STRING} VERSION_LESS "8.0") 51 | set(__cuda_arch_bin "3.5 5.0 5.2 5.3 6.0 6.1") 52 | elseif(NOT ${CUDA_VERSION_STRING} VERSION_LESS "6.5") 53 | set(__cuda_arch_bin "2.0 2.1(2.0) 3.0 3.5 5.0 5.2") 54 | elseif(NOT ${CUDA_VERSION_STRING} VERSION_LESS "6.0") 55 | set(__cuda_arch_bin "2.0 2.1(2.0) 3.0 3.5 5.0") 56 | elseif(NOT ${CUDA_VERSION_STRING} VERSION_LESS "5.0") 57 | set(__cuda_arch_bin "2.0 2.1(2.0) 3.0 3.5") 58 | elseif(${CUDA_VERSION_STRING} VERSION_GREATER "4.1") 59 | set(__cuda_arch_bin "2.0 2.1(2.0) 3.0") 60 | else() 61 | set(__cuda_arch_bin "2.0 2.1(2.0)") 62 | endif() 63 | 64 | set(CUDA_ARCH_BIN ${__cuda_arch_bin} CACHE STRING "Specify 'real' GPU architectures to build binaries for, BIN(PTX) format is supported") 65 | 66 | set(CUDA_ARCH_PTX "" CACHE STRING "Specify 'virtual' PTX arch to build PTX intermediate code for. Example: 1.0 1.2 or 10 12") 67 | #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") 68 | 69 | # Guess this macros will be included in cmake distributive 70 | include(cmake/CudaComputeTargetFlags.cmake) 71 | APPEND_TARGET_ARCH_FLAGS() 72 | 73 | # Prevent compilation issues between recent gcc versions and old CUDA versions 74 | list(APPEND CUDA_NVCC_FLAGS "-D_FORCE_INLINES") 75 | endif() 76 | -------------------------------------------------------------------------------- /src/error/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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(String::from(format!("Error loading plugin: {}", error))) 61 | } 62 | } 63 | 64 | impl From for CuckooMinerError { 65 | fn from(error: string::FromUtf8Error) -> Self { 66 | CuckooMinerError::PluginIOError(String::from( 67 | format!("Error loading plugin description: {}", error), 68 | )) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/error/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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 | //! Simple crate which just contains cuckoo-miner's error enum, used by 16 | //! all other internal crates. 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 error; 25 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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 | //! A brief description of basic operations follows, as well as some 25 | //! examples of how cuckoo miner should be called. 26 | //! 27 | //! ## Interfaces 28 | //! 29 | //! The library provides 2 high level interfaces: 30 | //! 31 | //! The [CuckooPluginManager](struct.CuckooPluginManager.html) 32 | //! takes care of querying and loading plugins. A caller can provide a directory 33 | //! for the plugin manager to scan, and the manager will load each plugin and 34 | //! populate a [CuckooPluginCapabilities](struct.CuckooPluginCapabilities.html) 35 | //! for each, which will contain a description of the plugin as well as any parameters 36 | //! that can be configured. 37 | //! 38 | //! The [CuckooMiner](struct.CuckooMiner.html) struct provides a 39 | //! high-level interface that a caller can use to load and run one or many 40 | //! simultaneous plugin mining implementations. 41 | //! 42 | //! ## Operational Modes 43 | //! 44 | //! The miner can be run in either synchronous or asynchronous mode. 45 | //! 46 | //! Syncronous mode uses the [`mine`](struct.CuckooMiner.html#method.mine) function, 47 | //! which takes a complete hash, processes it within the calling thread via the plugin's 48 | //! [`call_cuckoo`](struct.PluginLibrary.html#method.call_cuckoo) function, 49 | //! and returns the result. 50 | //! 51 | //! Asynchronous mode uses the [`notify`](struct.CuckoMiner.html#method.notify) 52 | //! function, which takes the pre-nonce and 53 | //! post-nonce parts of a block header, mutates it internally with a nonce, and 54 | //! inserts the resulting hash into the plugin's internal queue for processing. 55 | //! Solutions are placed into an output queue, which the calling thread can 56 | //! read ascynronously via a [job handle](struct.CuckooMinerJobHandle.html). 57 | //! 58 | //! Examples of using either mode follow: 59 | //! 60 | //! ## Example - Sync mode 61 | //! ``` 62 | //! extern crate cuckoo_miner as cuckoo; 63 | //! extern crate time; 64 | //! 65 | //! use std::path::PathBuf; 66 | //! 67 | //! let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 68 | //! d.push("target/debug/plugins/"); 69 | //! 70 | //! // load plugin manager 71 | //! let mut plugin_manager = cuckoo::CuckooPluginManager::new().unwrap(); 72 | //! plugin_manager 73 | //! .load_plugin_dir(String::from(d.to_str().unwrap())) 74 | //! .expect(""); 75 | //! 76 | //! // Load a single plugin using a filter 77 | //! let caps=plugin_manager.get_available_plugins("lean_cpu_16").unwrap(); 78 | //! let mut config_vec=Vec::new(); 79 | //! let mut config = cuckoo::CuckooMinerConfig::new(); 80 | //! config.plugin_full_path = caps[0].full_path.clone(); 81 | //! config_vec.push(config); 82 | //! 83 | //! let duration_in_seconds=60; 84 | //! let stat_check_interval = 3; 85 | //! let deadline = time::get_time().sec + duration_in_seconds; 86 | //! let mut next_stat_check = time::get_time().sec + stat_check_interval; 87 | //! 88 | //! let mut i=0; 89 | //! println!("Test mining for {} seconds, looking for difficulty > 0", duration_in_seconds); 90 | //! for c in config_vec.clone().into_iter(){ 91 | //! println!("Plugin (Sync Mode): {}", c.plugin_full_path); 92 | //! } 93 | //! 94 | //! while time::get_time().sec < deadline { 95 | //! let miner = cuckoo::CuckooMiner::new(config_vec.clone()).expect(""); 96 | //! //Mining with a dummy header here, but in reality this would be a passed in 97 | //! //header hash 98 | //! let mut header:[u8; 32] = [0;32]; 99 | //! let mut iterations=0; 100 | //! let mut solution = cuckoo::CuckooMinerSolution::new(); 101 | //! loop { 102 | //! header[0]=i; 103 | //! //Mine on plugin loaded at index 0 (which should be only one loaded in 104 | //! //Sync mode 105 | //! let result = miner.mine(&header, &mut solution, 0).unwrap(); 106 | //! iterations+=1; 107 | //! if result == true { 108 | //! println!("Solution found after {} iterations: {}", i, solution); 109 | //! println!("For hash: {:?}", header); 110 | //! break; 111 | //! } 112 | //! if time::get_time().sec > deadline { 113 | //! println!("Exiting after {} iterations", iterations); 114 | //! break; 115 | //! } 116 | //! if time::get_time().sec >= next_stat_check { 117 | //! let stats_vec=miner.get_stats(0).unwrap(); 118 | //! for s in stats_vec.into_iter() { 119 | //! let last_solution_time_secs = s.last_solution_time as f64 / 1000.0; 120 | //! let last_hashes_per_sec = 1.0 / last_solution_time_secs; 121 | //! println!("Plugin 0 - Device {} ({}) - Last Solution time: {}; Solutions per second: {:.*}", 122 | //! s.device_id, s.device_name, last_solution_time_secs, 3, last_hashes_per_sec); 123 | //! } 124 | //! next_stat_check = time::get_time().sec + stat_check_interval; 125 | //! } 126 | //! i+=1; 127 | //! if i==255 { 128 | //! i=0; 129 | //! } 130 | //! # break; 131 | //! } 132 | //! # break; 133 | //! } 134 | //! ``` 135 | //! 136 | //! ## Example - Async mode 137 | //! ``` 138 | //! extern crate cuckoo_miner as cuckoo; 139 | //! extern crate time; 140 | //! 141 | //! use std::path::PathBuf; 142 | //! 143 | //! //Grin Pre and Post headers, into which a nonce is to be insterted for mutation 144 | //! let SAMPLE_GRIN_PRE_HEADER_1:&str = "00000000000000118e0fe6bcfaa76c6795592339f27b6d330d8f9c4ac8e86171a66357d1\ 145 | //! d0fce808000000005971f14f0000000000000000000000000000000000000000000000000000000000000000\ 146 | //! 3e1fcdd453ce51ffbb16dd200aeb9ef7375aec196e97094868428a7325e4a19b00"; 147 | //! 148 | //! let SAMPLE_GRIN_POST_HEADER_1:&str = "010a020364"; 149 | //! 150 | //! let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 151 | //! d.push("target/debug/plugins/"); 152 | //! 153 | //! // load plugin manager 154 | //! let mut plugin_manager = cuckoo::CuckooPluginManager::new().unwrap(); 155 | //! plugin_manager 156 | //! .load_plugin_dir(String::from(d.to_str().unwrap())) 157 | //! .expect(""); 158 | //! 159 | //! // Load a single pugin using a filter 160 | //! let caps=plugin_manager.get_available_plugins("lean_cpu_16").unwrap(); 161 | //! let mut config_vec=Vec::new(); 162 | //! let mut config = cuckoo::CuckooMinerConfig::new(); 163 | //! config.plugin_full_path = caps[0].full_path.clone(); 164 | //! config_vec.push(config); 165 | //! 166 | //! let duration_in_seconds=60; 167 | //! let stat_check_interval = 3; 168 | //! let deadline = time::get_time().sec + duration_in_seconds; 169 | //! let mut next_stat_check = time::get_time().sec + stat_check_interval; 170 | //! let mut stats_updated=false; 171 | //! 172 | //! while time::get_time().sec < deadline { 173 | //! 174 | //! println!("Test mining for {} seconds, looking for difficulty > 0", duration_in_seconds); 175 | //! let mut i=0; 176 | //! for c in config_vec.clone().into_iter(){ 177 | //! println!("Plugin {}: {}", i, c.plugin_full_path); 178 | //! i+=1; 179 | //! } 180 | //! 181 | //! // these always get consumed after a notify 182 | //! let miner = cuckoo::CuckooMiner::new(config_vec.clone()).expect(""); 183 | //! let job_handle = miner.notify(1, SAMPLE_GRIN_PRE_HEADER_1, SAMPLE_GRIN_POST_HEADER_1, 0).unwrap(); 184 | //! 185 | //! loop { 186 | //! if let Some(s) = job_handle.get_solution() { 187 | //! println!("Sol found: {}, {:?}", s.get_nonce_as_u64(), s); 188 | //! // up to you to read it and check difficulty 189 | //! continue; 190 | //! } 191 | //! if time::get_time().sec >= next_stat_check { 192 | //! let mut sps_total=0.0; 193 | //! for index in 0..config_vec.len() { 194 | //! let stats_vec=job_handle.get_stats(index).unwrap(); 195 | //! for s in stats_vec.into_iter() { 196 | //! let last_solution_time_secs = s.last_solution_time as f64 / 1000.0; 197 | //! let last_hashes_per_sec = 1.0 / last_solution_time_secs; 198 | //! println!("Plugin {} - Device {} ({}) - Last Solution time: {}; Solutions per second: {:.*}", 199 | //! index,s.device_id, s.device_name, last_solution_time_secs, 3, last_hashes_per_sec); 200 | //! if last_hashes_per_sec.is_finite() { 201 | //! sps_total+=last_hashes_per_sec; 202 | //! } 203 | //! if last_solution_time_secs > 0.0 { 204 | //! stats_updated = true; 205 | //! } 206 | //! i+=1; 207 | //! } 208 | //! } 209 | //! println!("Total solutions per second: {}", sps_total); 210 | //! next_stat_check = time::get_time().sec + stat_check_interval; 211 | //! } 212 | //! if time::get_time().sec > deadline { 213 | //! println!("Stopping jobs and waiting for cleanup"); 214 | //! job_handle.stop_jobs(); 215 | //! break; 216 | //! } 217 | //! # break; 218 | //! } 219 | //! # break; 220 | //! } 221 | //! ``` 222 | 223 | #![deny(non_upper_case_globals)] 224 | #![deny(non_camel_case_types)] 225 | #![deny(non_snake_case)] 226 | #![deny(unused_mut)] 227 | #![warn(missing_docs)] 228 | 229 | #[macro_use] 230 | extern crate log; 231 | extern crate env_logger; 232 | 233 | extern crate serde; 234 | #[macro_use] 235 | extern crate serde_derive; 236 | extern crate serde_json; 237 | 238 | extern crate regex; 239 | extern crate rand; 240 | extern crate byteorder; 241 | extern crate crypto; 242 | extern crate blake2_rfc as blake2; 243 | 244 | extern crate libloading as libloading; 245 | extern crate libc; 246 | 247 | extern crate glob; 248 | 249 | mod error; 250 | mod miner; 251 | mod manager; 252 | mod cuckoo_sys; 253 | 254 | pub use error::error::CuckooMinerError; 255 | 256 | pub use miner::miner::{CuckooMinerConfig, CuckooMiner, CuckooMinerSolution, CuckooMinerJobHandle, 257 | CuckooMinerDeviceStats}; 258 | 259 | pub use manager::manager::{CuckooPluginManager, CuckooPluginCapabilities}; 260 | 261 | pub use cuckoo_sys::manager::PluginLibrary; 262 | -------------------------------------------------------------------------------- /src/manager/manager.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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 the cuckoo_miner plugin manager, which queries 16 | //! all available plugins in a particular directory and returns their 17 | //! descriptions, parameters, and capabilities. 18 | //! 19 | //! #Example 20 | //! ``` 21 | //! extern crate cuckoo_miner as cuckoo; 22 | //! use std::path::PathBuf; 23 | //! let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 24 | //! d.push("target/debug/plugins/"); 25 | //! 26 | //! // load plugin manager 27 | //! let mut plugin_manager = cuckoo::CuckooPluginManager::new().unwrap(); 28 | //! plugin_manager 29 | //! .load_plugin_dir(String::from(d.to_str().unwrap())) 30 | //! .expect(""); 31 | //! 32 | //! let caps = plugin_manager.get_available_plugins("").unwrap(); 33 | //! //Print all available plugins 34 | //! for c in &caps { 35 | //! println!("Found plugin: [{}]", c); 36 | //! } 37 | /// ``` 38 | 39 | use std::fmt; 40 | use std::env; 41 | use std::path::Path; 42 | 43 | use regex::Regex; 44 | use glob::glob; 45 | 46 | use serde_json; 47 | 48 | use cuckoo_sys::manager::PluginLibrary; 49 | use error::error::CuckooMinerError; 50 | 51 | // OS-specific library extensions 52 | 53 | static DLL_SUFFIX: &str = "cuckooplugin"; 54 | 55 | // Helper function to get the absolute path from a relative path 56 | 57 | fn abspath + ?Sized>(relpath: &P) -> String { 58 | let result = env::current_dir().map(|p| p.join(relpath.as_ref())); 59 | let full_path = result.unwrap(); 60 | String::from(full_path.to_str().unwrap()) 61 | } 62 | 63 | /// A wrapper for details that a plugin can report via it's cuckoo_description 64 | /// function. Basic at the moment, but will be extended. 65 | #[derive(Debug, Clone)] 66 | pub struct CuckooPluginCapabilities { 67 | /// The full path to the plugin 68 | pub full_path: String, 69 | 70 | /// The plugin's file name 71 | pub file_name: String, 72 | 73 | /// The plugin's reported parameters 74 | pub parameters: Vec, 75 | } 76 | 77 | impl Default for CuckooPluginCapabilities { 78 | fn default() -> CuckooPluginCapabilities { 79 | CuckooPluginCapabilities { 80 | full_path: String::from(""), 81 | file_name: String::from(""), 82 | parameters: Vec::new(), 83 | } 84 | } 85 | } 86 | 87 | impl fmt::Display for CuckooPluginCapabilities { 88 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 89 | write!( 90 | f, 91 | "Path:{}\nParameters:{}\n", 92 | self.full_path, 93 | serde_json::to_string(&self.parameters).unwrap() 94 | ) 95 | } 96 | } 97 | 98 | /// Holds a set of plugin parameter descriptions returned from a plugin 99 | /// as deserialised from json 100 | 101 | #[derive(Debug, Serialize, Deserialize, Clone)] 102 | pub struct CuckooPluginParameter { 103 | /// The name of the parameter 104 | pub name: String, 105 | 106 | /// Description of the parameter 107 | pub description: String, 108 | 109 | /// The default value of the parameter, used if none is provided 110 | pub default_value: u32, 111 | 112 | /// The minimum allowed value for the parameter 113 | pub min_value: u32, 114 | 115 | /// The maximum allowed value for the parameter 116 | pub max_value: u32, 117 | } 118 | 119 | /// A structure that loads and queries all of the plugins in a particular 120 | /// directory via their [`cuckoo_description`] 121 | /// (struct.PluginLibrary.html#method.call_cuckoo_description) method 122 | 123 | pub struct CuckooPluginManager { 124 | // The current directory 125 | plugin_dir: String, 126 | 127 | // Holds the current set of plugin capabilities, as returned 128 | // from all of the plugins in the plugin directory 129 | current_plugin_caps: Option>, 130 | } 131 | 132 | impl Default for CuckooPluginManager { 133 | fn default() -> CuckooPluginManager { 134 | CuckooPluginManager { 135 | plugin_dir: String::from("target/debug"), 136 | current_plugin_caps: None, 137 | } 138 | } 139 | } 140 | 141 | impl CuckooPluginManager { 142 | /// #Description 143 | /// 144 | /// Returns a new CuckooPluginManager. The default value of the 145 | /// plugin directory is "target/debug" to correspond with cargo's 146 | /// default location. 147 | /// 148 | /// #Arguments 149 | /// 150 | /// None 151 | /// 152 | /// #Returns 153 | /// 154 | /// * `Ok` if successful 155 | /// * a [CuckooMinerError](enum.CuckooMinerError.html) 156 | /// with specific detail if an error is encountered. 157 | /// 158 | 159 | pub fn new() -> Result { 160 | Ok(CuckooPluginManager::default()) 161 | } 162 | 163 | /// #Description 164 | /// 165 | /// Loads all available plugins in the specified directory one by one, 166 | /// calls their cuckoo_description functions, and stores an internal vector 167 | /// of [CuckooPluginCapabilities](struct.CuckooPluginCapabilities.html) 168 | /// representing the plugins in the directory. This will parse any file 169 | /// with the extension `.cuckooplugin`. 170 | /// 171 | /// #Arguments 172 | /// 173 | /// * `plugin_dir` (IN) The path to the prefered plugin directory. This can 174 | /// be either relative to the current directory or a full path. This will 175 | /// be resolved to a full path before calling each plugin. 176 | /// 177 | /// #Returns 178 | /// 179 | /// * `Ok` if successful 180 | /// * [CuckooMinerError](enum.CuckooMinerError.html) 181 | /// with specific detail if an error is encountered. 182 | /// 183 | 184 | pub fn load_plugin_dir(&mut self, plugin_dir: String) -> Result<(), CuckooMinerError> { 185 | self.plugin_dir = plugin_dir.clone(); 186 | let caps = self.load_all_plugin_caps(&plugin_dir)?; 187 | self.current_plugin_caps = Some(caps); 188 | Ok(()) 189 | } 190 | 191 | /// #Description 192 | /// 193 | /// Returns an list of 194 | /// [CuckooPluginCapabilities](struct.CuckooPluginCapabilities.html) 195 | /// representing the currently installed plugins in the currently 196 | /// loaded directory. Can optionally take a filter, 197 | /// which will limit the returned plugins to those with the occurrence of a 198 | /// particular string in their name. 199 | /// 200 | /// #Arguments 201 | /// 202 | /// * `filter` If an empty string, return all of the plugins found in the 203 | /// directory. Otherwise, only return plugins containing a occurrence of this 204 | /// string in their filename. 205 | /// 206 | /// #Returns 207 | /// 208 | /// *If successful, a Result containing a vector of 209 | /// [CuckooPluginCapabilities](struct.CuckooPluginCapabilities.html) , 210 | /// one for each plugin successfully read from the plugin directory, 211 | /// filtered as requested. 212 | /// *If there is an error loading plugins from the given directory, 213 | /// a [CuckooMinerError](enum.CuckooMinerError.html) 214 | /// will be returned outlining more specific details. 215 | /// 216 | /// #Example 217 | /// 218 | /// ``` 219 | /// extern crate cuckoo_miner as cuckoo; 220 | /// use std::path::PathBuf; 221 | /// let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 222 | /// d.push("target/debug/plugins/"); 223 | /// 224 | /// // load plugin manager 225 | /// let mut plugin_manager = cuckoo::CuckooPluginManager::new().unwrap(); 226 | /// plugin_manager 227 | /// .load_plugin_dir(String::from(d.to_str().unwrap())) 228 | /// .expect(""); 229 | /// 230 | /// let caps = plugin_manager.get_available_plugins("").unwrap(); 231 | /// 232 | /// //Print all available plugins 233 | /// for c in &caps { 234 | /// println!("Found plugin: [{}]", c); 235 | /// } 236 | /// ``` 237 | /// 238 | 239 | pub fn get_available_plugins( 240 | &mut self, 241 | filter: &str, 242 | ) -> Result, CuckooMinerError> { 243 | if filter.len() == 0 { 244 | return Ok(self.current_plugin_caps.as_mut().unwrap().clone()); 245 | } else { 246 | let result = self.current_plugin_caps 247 | .as_mut() 248 | .unwrap() 249 | .clone() 250 | .into_iter() 251 | .filter(|ref i| { 252 | let re = Regex::new(&format!(r"{}", filter)).unwrap(); 253 | let caps = re.captures(&i.full_path); 254 | match caps { 255 | Some(_) => return true, 256 | None => return false, 257 | } 258 | }) 259 | .collect::>(); 260 | if result.len() == 0 { 261 | return Err(CuckooMinerError::NoPluginsFoundError( 262 | format!("For given filter: {}", filter), 263 | )); 264 | } 265 | return Ok(result); 266 | } 267 | } 268 | 269 | /// Fills out and Returns a CuckooPluginCapabilities structure parsed from a 270 | /// call to cuckoo_description in the currently loaded plugin 271 | 272 | fn load_plugin_caps( 273 | &mut self, 274 | full_path: String, 275 | ) -> Result { 276 | debug!("Querying plugin at {}", full_path); 277 | let library = PluginLibrary::new(&full_path).unwrap(); 278 | let mut caps = CuckooPluginCapabilities::default(); 279 | 280 | caps.full_path = full_path.clone(); 281 | caps.file_name = String::from(""); 282 | 283 | let mut param_list_bytes: [u8; 4096] = [0; 4096]; 284 | let mut param_list_len = param_list_bytes.len() as u32; 285 | // get a list of parameters 286 | library.call_cuckoo_parameter_list(&mut param_list_bytes, &mut param_list_len); 287 | let mut param_list_vec: Vec = Vec::new(); 288 | // result contains null zero 289 | for i in 0..param_list_len { 290 | param_list_vec.push(param_list_bytes[i as usize].clone()); 291 | } 292 | let param_list_json = String::from_utf8(param_list_vec)?; 293 | caps.parameters = serde_json::from_str(¶m_list_json).unwrap(); 294 | 295 | library.unload(); 296 | 297 | return Ok(caps); 298 | } 299 | 300 | /// Loads and fills out the internal plugin capabilites vector from the 301 | /// given directory. 302 | 303 | fn load_all_plugin_caps( 304 | &mut self, 305 | plugin_dir: &str, 306 | ) -> Result, CuckooMinerError> { 307 | let lib_full_path = abspath(Path::new(&plugin_dir)); 308 | let glob_search_path = format!("{}/*.{}", lib_full_path, DLL_SUFFIX); 309 | 310 | let mut result_vec: Vec = Vec::new(); 311 | 312 | for entry in glob(&glob_search_path).expect("Failed to read glob pattern") { 313 | match entry { 314 | Ok(path) => { 315 | let caps = self.load_plugin_caps(String::from(path.to_str().unwrap()))?; 316 | result_vec.push(caps); 317 | } 318 | Err(e) => error!("{:?}", e), 319 | } 320 | } 321 | 322 | if result_vec.len() == 0 { 323 | return Err(CuckooMinerError::NoPluginsFoundError(format!( 324 | "No plugins found in plugin directory {}", 325 | lib_full_path.clone() 326 | ))); 327 | } 328 | 329 | Ok(result_vec) 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /src/manager/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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 | //! Cuckoo-miner's manager manages the loading, unloading, and querying 16 | //! of mining plugins installed on the system. It is meant as a helper 17 | //! to users of cuckoo-miner, to allow quick enumeration of all mining plugins, 18 | //! and return information about whether a particular plugin can be run 19 | //! on the host system. 20 | //! 21 | //! Although a plugin can only return its name and description at the moment, 22 | //! it will be extended in future to allow for other information such as 23 | //! version, and whether it can be run on a particular system. 24 | //! 25 | 26 | #![deny(non_upper_case_globals)] 27 | #![deny(non_camel_case_types)] 28 | #![deny(non_snake_case)] 29 | #![deny(unused_mut)] 30 | #![warn(missing_docs)] 31 | 32 | extern crate regex; 33 | extern crate glob; 34 | 35 | pub mod manager; 36 | -------------------------------------------------------------------------------- /src/miner/delegator.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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 | //! Internal module responsible for job delegation, creating hashes 16 | //! and sending them to the plugin's internal queues. Used internally 17 | //! 18 | //! 19 | 20 | use std::sync::{Arc, RwLock}; 21 | use std::{thread, time}; 22 | use std::mem::transmute; 23 | 24 | use rand::{self, Rng}; 25 | use byteorder::{ByteOrder, BigEndian}; 26 | use blake2::blake2b::Blake2b; 27 | use env_logger; 28 | 29 | use cuckoo_sys::manager::PluginLibrary; 30 | use error::error::CuckooMinerError; 31 | use CuckooMinerJobHandle; 32 | use CuckooMinerSolution; 33 | 34 | /// From grin 35 | /// The target is the 8-bytes hash block hashes must be lower than. 36 | const MAX_TARGET: [u8; 8] = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; 37 | 38 | type JobSharedDataType = Arc>; 39 | type JobControlDataType = Arc>; 40 | type PluginLibrariesDataType = Arc>>; 41 | 42 | /// Data intended to be shared across threads 43 | pub struct JobSharedData { 44 | /// ID of the current running job (not currently used) 45 | pub job_id: u32, 46 | 47 | /// The part of the header before the nonce, which this 48 | /// module will mutate in search of a solution 49 | pub pre_nonce: String, 50 | 51 | /// The part of the header after the nonce 52 | pub post_nonce: String, 53 | 54 | /// The target difficulty. Only solutions >= this 55 | /// target will be put into the output queue 56 | pub difficulty: u64, 57 | 58 | /// Output solutions 59 | pub solutions: Vec, 60 | } 61 | 62 | impl Default for JobSharedData { 63 | fn default() -> JobSharedData { 64 | JobSharedData { 65 | job_id: 0, 66 | pre_nonce: String::from(""), 67 | post_nonce: String::from(""), 68 | difficulty: 0, 69 | solutions: Vec::new(), 70 | } 71 | } 72 | } 73 | 74 | impl JobSharedData { 75 | pub fn new(job_id: u32, pre_nonce: &str, post_nonce: &str, difficulty: u64) -> JobSharedData { 76 | JobSharedData { 77 | job_id: job_id, 78 | pre_nonce: String::from(pre_nonce), 79 | post_nonce: String::from(post_nonce), 80 | difficulty: difficulty, 81 | solutions: Vec::new(), 82 | } 83 | } 84 | } 85 | 86 | /// an internal structure to flag job control 87 | 88 | pub struct JobControlData { 89 | /// Whether the mining job is running 90 | pub stop_flag: bool, 91 | 92 | /// Whether all plugins have stopped 93 | pub has_stopped: bool, 94 | } 95 | 96 | impl Default for JobControlData { 97 | fn default() -> JobControlData { 98 | JobControlData { 99 | stop_flag: false, 100 | has_stopped: false, 101 | } 102 | } 103 | } 104 | 105 | /// Internal structure which controls and runs processing jobs. 106 | /// 107 | /// 108 | 109 | pub struct Delegator { 110 | /// Data which is shared across all threads 111 | shared_data: JobSharedDataType, 112 | 113 | /// Job control flags which are shared across threads 114 | control_data: JobControlDataType, 115 | 116 | /// Loaded Plugin Library 117 | libraries: PluginLibrariesDataType, 118 | } 119 | 120 | impl Delegator { 121 | /// Create a new job delegator 122 | 123 | pub fn new(job_id: u32, pre_nonce: &str, post_nonce: &str, difficulty: u64, libraries:Vec) -> Delegator { 124 | Delegator { 125 | shared_data: Arc::new(RwLock::new(JobSharedData::new( 126 | job_id, 127 | pre_nonce, 128 | post_nonce, 129 | difficulty, 130 | ))), 131 | control_data: Arc::new(RwLock::new(JobControlData::default())), 132 | libraries: Arc::new(RwLock::new(libraries)), 133 | } 134 | } 135 | 136 | /// Starts the job loop, and initialises the internal plugin 137 | 138 | pub fn start_job_loop(self, hash_header: bool) -> Result { 139 | let _=env_logger::init(); 140 | // this will block, waiting until previous job is cleared 141 | // call_cuckoo_stop_processing(); 142 | 143 | let shared_data = self.shared_data.clone(); 144 | let control_data = self.control_data.clone(); 145 | let jh_library = self.libraries.clone(); 146 | 147 | thread::spawn(move || { 148 | let result = self.job_loop(hash_header); 149 | if let Err(e) = result { 150 | error!("Error in job loop: {:?}", e); 151 | } 152 | }); 153 | Ok(CuckooMinerJobHandle { 154 | shared_data: shared_data, 155 | control_data: control_data, 156 | library: jh_library, 157 | }) 158 | } 159 | 160 | /// Helper to convert a hex string 161 | 162 | fn from_hex_string(&self, in_str: &str) -> Vec { 163 | let mut bytes = Vec::new(); 164 | for i in 0..(in_str.len() / 2) { 165 | let res = u8::from_str_radix(&in_str[2 * i..2 * i + 2], 16); 166 | match res { 167 | Ok(v) => bytes.push(v), 168 | Err(e) => println!("Problem with hex: {}", e), 169 | } 170 | } 171 | bytes 172 | } 173 | 174 | /// As above, except doesn't hash the result 175 | fn header_data(&self, pre_nonce: &str, post_nonce: &str, nonce: u64) -> Vec { 176 | // Turn input strings into vectors 177 | let mut pre_vec = self.from_hex_string(pre_nonce); 178 | let mut post_vec = self.from_hex_string(post_nonce); 179 | 180 | let mut nonce_bytes = [0; 8]; 181 | BigEndian::write_u64(&mut nonce_bytes, nonce); 182 | let mut nonce_vec = nonce_bytes.to_vec(); 183 | 184 | // Generate new header 185 | pre_vec.append(&mut nonce_vec); 186 | pre_vec.append(&mut post_vec); 187 | 188 | pre_vec 189 | } 190 | /// helper that generates a nonce and returns a header 191 | 192 | fn get_next_header_data_hashed(&self, pre_nonce: &str, post_nonce: &str) -> (u64, Vec) { 193 | // Generate new nonce 194 | let nonce: u64 = rand::OsRng::new().unwrap().gen(); 195 | let mut blake2b = Blake2b::new(32); 196 | blake2b.update(&self.header_data(pre_nonce, post_nonce, nonce)); 197 | 198 | let mut ret = [0; 32]; 199 | ret.copy_from_slice(blake2b.finalize().as_bytes()); 200 | (nonce, ret.to_vec()) 201 | } 202 | 203 | /// as above, except doesn't hash the result 204 | fn get_next_header_data(&self, pre_nonce: &str, post_nonce: &str) -> (u64, Vec) { 205 | let nonce: u64 = rand:: OsRng::new().unwrap().gen(); 206 | (nonce, self.header_data(pre_nonce, post_nonce, nonce)) 207 | } 208 | 209 | /// Helper to determing whether a solution meets a target difficulty 210 | /// based on same algorithm from grin 211 | 212 | fn meets_difficulty(&self, in_difficulty: u64, sol: CuckooMinerSolution) -> bool { 213 | let max_target = BigEndian::read_u64(&MAX_TARGET); 214 | let num = BigEndian::read_u64(&sol.hash()[0..8]); 215 | max_target / num >= in_difficulty 216 | } 217 | 218 | /// The main job loop. Pushes hashes to the plugin and reads solutions 219 | /// from the queue, putting them into the job's output queue. Continues 220 | /// until another thread sets the is_running flag to false 221 | 222 | fn job_loop(self, hash_header: bool) -> Result<(), CuckooMinerError> { 223 | // keep some unchanging data here, can move this out of shared 224 | // object later if it's not needed anywhere else 225 | let pre_nonce: String; 226 | let post_nonce: String; 227 | // generate an identifier to ensure we're only reading our 228 | // jobs from the queue 229 | let queue_id: u32 = rand::OsRng::new().unwrap().gen(); 230 | let difficulty; 231 | { 232 | let s = self.shared_data.read().unwrap(); 233 | pre_nonce = s.pre_nonce.clone(); 234 | post_nonce = s.post_nonce.clone(); 235 | difficulty = s.difficulty; 236 | } 237 | debug!( 238 | "Cuckoo-miner: Searching for solution >= difficulty {}", 239 | difficulty 240 | ); 241 | 242 | for l in self.libraries.read().unwrap().iter() { 243 | l.call_cuckoo_start_processing(); 244 | } 245 | 246 | debug!("Cuckoo Miner Job loop processing"); 247 | let mut solution = CuckooMinerSolution::new(); 248 | 249 | loop { 250 | // Check if it's time to stop 251 | { 252 | let s = self.control_data.read().unwrap(); 253 | if s.stop_flag { 254 | break; 255 | } 256 | } 257 | for l in self.libraries.read().unwrap().iter() { 258 | while l.call_cuckoo_is_queue_under_limit() == 1 { 259 | let (nonce, data) = match hash_header { 260 | true => self.get_next_header_data_hashed(&pre_nonce, &post_nonce), 261 | false => self.get_next_header_data(&pre_nonce, &post_nonce), 262 | }; 263 | // TODO: make this a serialise operation instead 264 | let nonce_bytes: [u8; 8] = unsafe { transmute(nonce.to_be()) }; 265 | l.call_cuckoo_push_to_input_queue(queue_id, &data, &nonce_bytes); 266 | } 267 | } 268 | 269 | let mut plugin_index=0; 270 | for l in self.libraries.read().unwrap().iter() { 271 | let mut qid:u32 = 0; 272 | while l.call_cuckoo_read_from_output_queue( 273 | &mut qid, 274 | &mut solution.solution_nonces, 275 | &mut solution.cuckoo_size, 276 | &mut solution.nonce, 277 | ) != 0 278 | { 279 | // TODO: make this a serialise operation instead 280 | let nonce = unsafe { transmute::<[u8; 8], u64>(solution.nonce) }.to_be(); 281 | 282 | if self.meets_difficulty(difficulty, solution) && qid == queue_id { 283 | debug!( 284 | "Cuckoo-miner plugin[{}]: Solution Found for Nonce:({}), {:?}", 285 | plugin_index, 286 | nonce, 287 | solution 288 | ); 289 | let mut s = self.shared_data.write().unwrap(); 290 | s.solutions.push(solution.clone()); 291 | plugin_index+=1; 292 | } 293 | 294 | } 295 | } 296 | //avoid busy wait 297 | let sleep_dur = time::Duration::from_millis(100); 298 | thread::sleep(sleep_dur); 299 | } 300 | 301 | // Do any cleanup 302 | for l in self.libraries.read().unwrap().iter() { 303 | l.call_cuckoo_stop_processing(); 304 | } 305 | for l in self.libraries.read().unwrap().iter() { 306 | //wait for internal processing to finish 307 | while l.call_cuckoo_has_processing_stopped()==0{ 308 | thread::sleep(time::Duration::from_millis(1)); 309 | }; 310 | l.call_cuckoo_reset_processing(); 311 | } 312 | let mut s = self.control_data.write().unwrap(); 313 | s.has_stopped=true; 314 | Ok(()) 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /src/miner/miner.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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::sync::{Arc, RwLock}; 20 | use std::{thread, time}; 21 | use std::{fmt, cmp}; 22 | use std::path::Path; 23 | 24 | use byteorder::{ByteOrder, BigEndian}; 25 | use blake2::blake2b::Blake2b; 26 | 27 | use serde_json; 28 | 29 | use super::delegator:: {JobSharedData, JobControlData, Delegator}; 30 | use cuckoo_sys::manager::PluginLibrary; 31 | use error::error::CuckooMinerError; 32 | 33 | // Hardcoded assumption for now that the solution size will be 42 will be 34 | // maintained, to avoid having to allocate memory within the called C functions 35 | 36 | const CUCKOO_SOLUTION_SIZE: usize = 42; 37 | 38 | /// A simple struct to hold a cuckoo miner solution. Currently, 39 | /// it's assumed that a solution will be 42 bytes. The `solution_nonces` 40 | /// member is statically allocated here, and will be filled in 41 | /// by a plugin upon finding a solution. 42 | 43 | #[derive(Copy)] 44 | pub struct CuckooMinerSolution { 45 | /// Cuckoo size 46 | pub cuckoo_size: u32, 47 | /// An array allocated in rust that will be filled 48 | /// by the called plugin upon successfully finding 49 | /// a solution 50 | pub solution_nonces: [u32; CUCKOO_SOLUTION_SIZE], 51 | 52 | /// The nonce that was used to generate the 53 | /// hash for which a solution was found 54 | pub nonce: [u8; 8], 55 | } 56 | 57 | impl Default for CuckooMinerSolution { 58 | fn default() -> CuckooMinerSolution { 59 | CuckooMinerSolution { 60 | cuckoo_size: 30, 61 | solution_nonces: [0; CUCKOO_SOLUTION_SIZE], 62 | nonce: [0; 8], 63 | } 64 | } 65 | } 66 | 67 | impl Clone for CuckooMinerSolution { 68 | fn clone(&self) -> CuckooMinerSolution { 69 | *self 70 | } 71 | } 72 | 73 | impl CuckooMinerSolution { 74 | /// Creates a new cuckoo miner solution 75 | /// with nonces set to a u32 array of size 76 | /// 42 filled with zeroes. 77 | 78 | pub fn new() -> CuckooMinerSolution { 79 | CuckooMinerSolution::default() 80 | } 81 | 82 | /// Sets the solution, mostly for testing 83 | pub fn set_solution(&mut self, nonces: [u32; CUCKOO_SOLUTION_SIZE]) { 84 | self.solution_nonces = nonces; 85 | } 86 | 87 | /// return the nonce as a u64, for convenience 88 | pub fn get_nonce_as_u64(&self) -> u64 { 89 | BigEndian::read_u64(&self.nonce) 90 | } 91 | 92 | /// Converts the proof to a vector of u64s 93 | pub fn to_u64s(&self) -> Vec { 94 | let mut nonces = Vec::with_capacity(CUCKOO_SOLUTION_SIZE); 95 | for n in self.solution_nonces.iter() { 96 | nonces.push(*n as u64); 97 | } 98 | nonces 99 | } 100 | 101 | /// Returns the has of the solution, as performed in 102 | /// grin 103 | pub fn hash(&self) -> [u8; 32] { 104 | // Hash 105 | let mut blake2b = Blake2b::new(32); 106 | for n in 0..self.solution_nonces.len() { 107 | let mut bytes = [0; 4]; 108 | BigEndian::write_u32(&mut bytes, self.solution_nonces[n]); 109 | blake2b.update(&bytes); 110 | } 111 | let mut ret = [0; 32]; 112 | ret.copy_from_slice(blake2b.finalize().as_bytes()); 113 | ret 114 | } 115 | } 116 | 117 | impl fmt::Display for CuckooMinerSolution { 118 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 119 | let mut comma_separated = String::new(); 120 | 121 | for num in &self.solution_nonces[0..self.solution_nonces.len()] { 122 | comma_separated.push_str(&format!("0x{:X}", &num)); 123 | comma_separated.push_str(", "); 124 | } 125 | comma_separated.pop(); 126 | comma_separated.pop(); 127 | 128 | write!(f, "[{}]", comma_separated) 129 | } 130 | } 131 | 132 | impl fmt::Debug for CuckooMinerSolution { 133 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 134 | write!(f, "{:?}", &self.solution_nonces[..]) 135 | } 136 | } 137 | 138 | impl cmp::PartialEq for CuckooMinerSolution { 139 | fn eq(&self, other: &CuckooMinerSolution) -> bool { 140 | for i in 0..CUCKOO_SOLUTION_SIZE { 141 | if self.solution_nonces[i] != other.solution_nonces[i] { 142 | return false; 143 | } 144 | } 145 | return true; 146 | } 147 | } 148 | 149 | /// Structure containing the configuration values to pass into an 150 | /// instance of a miner 151 | #[derive(Debug, Clone)] 152 | pub struct CuckooMinerConfig { 153 | /// The full path to the plugin to load and use to find a solution 154 | /// to a POW problem. Defaults to empty string, so must be filled 155 | /// before use. 156 | pub plugin_full_path: String, 157 | 158 | /// A parameter list, which differs depending on which 159 | /// plugin is being called 160 | pub parameter_list: Vec<(String, u32, u32)>, 161 | } 162 | 163 | impl Default for CuckooMinerConfig { 164 | fn default() -> CuckooMinerConfig { 165 | CuckooMinerConfig { 166 | plugin_full_path: String::from(""), 167 | parameter_list: Vec::new(), 168 | } 169 | } 170 | } 171 | 172 | impl CuckooMinerConfig { 173 | /// Returns a new instance of CuckooMinerConfig 174 | 175 | pub fn new() -> CuckooMinerConfig { 176 | CuckooMinerConfig::default() 177 | } 178 | } 179 | 180 | /// Holds deserialised performance metrics returned from the 181 | /// plugin 182 | /// 183 | #[derive(Debug, Serialize, Deserialize, Clone)] 184 | pub struct CuckooMinerDeviceStats { 185 | /// The plugin file name (optional so the plugins don't have to deal with it on de/ser) 186 | pub plugin_name: Option, 187 | 188 | /// The internal device id 189 | pub device_id: String, 190 | 191 | /// Cuckoo size currently being used by the device 192 | pub cuckoo_size: String, 193 | 194 | /// The device name 195 | pub device_name: String, 196 | 197 | /// Whether the device is marked for use 198 | pub in_use: u32, 199 | 200 | /// Whether the device has thrown an error (and has stopped) 201 | pub has_errored: u32, 202 | 203 | /// The time at which the device last began to search a hash (epoch in 204 | /// mills) 205 | pub last_start_time: u64, 206 | 207 | /// The time at which the device last completed a solution search (epoch in 208 | /// mills) 209 | pub last_end_time: u64, 210 | 211 | /// The amount of time the last solution search took (epoch in mills) 212 | pub last_solution_time: u64, 213 | 214 | /// The total number of searched performed since init 215 | pub iterations_completed: u32, 216 | } 217 | 218 | /// Handle to the miner's running job, used to read solutions 219 | /// or to control the job. Internal members are not exposed 220 | /// and all interactions should be via public functions 221 | /// This will basically hold an arc reference clone of 222 | /// the Delegator's internal shared data 223 | 224 | pub struct CuckooMinerJobHandle { 225 | /// Data shared across threads 226 | pub shared_data: Arc>, 227 | 228 | /// Job control flag 229 | pub control_data: Arc>, 230 | 231 | /// The loaded plugin 232 | pub library: Arc>>, 233 | } 234 | 235 | impl CuckooMinerJobHandle { 236 | 237 | /// #Description 238 | /// 239 | /// Returns a solution if one is currently waiting. 240 | /// 241 | /// #Returns 242 | /// 243 | /// If a solution was found and is waiting in the plugin's input queue, 244 | /// returns 245 | /// * Ok([CuckooMinerSolution](struct.CuckooMinerSolution.html)) if a 246 | /// solution is waiting in the queue. 247 | /// * None if no solution is waiting 248 | 249 | pub fn get_solution(&self) -> Option { 250 | // just to prevent endless needless locking of this 251 | // when using fast test miners, in real cuckoo30 terms 252 | // this shouldn't be an issue 253 | // TODO: Make this less blocky 254 | thread::sleep(time::Duration::from_millis(10)); 255 | // let time_pre_lock=Instant::now(); 256 | let mut s = self.shared_data.write().unwrap(); 257 | // let time_elapsed=Instant::now()-time_pre_lock; 258 | // println!("Get_solution Time spent waiting for lock: {}", 259 | // time_elapsed.as_secs()*1000 +(time_elapsed.subsec_nanos()/1_000_000)as u64); 260 | if s.solutions.len() > 0 { 261 | let sol = s.solutions.pop().unwrap(); 262 | return Some(sol); 263 | } 264 | None 265 | } 266 | 267 | /// #Description 268 | /// 269 | /// Stops the current job, and signals for the loaded plugin to stop 270 | /// processing and perform any cleanup it needs to do. Blocks until 271 | /// the jobs have completed 272 | /// 273 | /// #Returns 274 | /// 275 | /// Nothing 276 | 277 | pub fn stop_jobs(&self) { 278 | debug!("Stop jobs called"); 279 | { 280 | let mut r = self.control_data.write().unwrap(); 281 | r.stop_flag = true; 282 | } 283 | debug!("Stop jobs flag set"); 284 | loop { 285 | { 286 | let r = self.control_data.read().unwrap(); 287 | if r.has_stopped { 288 | break; 289 | } 290 | } 291 | thread::sleep(time::Duration::from_millis(5)); 292 | } 293 | debug!("All jobs have stopped"); 294 | } 295 | 296 | /// #Description 297 | /// 298 | /// Returns an vector of [CuckooMinerDeviceStats](struct.CuckooMinerDeviceStats.html) 299 | /// which will contain information about every device currently mining within the plugin. 300 | /// In CPU based plugins, this will generally only contain the CPU device, but in plugins 301 | /// that access multiple devices (such as cuda) the vector will contain information for 302 | /// each currently engaged device. 303 | /// 304 | /// #Returns 305 | /// 306 | /// * Ok([CuckooMinerDeviceStats](struct.CuckooMinerDeviceStats.html)) if successful 307 | /// * A [CuckooMinerError](enum.CuckooMinerError.html) with specific detail if an 308 | /// error occurred 309 | 310 | pub fn get_stats(&self, plugin_index:usize) -> Result, CuckooMinerError> { 311 | let mut stats_bytes: [u8; 4096] = [0; 4096]; 312 | let mut stats_bytes_len = stats_bytes.len() as u32; 313 | // get a list of parameters 314 | self.library.read().unwrap()[plugin_index].call_cuckoo_get_stats( 315 | &mut stats_bytes, 316 | &mut stats_bytes_len, 317 | ); 318 | let mut stats_vec: Vec = Vec::new(); 319 | // result contains null zero 320 | for i in 0..stats_bytes_len { 321 | stats_vec.push(stats_bytes[i as usize].clone()); 322 | } 323 | let stats_json = String::from_utf8(stats_vec)?; 324 | //println!("Stats_json: {}", stats_json); 325 | 326 | let result = serde_json::from_str(&stats_json); 327 | if let Err(e) = result { 328 | return Err(CuckooMinerError::StatsError( 329 | String::from(format!("Error retrieving stats from plugin: {:?}", e)), 330 | )); 331 | } 332 | 333 | let mut result:Vec = result.unwrap(); 334 | let lib_full_path = &self.library.read().unwrap()[plugin_index].lib_full_path; 335 | let path_str = Path::new(lib_full_path).file_name().unwrap(); 336 | let path = Path::new(path_str).file_stem().unwrap(); 337 | 338 | for r in &mut result { 339 | r.plugin_name = Some(path.to_str().unwrap().to_owned()); 340 | } 341 | 342 | Ok(result) 343 | } 344 | } 345 | 346 | /// An instance of a miner, which loads a cuckoo-miner plugin 347 | /// and calls its mine function according to the provided configuration 348 | 349 | pub struct CuckooMiner { 350 | /// The internal Configuration objects, one for each loaded plugin 351 | pub configs: Vec, 352 | 353 | /// Delegator object, used when spawning a processing thread 354 | delegator: Option, 355 | 356 | /// Loaded plugin 357 | libraries: Vec, 358 | } 359 | 360 | impl CuckooMiner { 361 | /// #Description 362 | /// 363 | /// Creates a new instance of a CuckooMiner with the given configuration. 364 | /// 365 | /// #Arguments 366 | /// 367 | /// * `configs` an vector of 368 | /// [CuckooMinerConfigs](struct.CuckooMinerConfig.html), one for each plugin 369 | /// that is to be loaded and run, and each of which contains 370 | /// the full path name of a valid mining plugin. Each config struct may 371 | /// also contain values in its `parameter_list` field, which will be automatically set 372 | /// in the specified plugin. 373 | /// 374 | /// #Returns 375 | /// 376 | /// * `Ok()` if successful, and the specified plugin has been loaded internally. 377 | /// * Otherwise a [CuckooMinerError](enum.CuckooMinerError.html) 378 | /// with specific detail 379 | 380 | pub fn new(configs: Vec) -> Result { 381 | CuckooMiner::init(configs) 382 | } 383 | 384 | /// Internal function to perform tha actual library loading 385 | 386 | fn init(configs: Vec) -> Result { 387 | let mut lib_vec=Vec::new(); 388 | for c in &configs { 389 | let lib=PluginLibrary::new(&c.plugin_full_path)?; 390 | for elem in c.parameter_list.clone() { 391 | CuckooMiner::set_parameter(elem.0.clone(), elem.1.clone(), elem.2.clone(), &lib)?; 392 | } 393 | lib_vec.push(lib); 394 | } 395 | 396 | let ret_val=CuckooMiner { 397 | configs : configs.clone(), 398 | delegator : None, 399 | libraries : lib_vec, 400 | }; 401 | 402 | Ok(ret_val) 403 | } 404 | 405 | /// #Description 406 | /// 407 | /// Sets a parameter in the loaded plugin 408 | /// 409 | /// #Arguments 410 | /// 411 | /// * `name` The name of the parameter to set 412 | /// 413 | /// * `value` The value to set the parameter to 414 | /// 415 | /// #Returns 416 | /// 417 | /// *`Ok()` if successful and the parameter has been set. 418 | /// * Otherwise a 419 | /// [CuckooMinerError](enum.CuckooMinerError.html) 420 | /// with specific detail is returned. 421 | /// 422 | 423 | pub fn set_parameter(name: String, device_id: u32, value: u32, library:&PluginLibrary) -> Result<(), CuckooMinerError> { 424 | let return_code = library.call_cuckoo_set_parameter( 425 | name.as_bytes(), 426 | device_id, 427 | value, 428 | ); 429 | if return_code != 0 { 430 | 431 | let reason = match return_code { 432 | 1 => "Property doesn't exist for this plugin", 433 | 2 => "Property outside allowed range", 434 | 5 => "Device doesn't exist", 435 | _ => "Unknown Error", 436 | }; 437 | 438 | return Err(CuckooMinerError::ParameterError(String::from(format!( 439 | "Error setting parameter: {} to {} - {}", 440 | name, 441 | value, 442 | reason 443 | )))); 444 | } 445 | Ok(()) 446 | } 447 | 448 | /// #Description 449 | /// 450 | /// Synchronous call to the cuckoo_call function of the currently loaded 451 | /// plugin, which will perform 452 | /// a Cuckoo Cycle on the given seed, filling the first solution (a length 453 | /// 42 cycle) that is found in the provided 454 | /// [CuckooMinerSolution](struct.CuckooMinerSolution.html) structure. 455 | /// The implementation details are dependent on the particular loaded plugin. 456 | /// Values provided 457 | /// to the loaded plugin are contained in the internal 458 | /// [CuckooMinerConfig](struct.CuckooMinerConfig.html) 459 | /// 460 | /// #Arguments 461 | /// 462 | /// * `header` (IN) A reference to a block of [u8] bytes to use for the 463 | /// seed to the 464 | /// internal SIPHASH function which generates edge locations in the 465 | /// graph. In practice, 466 | /// this is a SHA3 hash of a Grin blockheader, but from the plugin's 467 | /// perspective this can be anything. 468 | /// 469 | /// * `solution` (OUT) An empty 470 | /// [CuckooMinerSolution](struct.CuckooMinerSolution.html). 471 | /// If a solution is found, this structure will contain a list of 472 | /// solution nonces, otherwise, it will remain untouched. 473 | /// 474 | /// #Returns 475 | /// 476 | /// * Ok(true) if a solution is found, with the 42 solution nonces 477 | /// contained within 478 | /// the provided [CuckooMinerSolution](struct.CuckooMinerSolution.html). 479 | /// * Ok(false) if no solution is found and `solution` remains untouched. 480 | /// * A [CuckooMinerError](enum.CuckooMinerError.html) 481 | /// if there is an error calling the function. 482 | 483 | pub fn mine( 484 | &self, 485 | header: &[u8], 486 | cuckoo_size: &mut u32, 487 | solution: &mut CuckooMinerSolution, 488 | plugin_index: usize 489 | ) -> Result { 490 | let result = self.libraries[plugin_index].call_cuckoo( 491 | header, 492 | cuckoo_size, 493 | &mut solution.solution_nonces, 494 | ); 495 | match result { 496 | 1 => { 497 | debug!("Solution found."); 498 | Ok(true) 499 | } 500 | 0 => Ok(false), 501 | _ => Err(CuckooMinerError::UnexpectedResultError(result)), 502 | } 503 | } 504 | 505 | /// #Description 506 | /// 507 | /// Returns an vector of [CuckooMinerDeviceStats](struct.CuckooMinerDeviceStats.html) 508 | /// which will contain information about every device currently mining within the plugin. 509 | /// When called in syncronous mode, this wil only ever return a vector with a single value 510 | /// containing stats on the currently running device. 511 | /// 512 | /// #Returns 513 | /// 514 | /// * Ok([CuckooMinerDeviceStats](struct.CuckooMinerDeviceStats.html)) if successful 515 | /// * A [CuckooMinerError](enum.CuckooMinerError.html) with specific detail if an 516 | /// error occurred 517 | 518 | pub fn get_stats(&self, plugin_index:usize) -> Result, CuckooMinerError> { 519 | let mut stats_bytes: [u8; 2048] = [0; 2048]; 520 | let mut stats_bytes_len = stats_bytes.len() as u32; 521 | // get a list of parameters 522 | self.libraries[plugin_index].call_cuckoo_get_stats( 523 | &mut stats_bytes, 524 | &mut stats_bytes_len, 525 | ); 526 | let mut stats_vec: Vec = Vec::new(); 527 | // result contains null zero 528 | for i in 0..stats_bytes_len { 529 | stats_vec.push(stats_bytes[i as usize].clone()); 530 | } 531 | let stats_json = String::from_utf8(stats_vec)?; 532 | //println!("Stats_json: {}", stats_json); 533 | 534 | let result = serde_json::from_str(&stats_json); 535 | if let Err(e) = result { 536 | return Err(CuckooMinerError::StatsError( 537 | String::from(format!("Error retrieving stats from plugin: {:?}", e)), 538 | )); 539 | } 540 | Ok(result.unwrap()) 541 | } 542 | 543 | /// #Description 544 | /// 545 | /// An asynchronous -esque version of the plugin miner, which takes 546 | /// parts of the header and the target difficulty as input, and begins 547 | /// asyncronous processing to find a solution. The loaded plugin is 548 | /// responsible 549 | /// for how it wishes to manage processing or distribute the load. Once 550 | /// called 551 | /// this function will continue to find solutions over the target difficulty 552 | /// for the given inputs and place them into its output queue until 553 | /// instructed to stop. 554 | /// 555 | /// Once this function is called, the miner is consumed, and all 556 | /// interaction with the miner, 557 | /// including reading solutions or stopping the job, then takes place via 558 | /// the returned 559 | /// [CuckooMinerJobHandle](struct.CuckooMinerJobHandle.html) struct. 560 | /// 561 | /// 562 | /// #Arguments 563 | /// 564 | /// * `job_id` (IN) A job ID, for later reference (not currently used). 565 | /// 566 | /// * `pre_nonce` (IN) The part of the header which comes before the nonce, 567 | /// as a hex string slice. 568 | /// 569 | /// * 'post_nonce` (IN) The part of the header which comes after the nonce 570 | /// as a hex string slice. This will be hashed together with generated 571 | /// nonces and the pre_nonce field to create hash inputs for the loaded 572 | /// cuckoo miner plugin. 573 | /// 574 | /// * `difficulty` (IN) The miner will only put solutions greater than or 575 | /// equal to this difficulty in its output queue. 576 | /// 577 | /// #Returns 578 | /// 579 | /// * Ok([CuckooMinerJobHandle](struct.CuckooMinerJobHandle.html)) if the 580 | /// job 581 | /// is successfully started. 582 | /// * A [CuckooMinerError](enum.CuckooMinerError.html) 583 | /// if there is no plugin loaded, or if there is an error calling the 584 | /// function. 585 | 586 | pub fn notify( 587 | mut self, 588 | job_id: u32, // Job id 589 | pre_nonce: &str, // Pre-nonce portion of header 590 | post_nonce: &str, // Post-nonce portion of header 591 | difficulty: u64, /* The target difficulty, only sols greater than this difficulty will 592 | * be returned. */ 593 | hash_header: bool, // (Temporary) Whether to hash the header before sending (true for testnet2 and earlier) 594 | ) -> Result { 595 | 596 | //Note this gives up the plugin to the job thread 597 | self.delegator = Some(Delegator::new(job_id, pre_nonce, post_nonce, difficulty, self.libraries)); 598 | Ok(self.delegator.unwrap().start_job_loop(hash_header).unwrap()) 599 | } 600 | } 601 | -------------------------------------------------------------------------------- /src/miner/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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 | mod delegator; 25 | pub mod miner; 26 | -------------------------------------------------------------------------------- /src/sanity.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Rust Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Sanity check for environment before attempting to run cmake build 12 | //! shamelessly adapted from: 13 | //! https://raw.githubusercontent. 14 | //! com/rust-lang/rust/master/src/bootstrap/sanity.rs 15 | //! more of this will be adapted later 16 | 17 | use std::collections::HashMap; 18 | use std::env; 19 | use std::ffi::{OsString, OsStr}; 20 | // use std::fs; 21 | // use std::process::Command; 22 | use std::path::PathBuf; 23 | 24 | // use build_helper::output; 25 | 26 | // use Build; 27 | 28 | pub struct Finder { 29 | cache: HashMap>, 30 | path: OsString, 31 | } 32 | 33 | impl Finder { 34 | pub fn new() -> Self { 35 | Self { 36 | cache: HashMap::new(), 37 | path: env::var_os("PATH").unwrap_or_default(), 38 | } 39 | } 40 | 41 | pub fn maybe_have>(&mut self, cmd: S) -> Option { 42 | let cmd: OsString = cmd.as_ref().into(); 43 | let path = self.path.clone(); 44 | self.cache 45 | .entry(cmd.clone()) 46 | .or_insert_with(|| { 47 | for path in env::split_paths(&path) { 48 | let target = path.join(&cmd); 49 | let mut cmd_alt = cmd.clone(); 50 | cmd_alt.push(".exe"); 51 | if target.is_file() || // some/path/git 52 | target.with_extension("exe").exists() || // some/path/git.exe 53 | target.join(&cmd_alt).exists() 54 | { 55 | // some/path/git/git.exe 56 | return Some(target); 57 | } 58 | } 59 | None 60 | }) 61 | .clone() 62 | } 63 | 64 | /*pub fn must_have>(&mut self, cmd: S) -> PathBuf { 65 | self.maybe_have(&cmd).unwrap_or_else(|| { 66 | panic!("\n\ncouldn't find required command: {:?}\n\n", cmd.as_ref()); 67 | }) 68 | }*/ 69 | } 70 | 71 | // Just use finder for now 72 | 73 | /*pub fn check(build: &mut Build) { 74 | let path = env::var_os("PATH").unwrap_or_default(); 75 | // On Windows, quotes are invalid characters for filename paths, and if 76 | // one is present as part of the PATH then that can lead to the system 77 | // being unable to identify the files properly. See 78 | // https://github.com/rust-lang/rust/issues/34959 for more details. 79 | if cfg!(windows) && path.to_string_lossy().contains("\"") { 80 | panic!("PATH contains invalid character '\"'"); 81 | } 82 | 83 | let mut cmd_finder = Finder::new(); 84 | // If we've got a git directory we're gona need git to update 85 | // submodules and learn about various other aspects. 86 | if build.rust_info.is_git() { 87 | cmd_finder.must_have("git"); 88 | } 89 | 90 | // We need cmake, but only if we're actually building LLVM or sanitizers. 91 | let building_llvm = build.hosts.iter() 92 | .filter_map(|host| build.config.target_config.get(host)) 93 | .any(|config| config.llvm_config.is_none()); 94 | if building_llvm || build.config.sanitizers { 95 | cmd_finder.must_have("cmake"); 96 | } 97 | 98 | // Ninja is currently only used for LLVM itself. 99 | // Some Linux distros rename `ninja` to `ninja-build`. 100 | // CMake can work with either binary name. 101 | if building_llvm && build.config.ninja && cmd_finder.maybe_have("ninja-build").is_none() { 102 | cmd_finder.must_have("ninja"); 103 | } 104 | 105 | build.config.python = build.config.python.take().map(|p| cmd_finder.must_have(p)) 106 | .or_else(|| env::var_os("BOOTSTRAP_PYTHON").map(PathBuf::from)) // set by bootstrap.py 107 | .or_else(|| cmd_finder.maybe_have("python2.7")) 108 | .or_else(|| cmd_finder.maybe_have("python2")) 109 | .or_else(|| Some(cmd_finder.must_have("python"))); 110 | 111 | build.config.nodejs = build.config.nodejs.take().map(|p| cmd_finder.must_have(p)) 112 | .or_else(|| cmd_finder.maybe_have("node")) 113 | .or_else(|| cmd_finder.maybe_have("nodejs")); 114 | 115 | build.config.gdb = build.config.gdb.take().map(|p| cmd_finder.must_have(p)) 116 | .or_else(|| cmd_finder.maybe_have("gdb")); 117 | 118 | // We're gonna build some custom C code here and there, host triples 119 | // also build some C++ shims for LLVM so we need a C++ compiler. 120 | for target in &build.targets { 121 | // On emscripten we don't actually need the C compiler to just 122 | // build the target artifacts, only for testing. For the sake 123 | // of easier bot configuration, just skip detection. 124 | if target.contains("emscripten") { 125 | continue; 126 | } 127 | 128 | cmd_finder.must_have(build.cc(*target)); 129 | if let Some(ar) = build.ar(*target) { 130 | cmd_finder.must_have(ar); 131 | } 132 | } 133 | 134 | for host in &build.hosts { 135 | cmd_finder.must_have(build.cxx(*host).unwrap()); 136 | 137 | // The msvc hosts don't use jemalloc, turn it off globally to 138 | // avoid packaging the dummy liballoc_jemalloc on that platform. 139 | if host.contains("msvc") { 140 | build.config.use_jemalloc = false; 141 | } 142 | } 143 | 144 | // Externally configured LLVM requires FileCheck to exist 145 | let filecheck = build.llvm_filecheck(build.build); 146 | if !filecheck.starts_with(&build.out) && !filecheck.exists() && build.config.codegen_tests { 147 | panic!("FileCheck executable {:?} does not exist", filecheck); 148 | } 149 | 150 | for target in &build.targets { 151 | // Can't compile for iOS unless we're on macOS 152 | if target.contains("apple-ios") && 153 | !build.build.contains("apple-darwin") { 154 | panic!("the iOS target is only supported on macOS"); 155 | } 156 | 157 | // Make sure musl-root is valid 158 | if target.contains("musl") && !target.contains("mips") { 159 | // If this is a native target (host is also musl) and no musl-root is given, 160 | // fall back to the system toolchain in /usr before giving up 161 | if build.musl_root(*target).is_none() && build.config.build == *target { 162 | let target = build.config.target_config.entry(target.clone()) 163 | .or_insert(Default::default()); 164 | target.musl_root = Some("/usr".into()); 165 | } 166 | match build.musl_root(*target) { 167 | Some(root) => { 168 | if fs::metadata(root.join("lib/libc.a")).is_err() { 169 | panic!("couldn't find libc.a in musl dir: {}", 170 | root.join("lib").display()); 171 | } 172 | if fs::metadata(root.join("lib/libunwind.a")).is_err() { 173 | panic!("couldn't find libunwind.a in musl dir: {}", 174 | root.join("lib").display()); 175 | } 176 | } 177 | None => { 178 | panic!("when targeting MUSL either the rust.musl-root \ 179 | option or the target.$TARGET.musl-root option must \ 180 | be specified in config.toml") 181 | } 182 | } 183 | } 184 | 185 | if target.contains("msvc") { 186 | // There are three builds of cmake on windows: MSVC, MinGW, and 187 | // Cygwin. The Cygwin build does not have generators for Visual 188 | // Studio, so detect that here and error. 189 | let out = output(Command::new("cmake").arg("--help")); 190 | if !out.contains("Visual Studio") { 191 | panic!(" 192 | cmake does not support Visual Studio generators. 193 | 194 | This is likely due to it being an msys/cygwin build of cmake, 195 | rather than the required windows version, built using MinGW 196 | or Visual Studio. 197 | 198 | If you are building under msys2 try installing the mingw-w64-x86_64-cmake 199 | package instead of cmake: 200 | 201 | $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake 202 | "); 203 | } 204 | } 205 | } 206 | 207 | let run = |cmd: &mut Command| { 208 | cmd.output().map(|output| { 209 | String::from_utf8_lossy(&output.stdout) 210 | .lines().next().unwrap() 211 | .to_string() 212 | }) 213 | }; 214 | build.lldb_version = run(Command::new("lldb").arg("--version")).ok(); 215 | if build.lldb_version.is_some() { 216 | build.lldb_python_dir = run(Command::new("lldb").arg("-P")).ok(); 217 | } 218 | 219 | if let Some(ref s) = build.config.ccache { 220 | cmd_finder.must_have(s); 221 | } 222 | }*/ 223 | -------------------------------------------------------------------------------- /tests/async_mode.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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 | 16 | //! Tests for async mode.. should be run with RUST_TEST_THREADS=1 17 | 18 | pub mod common; 19 | 20 | //mines for a bit on each available plugin, one after the other 21 | #[test] 22 | fn on_commit_mine_single_plugin_async() { 23 | //Should exercise lean/mean cpu at 1i9 for now 24 | let caps = common::get_plugin_vec("cuckatoo_mean_compat_cpu_29"); 25 | let mut params=Vec::new(); 26 | params.push((String::from("NUM_THREADS"),0,2)); 27 | for c in &caps { 28 | let mut plugin_path_vec:Vec<&str> = Vec::new(); 29 | plugin_path_vec.push(&c.full_path); 30 | println!("Mining on {} for 10s", c.full_path); 31 | common::mine_async_for_duration(plugin_path_vec, 10, Some(params.clone())); 32 | } 33 | } 34 | 35 | #[test] 36 | fn on_cuda_commit_mine_single_plugin_async() { 37 | let mut params=Vec::new(); 38 | /*params.push((String::from("USE_DEVICE"),0,0)); 39 | params.push((String::from("EXPAND"),0,0)); 40 | params.push((String::from("N_TRIMS"),0,176)); 41 | params.push((String::from("GEN_A_BLOCKS"),0,4096)); 42 | params.push((String::from("GEN_A_TPB"),0,256)); 43 | params.push((String::from("GEN_B_TPB"),0,128)); 44 | params.push((String::from("TRIM_TPB"),0,512)); 45 | params.push((String::from("TAIL_TPB"),0,1024)); 46 | params.push((String::from("RECOVER_BLOCKS"),0,1024)); 47 | params.push((String::from("RECOVER_TPB"),0,1024));*/ 48 | let caps = common::get_plugin_vec("cuda_30"); 49 | for c in &caps { 50 | let mut plugin_path_vec:Vec<&str> = Vec::new(); 51 | plugin_path_vec.push(&c.full_path); 52 | common::mine_async_for_duration(plugin_path_vec, 60, Some(params.clone())); 53 | } 54 | } 55 | //mine cuda and matrix (mean) miner for a bit 56 | #[test] 57 | fn on_cuda_commit_mine_mean_cpu_and_lean_cuda_async() { 58 | let caps = common::get_plugin_vec(""); 59 | let mut plugin_path_vec:Vec<&str> = Vec::new(); 60 | for c in &caps { 61 | if c.full_path.contains("lean_cuda_30") || c.full_path.contains("mean_cpu_30"){ 62 | plugin_path_vec.push(&c.full_path); 63 | } 64 | } 65 | common::mine_async_for_duration(plugin_path_vec, 180, None); 66 | } 67 | 68 | //Mines for a bit on all available plugins at once 69 | //(won't be efficient, but should stress-tes plugins nicely) 70 | #[test] 71 | fn on_commit_mine_plugins_async() { 72 | // Get a list of installed plugins and capabilities 73 | // only do cuckoo 30s 74 | let caps = common::get_plugin_vec("16"); 75 | let mut plugin_path_vec:Vec<&str> = Vec::new(); 76 | for c in &caps { 77 | //Have to confine this for the time being to 2, due to travis CI memory constraints 78 | if c.full_path.contains("lean_cpu") || c.full_path.contains("mean_cpu"){ 79 | plugin_path_vec.push(&c.full_path); 80 | } 81 | } 82 | common::mine_async_for_duration(plugin_path_vec, 15, None); 83 | } 84 | -------------------------------------------------------------------------------- /tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2017 The Grin Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | //! Common values and functions that can be used in all mining tests 18 | 19 | extern crate cuckoo_miner as cuckoo; 20 | extern crate time; 21 | extern crate rand; 22 | 23 | use std::path::PathBuf; 24 | use std::fmt::Write; 25 | use std; 26 | 27 | use common::rand::Rng; 28 | 29 | use self::cuckoo::{CuckooPluginManager, 30 | CuckooPluginCapabilities, 31 | CuckooMinerSolution, 32 | CuckooMinerConfig, 33 | CuckooMiner}; 34 | 35 | // Encode the provided bytes into a hex string 36 | pub fn to_hex(bytes: Vec) -> String { 37 | let mut s = String::new(); 38 | for byte in bytes { 39 | write!(&mut s, "{:02x}", byte).expect("Unable to write"); 40 | } 41 | s 42 | } 43 | 44 | //Helper to convert from hex string 45 | //avoids a lot of awkward byte array initialisation below 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 | 58 | pub const _DLL_SUFFIX: &str = ".cuckooplugin"; 59 | 60 | pub const _TEST_PLUGIN_LIBS_CORE : [&str;3] = [ 61 | "lean_cpu_16", 62 | "lean_cpu_30", 63 | "mean_cpu_30", 64 | ]; 65 | 66 | pub const _TEST_PLUGIN_LIBS_OPTIONAL : [&str;1] = [ 67 | "lean_cuda_30", 68 | ]; 69 | 70 | 71 | // Grin Pre and Post headers, into which a nonce is to be insterted for mutation 72 | pub const SAMPLE_GRIN_PRE_HEADER_1:&str = "00000000000000118e0fe6bcfaa76c6795592339f27b6d330d8f9c4ac8e86171a66357d1\ 73 | d0fce808000000005971f14f0000000000000000000000000000000000000000000000000000000000000000\ 74 | 3e1fcdd453ce51ffbb16dd200aeb9ef7375aec196e97094868428a7325e4a19b00"; 75 | 76 | pub const SAMPLE_GRIN_POST_HEADER_1:&str = "010a020364"; 77 | 78 | //hashes known to return a solution at cuckoo 30 and 16 79 | pub const KNOWN_30_HASH_1:&str = "11c5059b4d4053131323fdfab6a6509d73ef22\ 80 | 9aedc4073d5995c6edced5a3e6"; 81 | 82 | pub const KNOWN_16_HASH_1:&str = "c008b9ff7292fdacef0efbdff73d1db66674ff\ 83 | 3b6dea6cca670c85b6a110f0b2"; 84 | 85 | pub fn get_random_hash() -> [u8;32] { 86 | let mut ret_val:[u8;32] = [0;32]; 87 | for i in 0..32 { 88 | ret_val[i]=rand::OsRng::new().unwrap().gen(); 89 | }; 90 | ret_val 91 | } 92 | 93 | // Helper to load plugins 94 | pub fn get_plugin_vec(filter: &str) -> Vec{ 95 | let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 96 | d.push("target/debug/plugins/"); 97 | 98 | // get all plugins in directory 99 | let mut plugin_manager = CuckooPluginManager::new().unwrap(); 100 | plugin_manager 101 | .load_plugin_dir(String::from(d.to_str().unwrap())) 102 | .expect(""); 103 | 104 | // Get a list of installed plugins and capabilities 105 | plugin_manager.get_available_plugins(filter).unwrap() 106 | } 107 | 108 | // Helper function, mines a plugin once 109 | pub fn mine_once(full_path:&str, params:Option>) { 110 | 111 | let mut config_vec=Vec::new(); 112 | let mut config = CuckooMinerConfig::new(); 113 | config.plugin_full_path = String::from(full_path); 114 | 115 | if let Some(p) = params { 116 | config.parameter_list = p; 117 | } 118 | 119 | config_vec.push(config); 120 | for c in config_vec.clone().into_iter(){ 121 | println!("Plugin (Mine once): {}", c.plugin_full_path); 122 | } 123 | 124 | let miner = CuckooMiner::new(config_vec.clone()).expect(""); 125 | let header:[u8; 32] = get_random_hash(); 126 | let mut cuckoo_size = 0; 127 | let mut solution = CuckooMinerSolution::new(); 128 | let result = miner.mine(&header, &mut cuckoo_size, &mut solution, 0).unwrap(); 129 | } 130 | 131 | // Helper function, tests a particular miner implementation against a known set 132 | pub fn mine_sync_for_duration(full_path:&str, duration_in_seconds: i64, params:Option>) { 133 | let mut config_vec=Vec::new(); 134 | let mut config = CuckooMinerConfig::new(); 135 | config.plugin_full_path = String::from(full_path); 136 | 137 | if let Some(p) = params { 138 | config.parameter_list = p; 139 | } 140 | 141 | config_vec.push(config); 142 | 143 | let stat_check_interval = 3; 144 | let deadline = time::get_time().sec + duration_in_seconds; 145 | let mut next_stat_check = time::get_time().sec + stat_check_interval; 146 | 147 | let mut i:u64=0; 148 | println!("Test mining for {} seconds, looking for difficulty > 0", duration_in_seconds); 149 | for c in config_vec.clone().into_iter(){ 150 | println!("Plugin (Sync Mode): {}", c.plugin_full_path); 151 | } 152 | let miner = CuckooMiner::new(config_vec.clone()).expect(""); 153 | while time::get_time().sec < deadline { 154 | let mut iterations=0; 155 | let mut solution = CuckooMinerSolution::new(); 156 | loop { 157 | let header:[u8; 32] = get_random_hash(); 158 | let mut cuckoo_size = 0; 159 | //Mine on plugin loaded at index 0 160 | let result = miner.mine(&header, &mut cuckoo_size, &mut solution, 0).unwrap(); 161 | iterations+=1; 162 | if result == true { 163 | println!("Solution found after {} iterations: {}", i, solution); 164 | println!("For hash: {:?}", to_hex(header.to_vec())); 165 | i=0; 166 | break; 167 | } 168 | if time::get_time().sec > deadline { 169 | println!("Exiting after {} iterations", iterations); 170 | break; 171 | } 172 | if time::get_time().sec >= next_stat_check { 173 | let stats_vec=miner.get_stats(0).unwrap(); 174 | for s in stats_vec.into_iter() { 175 | if s.in_use == 0 {continue;} 176 | let last_solution_time_secs = s.last_solution_time as f64 / 1000000000.0; 177 | let last_hashes_per_sec = 1.0 / last_solution_time_secs; 178 | let status = match s.has_errored { 179 | 0 => "OK", 180 | _ => "ERRORED", 181 | }; 182 | println!("Plugin 0 - Device {} ({}) Status: {}, - Last Graph time: {}; Graphs per second: {:.*} \ 183 | - Total Attempts {}", 184 | s.device_id, s.device_name, status, last_solution_time_secs, 3, last_hashes_per_sec, 185 | s.iterations_completed); 186 | } 187 | next_stat_check = time::get_time().sec + stat_check_interval; 188 | } 189 | i+=1; 190 | } 191 | } 192 | } 193 | 194 | // Helper function, tests a particular miner implementation against a known set 195 | pub fn mine_async_for_duration(full_paths: Vec<&str>, duration_in_seconds: i64, 196 | params:Option>) { 197 | let mut config_vec=Vec::new(); 198 | for p in full_paths.into_iter() { 199 | let mut config = CuckooMinerConfig::new(); 200 | config.plugin_full_path = String::from(p); 201 | if let Some(p) = params.clone() { 202 | config.parameter_list = p.clone(); 203 | } 204 | config_vec.push(config); 205 | } 206 | 207 | let stat_check_interval = 3; 208 | let mut deadline = time::get_time().sec + duration_in_seconds; 209 | let mut next_stat_check = time::get_time().sec + stat_check_interval; 210 | let mut stats_updated=false; 211 | //for CI testing on slower servers 212 | //if we're trying to quit and there are no stats yet, keep going for a bit 213 | let mut extra_time=false; 214 | let extra_time_value=600; 215 | 216 | while time::get_time().sec < deadline { 217 | 218 | println!("Test mining for {} seconds, looking for difficulty > 0", duration_in_seconds); 219 | let mut i=0; 220 | for c in config_vec.clone().into_iter(){ 221 | println!("Plugin {}: {}", i, c.plugin_full_path); 222 | i+=1; 223 | } 224 | 225 | // these always get consumed after a notify 226 | let miner = CuckooMiner::new(config_vec.clone()).expect(""); 227 | let job_handle = miner.notify(1, SAMPLE_GRIN_PRE_HEADER_1, SAMPLE_GRIN_POST_HEADER_1, 0, false).unwrap(); 228 | 229 | loop { 230 | if let Some(s) = job_handle.get_solution() { 231 | println!("Sol found: {}, {:?}", s.get_nonce_as_u64(), s); 232 | // up to you to read it and check difficulty 233 | continue; 234 | } 235 | if time::get_time().sec >= next_stat_check { 236 | let mut sps_total=0.0; 237 | for index in 0..config_vec.len() { 238 | let stats_vec=job_handle.get_stats(index); 239 | if let Err(e) = stats_vec { 240 | panic!("Error getting stats: {:?}", e); 241 | } 242 | for s in stats_vec.unwrap().into_iter() { 243 | if s.in_use == 0 {continue;} 244 | let status = match s.has_errored { 245 | 0 => "OK", 246 | _ => "ERRORED", 247 | }; 248 | let last_solution_time_secs = s.last_solution_time as f64 / 1000000000.0; 249 | let last_hashes_per_sec = 1.0 / last_solution_time_secs; 250 | println!("Plugin 0 - Device {} ({}) at Cuckoo{} - Status: {} - Last Graph time: {}; Graphs per second: {:.*} \ 251 | - Total Attempts {}", 252 | s.device_id, s.device_name, s.cuckoo_size, status, last_solution_time_secs, 3, last_hashes_per_sec, 253 | s.iterations_completed); 254 | if last_hashes_per_sec.is_finite() { 255 | sps_total+=last_hashes_per_sec; 256 | } 257 | if last_solution_time_secs > 0.0 { 258 | stats_updated = true; 259 | } 260 | i+=1; 261 | } 262 | } 263 | println!("Total solutions per second: {}", sps_total); 264 | next_stat_check = time::get_time().sec + stat_check_interval; 265 | } 266 | if time::get_time().sec > deadline { 267 | if !stats_updated && !extra_time { 268 | extra_time=true; 269 | deadline+=extra_time_value; 270 | println!("More time needed"); 271 | } else { 272 | println!("Stopping jobs and waiting for cleanup"); 273 | job_handle.stop_jobs(); 274 | break; 275 | } 276 | } 277 | if stats_updated && extra_time { 278 | break; 279 | } 280 | //avoid busy wait 281 | let sleep_dur = std::time::Duration::from_millis(100); 282 | std::thread::sleep(sleep_dur); 283 | 284 | } 285 | if stats_updated && extra_time { 286 | break; 287 | } 288 | } 289 | assert!(stats_updated==true); 290 | } 291 | -------------------------------------------------------------------------------- /tests/performance.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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 | 16 | //! Performance-related tests go here 17 | 18 | pub mod common; 19 | 20 | //Test for profiling 21 | #[test] 22 | fn profile_mine_mean_30_async() { 23 | // Get a list of installed plugins and capabilities 24 | // only do cuckoo 30s 25 | let caps = common::get_plugin_vec("mean_cpu_30"); 26 | let mut plugin_path_vec:Vec<&str> = Vec::new(); 27 | for c in &caps { 28 | plugin_path_vec.push(&c.full_path); 29 | } 30 | common::mine_async_for_duration(plugin_path_vec, 120, None); 31 | } 32 | 33 | //Compare sync and async modes of mining, ensure 34 | //they're taking roughly the same time 35 | #[test] 36 | fn perf_mean_30_compare() { 37 | let caps = common::get_plugin_vec("mean_cpu_16"); 38 | let mut plugin_path_vec:Vec<&str> = Vec::new(); 39 | for c in &caps { 40 | plugin_path_vec.push(&c.full_path); 41 | } 42 | let mut params=Vec::new(); 43 | params.push((String::from("NUM_THREADS"),0,1)); 44 | common::mine_sync_for_duration(plugin_path_vec[0].clone(), 20, Some(params.clone())); 45 | common::mine_async_for_duration(plugin_path_vec, 20, Some(params.clone())); 46 | } 47 | -------------------------------------------------------------------------------- /tests/plugins.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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 | /// Tests exercising the loading and unloading of plugins, as well as the 16 | /// existence and correct functionality of each plugin function 17 | 18 | extern crate rand; 19 | extern crate cuckoo_miner as cuckoo; 20 | 21 | use std::path::PathBuf; 22 | use std::{thread, time}; 23 | use std::time::Instant; 24 | 25 | use cuckoo::CuckooMinerError; 26 | use cuckoo::PluginLibrary; 27 | 28 | pub mod common; 29 | 30 | use common::{ 31 | KNOWN_30_HASH_1, 32 | KNOWN_16_HASH_1}; 33 | 34 | static DLL_SUFFIX: &str = ".cuckooplugin"; 35 | 36 | const TEST_PLUGIN_LIBS_CORE : [&str;6] = [ 37 | "lean_cpu_16", 38 | "lean_cpu_30", 39 | "mean_cpu_16", 40 | "mean_cpu_30", 41 | "mean_compat_cpu_16", 42 | "mean_compat_cpu_30" 43 | ]; 44 | 45 | const TEST_PLUGIN_LIBS_OPTIONAL : [&str;1] = [ 46 | "lean_cuda_30", 47 | ]; 48 | 49 | //Helper to convert from hex string 50 | fn from_hex_string(in_str: &str) -> Vec { 51 | let mut bytes = Vec::new(); 52 | for i in 0..(in_str.len() / 2) { 53 | let res = u8::from_str_radix(&in_str[2 * i..2 * i + 2], 16); 54 | match res { 55 | Ok(v) => bytes.push(v), 56 | Err(e) => println!("Problem with hex: {}", e), 57 | } 58 | } 59 | bytes 60 | } 61 | 62 | //Helper to load a plugin library 63 | fn load_plugin_lib(plugin:&str) -> Result { 64 | let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 65 | d.push(format!("target/debug/plugins/{}{}", plugin, DLL_SUFFIX).as_str()); 66 | PluginLibrary::new(d.to_str().unwrap()) 67 | } 68 | 69 | //Helper to load all plugin libraries specified above 70 | fn load_all_plugins() -> Vec{ 71 | let mut plugin_libs:Vec = Vec::new(); 72 | for p in TEST_PLUGIN_LIBS_CORE.into_iter(){ 73 | plugin_libs.push(load_plugin_lib(p).unwrap()); 74 | } 75 | for p in TEST_PLUGIN_LIBS_OPTIONAL.into_iter(){ 76 | let pl = load_plugin_lib(p); 77 | if let Ok(p) = pl { 78 | plugin_libs.push(p); 79 | } 80 | } 81 | plugin_libs 82 | } 83 | 84 | //loads and unloads a plugin many times 85 | #[test] 86 | fn on_commit_plugin_loading(){ 87 | //core plugins should be built on all systems, fail if they don't exist 88 | for _ in 0..100 { 89 | for p in TEST_PLUGIN_LIBS_CORE.into_iter() { 90 | let pl = load_plugin_lib(p).unwrap(); 91 | pl.unload(); 92 | } 93 | } 94 | //only test these if they do exist (cuda, etc) 95 | for _ in 0..100 { 96 | for p in TEST_PLUGIN_LIBS_OPTIONAL.into_iter() { 97 | let pl = load_plugin_lib(p); 98 | if let Err(_) = pl { 99 | break; 100 | } 101 | pl.unwrap().unload(); 102 | } 103 | } 104 | } 105 | 106 | //Loads all plugins at once 107 | #[test] 108 | fn on_commit_plugin_multiple_loading(){ 109 | let _p=load_all_plugins(); 110 | } 111 | 112 | //tests cuckoo_init() on all available plugins 113 | //multiple calls to cuckoo init should be fine 114 | #[test] 115 | fn on_commit_cuckoo_init(){ 116 | let iterations = 100; 117 | let plugins = load_all_plugins(); 118 | for p in plugins.into_iter() { 119 | for _ in 0..iterations { 120 | p.call_cuckoo_init(); 121 | } 122 | } 123 | } 124 | 125 | // Helper to test call_cuckoo_parameter_list and return results 126 | // Ensures that all plugins *probably* don't overwrite 127 | // their buffers as they contain an null zero somewhere 128 | // within the rust-enforced length 129 | 130 | fn call_cuckoo_parameter_list_tests(pl: &PluginLibrary){ 131 | ///Test normal rust-enforced value 132 | const LENGTH:usize = 1024; 133 | let mut param_list_bytes:[u8;LENGTH]=[0;LENGTH]; 134 | let mut param_list_bytes_len=param_list_bytes.len() as u32; 135 | let ret_val=pl.call_cuckoo_parameter_list(&mut param_list_bytes, 136 | &mut param_list_bytes_len); 137 | let result_list = String::from_utf8(param_list_bytes.to_vec()).unwrap(); 138 | let result_list_null_index = result_list.find('\0'); 139 | 140 | //Check name is less than rust-enforced length, 141 | //if there's no \0 the plugin is likely overwriting the buffer 142 | println!("Plugin: {}", pl.lib_full_path); 143 | assert!(ret_val==0); 144 | println!("Parameter List: **{}**", result_list); 145 | assert!(result_list.len()>0); 146 | assert!(result_list_null_index != None); 147 | println!("Null Index: {}", result_list_null_index.unwrap()); 148 | 149 | //Basic form check... json parsing can be checked higher up 150 | assert!(result_list.contains("[")); 151 | 152 | ///Test provided length too short 153 | ///Plugin shouldn't explode as a result 154 | const TOO_SHORT_LENGTH:usize = 64; 155 | let mut param_list_bytes:[u8;TOO_SHORT_LENGTH]=[0;TOO_SHORT_LENGTH]; 156 | let mut param_list_bytes_len=param_list_bytes.len() as u32; 157 | let ret_val=pl.call_cuckoo_parameter_list(&mut param_list_bytes, 158 | &mut param_list_bytes_len); 159 | //let result_list = String::from_utf8(param_list_bytes.to_vec()).unwrap(); 160 | assert!(ret_val==3); 161 | } 162 | 163 | //tests call_cuckoo_parameter_list() on all available plugins 164 | #[test] 165 | fn on_commit_cuckoo_parameter_list(){ 166 | let iterations = 100; 167 | let plugins = load_all_plugins(); 168 | for p in plugins.into_iter() { 169 | for _ in 0..iterations { 170 | call_cuckoo_parameter_list_tests(&p); 171 | } 172 | } 173 | } 174 | 175 | // Helper to test call_cuckoo_get_parameter and return results 176 | // Ensures that all plugins *probably* don't overwrite 177 | // their buffers as they contain an null zero somewhere 178 | // within the rust-enforced length 179 | 180 | fn call_cuckoo_get_parameter_tests(pl: &PluginLibrary){ 181 | println!("Plugin: {}", pl.lib_full_path); 182 | //normal param that should be there 183 | let name = "NUM_THREADS"; 184 | let mut num_threads:u32 = 0; 185 | let return_value = pl.call_cuckoo_get_parameter(name.as_bytes(), 0, &mut num_threads); 186 | assert!(num_threads > 0); 187 | assert!(return_value == 0); 188 | 189 | //normal param that's not there 190 | let name = "SANDWICHES"; 191 | let mut num_sandwiches:u32 = 0; 192 | let return_value = pl.call_cuckoo_get_parameter(name.as_bytes(), 0, &mut num_sandwiches); 193 | assert!(num_sandwiches == 0); 194 | assert!(return_value == 1); 195 | 196 | //normal param that's not there and is too long 197 | let name = "SANDWICHESSANDWICHESSANDWICHESSANDWICHESSANDWICHESSANDWICHESANDWICHESSAES"; 198 | let mut num_sandwiches:u32 = 0; 199 | let return_value = pl.call_cuckoo_get_parameter(name.as_bytes(), 0, &mut num_sandwiches); 200 | assert!(num_sandwiches == 0); 201 | assert!(return_value == 4); 202 | } 203 | 204 | //tests call_cuckoo_get_parameter() on all available plugins 205 | #[test] 206 | fn on_commit_cuckoo_get_parameter(){ 207 | let iterations = 100; 208 | let plugins = load_all_plugins(); 209 | for p in plugins.into_iter() { 210 | for _ in 0..iterations { 211 | call_cuckoo_get_parameter_tests(&p); 212 | } 213 | } 214 | } 215 | 216 | // Helper to test call_cuckoo_set_parameter and return results 217 | // Ensures that all plugins *probably* don't overwrite 218 | // their buffers as they contain an null zero somewhere 219 | // within the rust-enforced length 220 | 221 | fn call_cuckoo_set_parameter_tests(pl: &PluginLibrary){ 222 | println!("Plugin: {}", pl.lib_full_path); 223 | //normal param that should be there 224 | let name = "NUM_THREADS"; 225 | let return_value = pl.call_cuckoo_set_parameter(name.as_bytes(), 0, 16); 226 | assert!(return_value == 0); 227 | 228 | //param is there, but calling it with a value outside its expected range 229 | let name = "NUM_THREADS"; 230 | let return_value = pl.call_cuckoo_set_parameter(name.as_bytes(), 0, 99999999); 231 | assert!(return_value == 2); 232 | 233 | //normal param that's not there 234 | let name = "SANDWICHES"; 235 | let return_value = pl.call_cuckoo_set_parameter(name.as_bytes(), 0, 8); 236 | assert!(return_value == 1); 237 | 238 | //normal param that's not there and is too long 239 | let name = "SANDWICHESSANDWICHESSANDWICHESSANDWICHESSANDWICHESSANDWICHESANDWICHESSAES"; 240 | let return_value = pl.call_cuckoo_set_parameter(name.as_bytes(), 0, 8); 241 | assert!(return_value == 4); 242 | 243 | //get that one back and check value 244 | let name = "NUM_THREADS"; 245 | let mut num_threads:u32 = 0; 246 | let return_value = pl.call_cuckoo_get_parameter(name.as_bytes(), 0, &mut num_threads); 247 | println!("Num Threads: {}", num_threads); 248 | assert!(return_value == 0); 249 | assert!(num_threads == 16); 250 | } 251 | 252 | //tests call_cuckoo_get_parameter() on all available plugins 253 | #[test] 254 | fn on_commit_cuckoo_set_parameter(){ 255 | let iterations = 100; 256 | let plugins = load_all_plugins(); 257 | for p in plugins.into_iter() { 258 | for _ in 0..iterations { 259 | call_cuckoo_set_parameter_tests(&p); 260 | } 261 | } 262 | } 263 | 264 | // Helper to test cuckoo_call 265 | // at this level, given the time involved we're just going to 266 | // do a sanity check that the same known hash will indeed give 267 | // a solution consistently across plugin implementations 268 | 269 | fn cuckoo_call_tests(pl: &PluginLibrary){ 270 | println!("Plugin: {}", pl.lib_full_path); 271 | 272 | //Known Hash 273 | let mut header = from_hex_string(KNOWN_30_HASH_1); 274 | //or 16, if needed 275 | if pl.lib_full_path.contains("16") { 276 | header = from_hex_string(KNOWN_16_HASH_1); 277 | } 278 | 279 | let mut solution:[u32; 42] = [0;42]; 280 | let mut size = 0; 281 | let result=pl.call_cuckoo(&header, &mut size, &mut solution); 282 | if result==1 { 283 | println!("Solution Found!"); 284 | } else { 285 | println!("No Solution Found"); 286 | println!("Header {:?}", header); 287 | } 288 | assert!(result==1); 289 | } 290 | 291 | //tests cuckoo_call() on all available plugins 292 | #[test] 293 | fn on_commit_cuckoo_call(){ 294 | let iterations = 1; 295 | let plugins = load_all_plugins(); 296 | for p in plugins.into_iter() { 297 | for _ in 0..iterations { 298 | //Only do 16 299 | if p.lib_full_path.contains("16"){ 300 | cuckoo_call_tests(&p); 301 | } 302 | } 303 | } 304 | /*let pl = load_plugin_lib("mean_cpu_30").unwrap(); 305 | cuckoo_call_tests(&pl);*/ 306 | //pl.unload(); 307 | /*let pl2 = load_plugin_lib("mean_cpu_16").unwrap(); 308 | cuckoo_call_tests(&pl2);*/ 309 | 310 | } 311 | 312 | // Helper to test call_cuckoo_start_processing 313 | // Starts up queue, lets it spin for a bit, 314 | // then shuts it down. Should be no segfaults 315 | // and everything cleared up cleanly 316 | 317 | fn call_cuckoo_start_processing_tests(pl: &PluginLibrary){ 318 | println!("Plugin: {}", pl.lib_full_path); 319 | //Just start processing 320 | let ret_val=pl.call_cuckoo_start_processing(); 321 | 322 | let wait_time = time::Duration::from_millis(25); 323 | 324 | thread::sleep(wait_time); 325 | pl.call_cuckoo_stop_processing(); 326 | 327 | //wait for internal processing to finish 328 | while pl.call_cuckoo_has_processing_stopped()==0{}; 329 | pl.call_cuckoo_reset_processing(); 330 | 331 | println!("{}",ret_val); 332 | assert!(ret_val==0); 333 | } 334 | 335 | //tests call_cuckoo_start_processing 336 | //on all available plugins 337 | #[test] 338 | fn on_commit_call_cuckoo_start_processing(){ 339 | let iterations = 10; 340 | let plugins = load_all_plugins(); 341 | for p in plugins.into_iter() { 342 | for _ in 0..iterations { 343 | call_cuckoo_start_processing_tests(&p); 344 | } 345 | } 346 | } 347 | 348 | // Helper to test call_cuckoo_push_to_input_queue 349 | 350 | fn call_cuckoo_push_to_input_queue_tests(pl: &PluginLibrary){ 351 | println!("Plugin: {}", pl.lib_full_path); 352 | 353 | //hash too long 354 | let hash:[u8;42]=[0;42]; 355 | let nonce:[u8;8]=[0;8]; 356 | println!("HASH LEN {}", hash.len()); 357 | let result=pl.call_cuckoo_push_to_input_queue(0, &hash, &nonce); 358 | println!("Result: {}",result); 359 | assert!(result==2); 360 | 361 | //basic push 362 | let hash:[u8;32]=[0;32]; 363 | let nonce:[u8;8]=[0;8]; 364 | let result=pl.call_cuckoo_push_to_input_queue(1, &hash, &nonce); 365 | assert!(result==0); 366 | 367 | //push until queue is full 368 | for i in 0..10000 { 369 | let result=pl.call_cuckoo_push_to_input_queue(i+2, &hash, &nonce); 370 | if result==1 { 371 | break; 372 | } 373 | //Should have been full long before now 374 | assert!(i!=10000); 375 | } 376 | 377 | //should be full 378 | let result=pl.call_cuckoo_push_to_input_queue(3, &hash, &nonce); 379 | assert!(result==1); 380 | 381 | //only do this on smaller test cuckoo, or we'll be here forever 382 | if pl.lib_full_path.contains("16"){ 383 | pl.call_cuckoo_start_processing(); 384 | let wait_time = time::Duration::from_millis(100); 385 | thread::sleep(wait_time); 386 | pl.call_cuckoo_stop_processing(); 387 | //wait for internal processing to finish 388 | while pl.call_cuckoo_has_processing_stopped()==0{}; 389 | } 390 | 391 | //Clear queues and reset internal 'should_quit' flag 392 | pl.call_cuckoo_clear_queues(); 393 | pl.call_cuckoo_reset_processing(); 394 | } 395 | 396 | //tests call_cuckoo_push_to_input_queue 397 | //on all available plugins 398 | #[test] 399 | fn on_commit_call_cuckoo_push_to_input_queue(){ 400 | let iterations = 10; 401 | let plugins = load_all_plugins(); 402 | for p in plugins.into_iter() { 403 | for _ in 0..iterations { 404 | call_cuckoo_push_to_input_queue_tests(&p); 405 | } 406 | } 407 | } 408 | 409 | // Helper to test call_cuckoo_stop_processing 410 | // basically, when a plugin is told to shut down, 411 | // it should immediately stop its processing, 412 | // clean up all alocated memory, and terminate 413 | // its processing thread. This will check to ensure each plugin 414 | // does so, and does so within a reasonable time frame 415 | 416 | fn call_cuckoo_stop_processing_tests(pl: &PluginLibrary){ 417 | println!("Plugin: {}", pl.lib_full_path); 418 | 419 | //start processing, which should take non-trivial time 420 | //in most cases 421 | let ret_val=pl.call_cuckoo_start_processing(); 422 | assert!(ret_val==0); 423 | 424 | //push anything to input queue 425 | let mut hash:[u8;32]=[0;32]; 426 | let nonce:[u8;8]=[0;8]; 427 | //push a few hashes into the queue 428 | for i in 0..100 { 429 | hash[0]=i; 430 | let result=pl.call_cuckoo_push_to_input_queue(i as u32, &hash, &nonce); 431 | assert!(result==0); 432 | } 433 | 434 | //Give it a bit to start up and process a bit 435 | let wait_time = time::Duration::from_millis(2500); 436 | thread::sleep(wait_time); 437 | 438 | let start=Instant::now(); 439 | 440 | //Now stop 441 | pl.call_cuckoo_stop_processing(); 442 | 443 | //wait for internal processing to finish 444 | while pl.call_cuckoo_has_processing_stopped()==0{}; 445 | pl.call_cuckoo_reset_processing(); 446 | 447 | let elapsed=start.elapsed(); 448 | let elapsed_ms=(elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64; 449 | println!("Shutdown elapsed_ms: {}",elapsed_ms); 450 | 451 | //will give each plugin a few seconds for now 452 | //but give cuda libs a pass for now, as they're hard to stop 453 | if !pl.lib_full_path.contains("cuda"){ 454 | //assert!(elapsed_ms<=5000); 455 | } 456 | } 457 | 458 | //tests call_cuckoo_stop_processing 459 | //on all available plugins 460 | #[test] 461 | fn on_commit_call_cuckoo_stop_processing(){ 462 | let iterations = 1; 463 | let plugins = load_all_plugins(); 464 | for p in plugins.into_iter() { 465 | for _ in 0..iterations { 466 | if p.lib_full_path.contains("16"){ 467 | call_cuckoo_stop_processing_tests(&p); 468 | } 469 | } 470 | } 471 | 472 | //let pl = load_plugin_lib("lean_cuda_30").unwrap(); 473 | //call_cuckoo_stop_processing_tests(&pl); 474 | } 475 | 476 | // Helper to test call_cuckoo_read_from_output_queue 477 | // will basically test that each plugin comes back 478 | // with a known solution in async mode 479 | 480 | fn call_cuckoo_read_from_output_queue_tests(pl: &PluginLibrary){ 481 | println!("Plugin: {}", pl.lib_full_path); 482 | 483 | //Known Hash 484 | let mut header = from_hex_string(KNOWN_30_HASH_1); 485 | //or 16, if needed 486 | if pl.lib_full_path.contains("16") { 487 | header = from_hex_string(KNOWN_16_HASH_1); 488 | } 489 | //Just zero nonce here, for ID 490 | let nonce:[u8;8]=[0;8]; 491 | let result=pl.call_cuckoo_push_to_input_queue(0, &header, &nonce); 492 | println!("Result: {}", result); 493 | assert!(result==0); 494 | 495 | //start processing 496 | let ret_val=pl.call_cuckoo_start_processing(); 497 | assert!(ret_val==0); 498 | //Record time now, because we don't want to wait forever 499 | let start=Instant::now(); 500 | 501 | //if 8 minutes has elapsed, there's no solution 502 | let max_time_ms=480000; 503 | 504 | let mut sols:[u32; 42] = [0; 42]; 505 | let mut nonce: [u8; 8] = [0;8]; 506 | let mut id = 0; 507 | let mut size = 0; 508 | loop { 509 | let found = pl.call_cuckoo_read_from_output_queue(&mut id, &mut sols, &mut size, &mut nonce); 510 | if found == 1 { 511 | println!("Found solution"); 512 | break; 513 | } 514 | let elapsed=start.elapsed(); 515 | let elapsed_ms=(elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64; 516 | if elapsed_ms > max_time_ms{ 517 | //stop 518 | pl.call_cuckoo_stop_processing(); 519 | 520 | while pl.call_cuckoo_has_processing_stopped()==0{}; 521 | pl.call_cuckoo_reset_processing(); 522 | //cry about it 523 | panic!("Known solution not found"); 524 | } 525 | } 526 | 527 | //now stop 528 | pl.call_cuckoo_stop_processing(); 529 | 530 | //wait for internal processing to finish 531 | while pl.call_cuckoo_has_processing_stopped()==0{}; 532 | pl.call_cuckoo_reset_processing(); 533 | 534 | } 535 | 536 | //tests call_cuckoo_read_from_output_queue() on all available 537 | //plugins 538 | 539 | #[test] 540 | fn on_commit_call_cuckoo_read_from_output_queue(){ 541 | let iterations = 1; 542 | let plugins = load_all_plugins(); 543 | for p in plugins.into_iter() { 544 | for _ in 0..iterations { 545 | if p.lib_full_path.contains("16"){ 546 | call_cuckoo_read_from_output_queue_tests(&p); 547 | } 548 | } 549 | } 550 | /*let pl = load_plugin_lib("lean_cuda_30").unwrap(); 551 | call_cuckoo_read_from_output_queue_tests(&pl);*/ 552 | } 553 | 554 | // Helper to test call_cuckoo_get_stats and return results 555 | // Ensures that all plugins *probably* don't overwrite 556 | // their buffers as they contain an null zero somewhere 557 | // within the rust-enforced length 558 | 559 | fn call_cuckoo_get_stats_test(pl: &PluginLibrary){ 560 | ///Test normal value 561 | const LENGTH:usize = 1024; 562 | let mut stat_bytes:[u8;LENGTH]=[0;LENGTH]; 563 | let mut stat_bytes_len=stat_bytes.len() as u32; 564 | let ret_val=pl.call_cuckoo_get_stats(&mut stat_bytes, 565 | &mut stat_bytes_len); 566 | let result_list = String::from_utf8(stat_bytes.to_vec()).unwrap(); 567 | let result_list_null_index = result_list.find('\0'); 568 | 569 | //Check name is less than rust-enforced length, 570 | //if there's no \0 the plugin is likely overwriting the buffer 571 | println!("Plugin: {}", pl.lib_full_path); 572 | assert!(ret_val==0); 573 | println!("Stat List: **{}**", result_list); 574 | assert!(result_list.len()>0); 575 | assert!(result_list_null_index != None); 576 | println!("Null Index: {}", result_list_null_index.unwrap()); 577 | 578 | //Basic form check... json parsing can be checked higher up 579 | assert!(result_list.contains("[")); 580 | assert!(result_list.contains("]")); 581 | 582 | //Check buffer too small 583 | const TOO_SMALL:usize = 10; 584 | let mut stat_bytes:[u8;TOO_SMALL]=[0;TOO_SMALL]; 585 | let mut stat_bytes_len=stat_bytes.len() as u32; 586 | let ret_val=pl.call_cuckoo_get_stats(&mut stat_bytes, 587 | &mut stat_bytes_len); 588 | 589 | assert!(ret_val==3); 590 | 591 | //Now start up processing and check values 592 | //Known Hash 593 | let mut header = from_hex_string(KNOWN_30_HASH_1); 594 | //or 16, if needed 595 | if pl.lib_full_path.contains("16") { 596 | header = from_hex_string(KNOWN_16_HASH_1); 597 | } 598 | //Just zero nonce here, for ID 599 | let nonce:[u8;8]=[0;8]; 600 | let result=pl.call_cuckoo_push_to_input_queue(0, &header, &nonce); 601 | println!("Result: {}", result); 602 | assert!(result==0); 603 | 604 | //start processing 605 | let ret_val=pl.call_cuckoo_start_processing(); 606 | assert!(ret_val==0); 607 | 608 | //Not going to wait around to test values here, 609 | //will to that higher up as part of other tests 610 | //in the interests of time 611 | 612 | let wait_time = time::Duration::from_millis(2000); 613 | thread::sleep(wait_time); 614 | 615 | let mut stat_bytes:[u8;LENGTH]=[0;LENGTH]; 616 | let mut stat_bytes_len=stat_bytes.len() as u32; 617 | let ret_val=pl.call_cuckoo_get_stats(&mut stat_bytes, 618 | &mut stat_bytes_len); 619 | println!("Ret val: {}", ret_val); 620 | let result_list = String::from_utf8(stat_bytes.to_vec()).unwrap(); 621 | //let result_list_null_index = result_list.find('\0'); 622 | assert!(ret_val==0); 623 | 624 | println!("Stats after starting: {}", result_list); 625 | 626 | //now stop 627 | pl.call_cuckoo_stop_processing(); 628 | 629 | //wait for internal processing to finish 630 | while pl.call_cuckoo_has_processing_stopped()==0{}; 631 | pl.call_cuckoo_reset_processing(); 632 | } 633 | 634 | //tests call_cuckoo_parameter_list() on all available plugins 635 | #[test] 636 | fn on_commit_call_cuckoo_get_stats(){ 637 | let iterations = 2; 638 | let plugins = load_all_plugins(); 639 | for p in plugins.into_iter() { 640 | for _ in 0..iterations { 641 | if p.lib_full_path.contains("16"){ 642 | call_cuckoo_get_stats_test(&p); 643 | } 644 | } 645 | } 646 | /*let pl = load_plugin_lib("lean_cpu_30").unwrap(); 647 | call_cuckoo_get_stats_test(&pl);*/ 648 | } 649 | 650 | // test specific issues in plugins, 651 | // for instance exercising parameters, etc 652 | // Known to fail hard at moment due to thread cleanup issues in lean_16 653 | #[test] 654 | fn specific_lean_cpu_16(){ 655 | let pl = load_plugin_lib("lean_cpu_16").unwrap(); 656 | println!("Plugin: {}", pl.lib_full_path); 657 | 658 | let mut header:[u8;32] = [0;32]; 659 | let mut solution:[u32; 42] = [0;42]; 660 | let max_iterations=10000; 661 | let return_value=pl.call_cuckoo_set_parameter(String::from("NUM_THREADS").as_bytes(), 0, 4); 662 | assert!(return_value==0); 663 | 664 | //check specific header on 4 threads 665 | let known_header = from_hex_string(KNOWN_16_HASH_1); 666 | let mut size = 0; 667 | let return_value=pl.call_cuckoo(&known_header, &mut size, &mut solution); 668 | assert!(return_value==1); 669 | 670 | for i in 0..max_iterations { 671 | for j in 0..32 { 672 | header[j]=rand::random::(); 673 | } 674 | let _=pl.call_cuckoo(&header, &mut size, &mut solution); 675 | if i%100 == 0{ 676 | println!("Iterations: {}", i); 677 | } 678 | } 679 | let return_value=pl.call_cuckoo(&known_header, &mut size, &mut solution); 680 | assert!(return_value==1); 681 | } 682 | 683 | 684 | // test specific issues in plugins, 685 | // for instance exercising parameters, etc 686 | #[test] 687 | fn on_commit_specific_mean_cpu_16(){ 688 | let pl = load_plugin_lib("mean_cpu_16").unwrap(); 689 | println!("Plugin: {}", pl.lib_full_path); 690 | 691 | let mut header:[u8;32] = [0;32]; 692 | let mut solution:[u32; 42] = [0;42]; 693 | let max_iterations=10000; 694 | let return_value=pl.call_cuckoo_set_parameter(String::from("NUM_THREADS").as_bytes(), 0, 4); 695 | let mut size = 0; 696 | assert!(return_value==0); 697 | for i in 0..max_iterations { 698 | for j in 0..32 { 699 | header[j]=rand::random::(); 700 | } 701 | let _=pl.call_cuckoo(&header, &mut size, &mut solution); 702 | if i%100 == 0{ 703 | println!("Iterations: {}", i); 704 | } 705 | } 706 | //check specific header on 4 threads 707 | let known_header = from_hex_string(KNOWN_16_HASH_1); 708 | let return_value=pl.call_cuckoo(&known_header, &mut size, &mut solution); 709 | assert!(return_value==1); 710 | } 711 | -------------------------------------------------------------------------------- /tests/sync_mode.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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 | 16 | //! Tests for async mode.. should be run with RUST_TEST_THREADS=1 17 | 18 | extern crate cuckoo_miner as cuckoo; 19 | extern crate time; 20 | 21 | pub mod common; 22 | 23 | //mines plugin as requsted 24 | #[test] 25 | fn mine_once_cuckatoo_cuda_29() { 26 | //Should exercise lean/mean cpu at 1i9 for now 27 | let caps = common::get_plugin_vec("cuckatoo_cuda_29"); 28 | for c in &caps { 29 | let mut plugin_path_vec:Vec<&str> = Vec::new(); 30 | plugin_path_vec.push(&c.full_path); 31 | common::mine_once(&c.full_path, None); 32 | } 33 | } 34 | 35 | #[test] 36 | fn mine_once_cuckatoo_lean_29() { 37 | //Should exercise lean/mean cpu at 1i9 for now 38 | let caps = common::get_plugin_vec("cuckatoo_lean_cpu_29"); 39 | let mut params=Vec::new(); 40 | params.push((String::from("NUM_THREADS"),0,2)); 41 | for c in &caps { 42 | let mut plugin_path_vec:Vec<&str> = Vec::new(); 43 | plugin_path_vec.push(&c.full_path); 44 | common::mine_once(&c.full_path, Some(params.clone())); 45 | } 46 | } 47 | 48 | //Mines for a bit on each available plugin, one after the other 49 | #[test] 50 | fn on_commit_mine_sync() { 51 | let caps = common::get_plugin_vec("16"); 52 | for c in &caps { 53 | common::mine_sync_for_duration(&c.full_path, 10, None); 54 | } 55 | } 56 | 57 | //Same as above, but for cuda only 58 | #[test] 59 | fn on_cuda_commit_mine_sync() { 60 | let caps = common::get_plugin_vec("cuda_30"); 61 | for c in &caps { 62 | common::mine_sync_for_duration(&c.full_path, 75, None); 63 | } 64 | } 65 | 66 | //Same as above, but for cuda only 67 | #[test] 68 | fn on_cuda_meaner_commit_mine_sync() { 69 | let caps = common::get_plugin_vec("cuda_meaner_30"); 70 | for c in &caps { 71 | common::mine_sync_for_duration(&c.full_path, 75, None); 72 | } 73 | } 74 | 75 | //test for mean_16 compat 76 | //(won't be efficient, but should stress-tes plugins nicely) 77 | #[test] 78 | fn manual_mean_16_compat() { 79 | let mut params=Vec::new(); 80 | params.push((String::from("NUM_THREADS"),0,4)); 81 | let caps = common::get_plugin_vec("mean_compat_cpu_16"); 82 | common::mine_sync_for_duration(&caps[0].full_path, 3600, Some(params.clone())); 83 | } 84 | --------------------------------------------------------------------------------