├── README.md ├── arionum-benchmark ├── .gitignore ├── LICENSE.md ├── README.md ├── hasher-tests │ ├── README.md │ ├── Results-Summary-24hrs-php_vs_java.xlsx │ ├── java-miner-24hrs.log │ ├── php-miner-1-24hrs.log │ └── php-miner-2-24hrs.log ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── programmerdan │ └── arionum │ └── arionum_benchmark │ ├── AdvMode.java │ ├── BasicHasher.java │ ├── ExperimentalHasher.java │ ├── Hasher.java │ └── StableHasher.java └── arionum-miner ├── .gitignore ├── DOCS.md ├── LICENSE.md ├── README.md ├── WINDOWS_COMPILE_GUIDE.md ├── pom-arm.xml ├── pom.xml └── src └── main ├── bin ├── build-argon-arm.sh ├── build-argon.sh ├── pick-argon.bat ├── pick-argon.ps1 ├── run.bat └── run.sh ├── java └── com │ └── programmerdan │ └── arionum │ └── arionum_miner │ ├── AdvMode.java │ ├── AggressiveAffinityThreadFactory.java │ ├── CPrint.java │ ├── ExperimentalHasher.java │ ├── GPUHasher.java │ ├── Hasher.java │ ├── HasherFactory.java │ ├── HasherStats.java │ ├── MappedHasher.java │ ├── Miner.java │ ├── MinerType.java │ ├── Profile.java │ ├── Report.java │ ├── SafeMappedHasher.java │ ├── Utility.java │ └── jna │ ├── Argon2Library.java │ ├── Argon2_Context.java │ ├── Argon2_type.java │ ├── JnaUint32.java │ ├── JnaUint8.java │ └── Size_t.java └── resources ├── darwin └── libargon2.dylib ├── linux-x86-64 └── libargon2.so └── win32-x86-64 ├── argon2-avx.dll ├── argon2-avx2.dll ├── argon2-avx512f.dll ├── argon2-generic.dll └── argon2.dll /README.md: -------------------------------------------------------------------------------- 1 | Arionum-Java 2 | ------------------ 3 | Arionum is a pretty new cryptocurrency that's largely CPU bound, resisting ASIC and GPU optimizations. This is a perfect coin to mine for people with GPU hardware doing other mining but low utilization of CPU resources. 4 | 5 | It caught my eye and as part of code-vetting I figured I might as well do a port to Java and see if I can get some more efficiency out of the code or better interfacing. I'll try as I'm able (pretty rusty with PHP) to also contribute back to the PHP reference implementations. 6 | 7 | Learn more here: http://arionum.com 8 | 9 | Bitcointalk: https://bitcointalk.org/index.php?topic=2710248.0 10 | 11 | Block explorer: https://arionum.info/ 12 | 13 | Send Arionum donations here: 4SxMLLWRCxoL42gqgf2C2x5T5Q1BJhgfQvMsfwrcFAu4owsBYncsvVnBBVqtxVrkqHVZ8nJGrcJB7yWnV92j5Rca 14 | 15 | Send BTC donations here: bc1qucem39g0lamacevmvj7qnkfjh3mqkr0jmlnlt3 (segwit) or 1MXcQyo1jbaTYdWUqeAQumsKUDnytiFAe5 (standard) 16 | 17 | Send CureCoin donations here: BFDK84Z29zb4caNMArKBZVBXAxGqGMRMfq (get involved here: https://curecoin.net/ -- works great alongside Arionum) 18 | 19 | Send FoldingCoin donations here: 1HH3SQNf1KPfsAGC5BAvDj849oHoAsYEms (get involved here: https://foldingcoin.net/ -- mergefold w/ curecoin!) 20 | 21 | Thanks, this looks to be a super fun project. 22 | 23 | --------------- 24 | # Arionum-miner 25 | 26 | This is a slightly optimized miner, coded in Java, and based very strongly on the php reference version. 27 | 28 | Major differentiators: 29 | 30 | * One JVM can run multiple hashers and share the same "update" requests to the pool, reducing traffic to the core 31 | 32 | * The hashers never stop -- the reference PHP implementation pauses the hasher during update requests and nonce submissions. The java hashers never stop. 33 | 34 | * Optimizations -- the Java miner has easy swap support for alternate "core" types, with "standard", "experimental" and "legacy" for now; standard (or "enhanced") is the current strong recommendation, it has better adaptive behavior and some optimizations to reduce rejections, identify bad or reduced capability memory sectors and generally focus on accepted hashes, not just hashrate (although hashrate is great too). All other cores represent prior or lower performing cores. 35 | 36 | For more details, see the arionum forum thread here: https://forum.arionum.com/viewtopic.php?f=11&t=28 37 | 38 | I hope you enjoy. See the README.md in the arionum-miner subfolder for details on compiling and running. 39 | -------------------------------------------------------------------------------- /arionum-benchmark/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | *.exe 3 | *.jar 4 | *.sh 5 | *.bat 6 | *.cfg 7 | **/dependency-reduced-pom.xml 8 | dependency-reduced-pom.xml 9 | .project 10 | .classpath 11 | .settings/ 12 | -------------------------------------------------------------------------------- /arionum-benchmark/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 3 | 4 | www.arionum.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of 15 | the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 21 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 23 | OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | 26 | Libraries used (such as argon2-jvm and json-simple) are covered by their own licenses, I use them as-is without warranty. 27 | -------------------------------------------------------------------------------- /arionum-benchmark/README.md: -------------------------------------------------------------------------------- 1 | Arionum Benchmark for Java 2 | ==================== 3 | by ProgrammerDan, Jan 2018 4 | based on https://github.com/arionum/miner a work by AroDev 5 | 6 | For advanced system benchmarking. Compile using `mvn clean package` and run using `java -jar target/argon-benchmark-0.0.1-SNAPSHOT.jar`. It will run until forcibly stopped. 7 | 8 | -------------------------------------------------------------------------------- /arionum-benchmark/hasher-tests/README.md: -------------------------------------------------------------------------------- 1 | These are data files and an xlsx showing results from a 24 hr side by side test of php reference miner and java multithreaded. 2 | 3 | Full results summary here: 4 | 5 | https://docs.google.com/spreadsheets/d/e/2PACX-1vSfq_6E935nj3Uek1kF1nENIFMTk7I9-EsxBrKRRyaZo03nEktzMlBV6ZHVOXvc6I8oUhhRYMOOiidS/pubhtml?gid=105959191&single=true 6 | -------------------------------------------------------------------------------- /arionum-benchmark/hasher-tests/Results-Summary-24hrs-php_vs_java.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammerDan/arionum-java/17d3ecedfbf6106e66b7a1b19702604df53f764d/arionum-benchmark/hasher-tests/Results-Summary-24hrs-php_vs_java.xlsx -------------------------------------------------------------------------------- /arionum-benchmark/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.programmerdan.arionum 6 | arionum-benchmark 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | arionum-benchmark-java 11 | https://github.com/ProgrammerDan/arionum-java/arionum-benchmark 12 | 13 | 14 | UTF-8 15 | 1.8 16 | 1.8 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-compiler-plugin 24 | 3.5.1 25 | 26 | 1.8 27 | 1.8 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-shade-plugin 33 | 2.4.3 34 | 35 | 36 | package 37 | 38 | shade 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | com.programmerdan.arionum.arionum_benchmark.Hasher 48 | 49 | 50 | 52 | META-INF/README.md 53 | README.md 54 | 55 | 57 | META-INF/LICENSE.md 58 | LICENSE.md 59 | 60 | 61 | 62 | 63 | junit:junit 64 | 65 | 66 | 67 | 68 | de.mkammerer:* 69 | 70 | ** 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | junit 82 | junit 83 | 3.8.1 84 | test 85 | 86 | 87 | de.mkammerer 88 | argon2-jvm 89 | 2.3 90 | 91 | 92 | a java benchmark of arionum's argon2i function. DOES NOT MINE 93 | 94 | -------------------------------------------------------------------------------- /arionum-benchmark/src/main/java/com/programmerdan/arionum/arionum_benchmark/AdvMode.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_benchmark; 28 | 29 | public enum AdvMode { 30 | basic, 31 | experimental, 32 | stable 33 | } 34 | -------------------------------------------------------------------------------- /arionum-benchmark/src/main/java/com/programmerdan/arionum/arionum_benchmark/BasicHasher.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_benchmark; 28 | 29 | import java.math.BigInteger; 30 | import java.security.MessageDigest; 31 | import java.security.NoSuchAlgorithmException; 32 | import java.security.SecureRandom; 33 | import java.util.Base64; 34 | 35 | import de.mkammerer.argon2.Argon2; 36 | import de.mkammerer.argon2.Argon2Factory; 37 | import de.mkammerer.argon2.Argon2Factory.Argon2Types; 38 | 39 | public class BasicHasher extends Hasher { 40 | public BasicHasher() { 41 | super(); 42 | } 43 | 44 | @Override 45 | public void run() { 46 | active = true; 47 | long start = System.currentTimeMillis(); 48 | long lastUpdate = start; 49 | 50 | byte[] nonce = new byte[32]; 51 | String encNonce = null; 52 | 53 | StringBuilder hashBase = new StringBuilder(); 54 | String base = null; 55 | byte[] byteBase = null; 56 | String argon = null; 57 | 58 | StringBuilder hashedHash = new StringBuilder(); 59 | 60 | SecureRandom random = new SecureRandom(); 61 | 62 | Argon2 argon2 = Argon2Factory.create(Argon2Types.ARGON2i); 63 | 64 | MessageDigest sha512 = null; 65 | try { 66 | sha512 = MessageDigest.getInstance("SHA-512"); 67 | } catch (NoSuchAlgorithmException e1) { 68 | System.err.println("Unable to find SHA-512 algorithm! Fatal error."); 69 | e1.printStackTrace(); 70 | System.exit(1); 71 | active = false; 72 | } 73 | if (active) { 74 | System.out.println("Spun up php-parity hashing worker in " + (System.currentTimeMillis() - start) + "ms"); 75 | start = System.currentTimeMillis(); 76 | } 77 | 78 | long statCycle = 0l; 79 | long statBegin = 0l; 80 | long statArgonBegin = 0l; 81 | long statArgonEnd = 0l; 82 | long statEnd = 0l; 83 | 84 | int cCount = 0; 85 | double speed = 0d; 86 | double avgSpeed = 0d; 87 | 88 | statCycle = System.currentTimeMillis(); 89 | 90 | while (active) { 91 | 92 | if (System.currentTimeMillis()-lastUpdate > 2000) { 93 | System.out.println("--> Last hash rate: " + speed + " H/s Average: " + avgSpeed + " H/s Total hashes: " + hashCount + " Mining Time: " + ((System.currentTimeMillis() - start) / 1000d) + 94 | " Shares: " + shares + " Finds: " + finds); 95 | lastUpdate = System.currentTimeMillis(); 96 | } 97 | 98 | statBegin = System.nanoTime(); 99 | try { 100 | random.nextBytes(nonce); 101 | encNonce = Base64.getEncoder().encodeToString(nonce); 102 | encNonce = encNonce.replaceAll("[^a-zA-Z0-9]", ""); // TODO: static test vs other impls 103 | hashBase = new StringBuilder(); 104 | hashBase.append(this.publicKey).append("-"); 105 | hashBase.append(encNonce).append("-"); 106 | hashBase.append(this.data).append("-"); 107 | hashBase.append(this.difficultyString); 108 | statArgonBegin = System.nanoTime(); 109 | argon = argon2.hash(4, 16384, 4, hashBase.toString()); 110 | statArgonEnd = System.nanoTime(); 111 | hashBase.append(argon); 112 | 113 | base = hashBase.toString(); 114 | byteBase = base.getBytes(); 115 | for (int i = 0; i < 5; i++) { 116 | byteBase = sha512.digest(byteBase); 117 | } 118 | byteBase = sha512.digest(byteBase); 119 | // see https://stackoverflow.com/a/33085670 120 | hashedHash = new StringBuilder(); // TODO: Timing tests. 121 | // for (int j = 0; j < byteBase.length; j++) { 122 | // hashedHash.append(Integer.toString((byteBase[j] & 0xff) + 123 | // 0x100, 16).substring(1)); 124 | // } 125 | // or see https://stackoverflow.com/a/19722967 126 | BigInteger bi = new BigInteger(1, byteBase); 127 | hashedHash.append(String.format("%0" + (byteBase.length << 1) + "x", bi)); 128 | 129 | StringBuilder duration = new StringBuilder(); 130 | duration.append(hexdec_equiv(hashedHash, 10)).append(hexdec_equiv(hashedHash, 15)) 131 | .append(hexdec_equiv(hashedHash, 20)).append(hexdec_equiv(hashedHash, 23)) 132 | .append(hexdec_equiv(hashedHash, 31)).append(hexdec_equiv(hashedHash, 40)) 133 | .append(hexdec_equiv(hashedHash, 45)).append(hexdec_equiv(hashedHash, 55)); 134 | 135 | // TODO: Bypass double ended conversion; no reason to go from 136 | // binary to hex to dec; go straight from bin to dec for max 137 | // eff. 138 | 139 | long finalDuration = new BigInteger(duration.toString()).divide(this.difficulty).longValue(); 140 | if (finalDuration > 0 && finalDuration <= this.limit) { 141 | if (finalDuration <= 240) { 142 | finds++; 143 | } else { 144 | shares++; 145 | } 146 | } 147 | 148 | //parent.bestDL.getAndUpdate( (dl) -> {if (finalDuration < dl) return finalDuration; else return dl;} ); 149 | 150 | hashCount++; 151 | cCount++; 152 | //parent.hashes.incrementAndGet(); 153 | //parent.currentHashes.incrementAndGet(); 154 | statEnd = System.nanoTime(); 155 | //parent.workerHash(this.id, finalDuration, statArgonEnd - statArgonBegin, (statArgonBegin - statBegin) + (statEnd - statArgonEnd)); 156 | 157 | if (cCount == 100) { 158 | cCount = 0; 159 | long cycleEnd = System.currentTimeMillis(); 160 | speed = 100d / ((cycleEnd - statCycle)/ 1000d); 161 | avgSpeed = (double) hashCount / ((cycleEnd - start) / 1000d); 162 | statCycle = cycleEnd; 163 | } 164 | 165 | } catch (Exception e) { 166 | System.err.println("This worker failed somehow. Killing it."); 167 | e.printStackTrace(); 168 | active = false; 169 | } 170 | } 171 | System.out.println("This worker is now inactive."); 172 | } 173 | 174 | public int hexdec_equiv(StringBuilder m, int index) { 175 | return Integer.parseInt(m.substring(index * 2, index * 2 + 2), 16); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /arionum-benchmark/src/main/java/com/programmerdan/arionum/arionum_benchmark/ExperimentalHasher.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_benchmark; 28 | 29 | import java.math.BigInteger; 30 | import java.security.MessageDigest; 31 | import java.security.NoSuchAlgorithmException; 32 | import java.security.SecureRandom; 33 | import java.util.Base64; 34 | import java.util.Base64.Encoder; 35 | 36 | import de.mkammerer.argon2.Argon2; 37 | import de.mkammerer.argon2.Argon2Factory; 38 | import de.mkammerer.argon2.Argon2Factory.Argon2Types; 39 | 40 | /** 41 | * The intent for this hasher is deeper self-inspection of running times of various components. 42 | * It can be used as a testbed for comparative performance. It is not meant to be used for general use 43 | * 44 | * This particular experimental hasher takes advantage of an observation: namely, that 45 | * we're doing double the randomness minimally necessary for the scheme, since the argon2i 46 | * implementation here and in the php reference internally salts the "password" with 32 bytes of 47 | * random data. So the nonce itself can be considered just a payload, fixed entity, and allow the 48 | * salting of the argon2i to control uniformly random generation of SHA-512 DL outcomes. 49 | * 50 | * This gives me 5-10% H/s speedups on isolated testing with no increase in rejections. Block 51 | * finds and shares remain as expected for H/s observed. 52 | * 53 | * Another indicator of improved performance is tracking reveals 99.97% of time is spent in argon2i 54 | * codepath, vs. between 99.8 and 99.9% for other cores. This might sound small but adds up in a big way 55 | * over time, as its a per-hash improvement. 56 | * 57 | * Once a nonce is submitted, it is discarded and a new one generated, as the pool does not allow 58 | * resubmission of prior nonces. 59 | * 60 | * 61 | * @author ProgrammerDan (Daniel Boston) 62 | * 63 | */ 64 | public class ExperimentalHasher extends Hasher { 65 | 66 | public ExperimentalHasher() { 67 | } 68 | 69 | private SecureRandom random = new SecureRandom(); 70 | private Encoder encoder = Base64.getEncoder(); 71 | private String rawHashBase; 72 | private byte[] nonce = new byte[32]; 73 | private String rawNonce; 74 | 75 | private long bestDL; 76 | 77 | /** 78 | */ 79 | private void genNonce() { 80 | String encNonce = null; 81 | StringBuilder hashBase; 82 | random.nextBytes(nonce); 83 | encNonce = encoder.encodeToString(nonce); 84 | 85 | char[] nonceChar = encNonce.toCharArray(); 86 | 87 | // shaves a bit off vs regex -- for this operation, about 50% savings 88 | StringBuilder nonceSb = new StringBuilder(encNonce.length()); 89 | for (char ar : nonceChar) { 90 | if (ar >= '0' && ar <= '9' || ar >= 'a' && ar <= 'z' || ar >= 'A' && ar <= 'Z') { 91 | nonceSb.append(ar); 92 | } 93 | } 94 | 95 | // prealloc probably saves us 10% on this op sequence 96 | hashBase = new StringBuilder(hashBufferSize); // size of key + nonce + difficult + argon + data + spacers 97 | hashBase.append(this.publicKey).append("-"); 98 | hashBase.append(nonceSb).append("-"); 99 | hashBase.append(this.data).append("-"); 100 | hashBase.append(this.difficultyString); 101 | 102 | rawNonce = nonceSb.toString(); 103 | rawHashBase = hashBase.toString(); 104 | bestDL = Long.MAX_VALUE; 105 | } 106 | 107 | 108 | @Override 109 | public void run() { 110 | active = true; 111 | long start = System.currentTimeMillis(); 112 | long lastUpdate = start; 113 | 114 | StringBuilder hashBase = null; 115 | byte[] byteBase = null; 116 | 117 | String argon = null; 118 | Argon2 argon2 = Argon2Factory.create(Argon2Types.ARGON2i); 119 | 120 | MessageDigest sha512 = null; 121 | try { 122 | sha512 = MessageDigest.getInstance("SHA-512"); 123 | } catch (NoSuchAlgorithmException e1) { 124 | System.err.println("Unable to find SHA-512 algorithm! Fatal error."); 125 | e1.printStackTrace(); 126 | System.exit(1); 127 | active = false; 128 | } 129 | if (active) { 130 | System.out.println("Spun up EXPERIMENTAL hashing worker in " + (System.currentTimeMillis() - start) + "ms"); 131 | start = System.currentTimeMillis(); 132 | genNonce(); 133 | } 134 | 135 | long statCycle = 0l; 136 | long statBegin = 0l; 137 | long statArgonBegin = 0l; 138 | long statArgonEnd = 0l; 139 | long statEnd = 0l; 140 | long stuck = 0; 141 | 142 | int cCount = 0; 143 | double speed = 0d; 144 | double avgSpeed = 0d; 145 | 146 | statCycle = System.currentTimeMillis(); 147 | 148 | while (active) { 149 | 150 | if (System.currentTimeMillis()-lastUpdate > 2000) { 151 | System.out.println("--> Last hash rate: " + speed + " H/s Average: " + avgSpeed + " H/s Total hashes: " + hashCount + " Mining Time: " + ((System.currentTimeMillis() - start) / 1000d) + 152 | " Shares: " + shares + " Finds: " + finds); 153 | lastUpdate = System.currentTimeMillis(); 154 | } 155 | 156 | statBegin = System.nanoTime(); 157 | try { 158 | hashBase = new StringBuilder(this.hashBufferSize); 159 | statArgonBegin = System.nanoTime(); 160 | argon = argon2.hash(4, 16384, 4, rawHashBase); 161 | statArgonEnd = System.nanoTime(); 162 | hashBase.append(rawHashBase).append(argon); 163 | 164 | byteBase = hashBase.toString().getBytes(); 165 | for (int i = 0; i < 5; i++) { 166 | byteBase = sha512.digest(byteBase); 167 | } 168 | byteBase = sha512.digest(byteBase); 169 | 170 | StringBuilder duration = new StringBuilder(25); 171 | duration.append(byteBase[10] & 0xFF).append(byteBase[15] & 0xFF).append(byteBase[20] & 0xFF) 172 | .append(byteBase[23] & 0xFF).append(byteBase[31] & 0xFF).append(byteBase[40] & 0xFF) 173 | .append(byteBase[45] & 0xFF).append(byteBase[55] & 0xFF); 174 | 175 | long finalDuration = new BigInteger(duration.toString()).divide(this.difficulty).longValue(); 176 | if (finalDuration > 0 && finalDuration <= this.limit) { 177 | if (finalDuration <= 240) { 178 | finds++; 179 | } else { 180 | shares++; 181 | } 182 | genNonce(); // only gen a new nonce once we exhaust the one we had 183 | } 184 | 185 | hashCount++; 186 | cCount++; 187 | statEnd = System.nanoTime(); 188 | 189 | if (finalDuration < this.bestDL) { // split the difference; if we're not getting movement after a while, just move on 190 | this.bestDL = finalDuration; 191 | stuck = 0; 192 | } else { 193 | stuck++; 194 | if (stuck > avgSpeed * 15) { 195 | genNonce(); 196 | stuck = 0; 197 | } 198 | } 199 | 200 | 201 | if (cCount == 100) { 202 | cCount = 0; 203 | long cycleEnd = System.currentTimeMillis(); 204 | speed = 100d / ((cycleEnd - statCycle)/ 1000d); 205 | avgSpeed = (double) hashCount / ((cycleEnd - start) / 1000d); 206 | statCycle = cycleEnd; 207 | } 208 | 209 | } catch (Exception e) { 210 | System.err.println("This worker failed somehow. Killing it."); 211 | e.printStackTrace(); 212 | active = false; 213 | } 214 | } 215 | System.out.println("This worker is now inactive."); 216 | } 217 | 218 | } 219 | -------------------------------------------------------------------------------- /arionum-benchmark/src/main/java/com/programmerdan/arionum/arionum_benchmark/Hasher.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_benchmark; 28 | 29 | import java.math.BigInteger; 30 | 31 | /** 32 | * Abstraction layer to allow multiple miner definitions. 33 | * 34 | * 35 | * @author ProgrammerDan (Daniel Boston) 36 | * 37 | */ 38 | public class Hasher implements Runnable { 39 | 40 | protected BigInteger difficulty = BigInteger.valueOf(100000000); 41 | protected String difficultyString = "100000000"; 42 | protected String data = "sampledatafornode"; 43 | protected int hashBufferSize; 44 | protected long limit = 50000; 45 | protected String publicKey = "4SxMLLWRCxoL42gqgf2C2x5T5Q1BJhgfQvMsfwrcFAu4owsBYncsvVnBBVqtxVrkqHVZ8nJGrcJB7yWnV92j5Rca"; 46 | 47 | // local stats stores, retrieved "off thread" 48 | protected long shares; 49 | protected long finds; 50 | 51 | /* Timing Stats, current */ 52 | protected long hashCount; 53 | 54 | protected boolean active; 55 | 56 | public static void main(String[] args) { 57 | if (args == null && args.length < 1) { 58 | System.err.println("Pass in core to test: stable | experimental | basic"); 59 | System.err.println(); 60 | System.exit(1); 61 | } 62 | 63 | Hasher hasher = null; 64 | AdvMode which = AdvMode.valueOf(args[0]); 65 | switch (which) { 66 | case basic: 67 | hasher = new BasicHasher(); 68 | break; 69 | case experimental: 70 | hasher = new ExperimentalHasher(); 71 | break; 72 | case stable: 73 | hasher = new StableHasher(); 74 | break; 75 | } 76 | 77 | hasher.run(); 78 | 79 | } 80 | 81 | @Override 82 | public void run() {} 83 | 84 | public Hasher() { 85 | this.hashCount = 0l; 86 | } 87 | 88 | public long getHashes() { 89 | return this.hashCount; 90 | } 91 | 92 | public boolean isActive() { 93 | return active; 94 | } 95 | 96 | } -------------------------------------------------------------------------------- /arionum-benchmark/src/main/java/com/programmerdan/arionum/arionum_benchmark/StableHasher.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_benchmark; 28 | 29 | import java.math.BigInteger; 30 | import java.security.MessageDigest; 31 | import java.security.NoSuchAlgorithmException; 32 | import java.security.SecureRandom; 33 | import java.util.Base64; 34 | import java.util.Base64.Encoder; 35 | 36 | import de.mkammerer.argon2.Argon2; 37 | import de.mkammerer.argon2.Argon2Factory; 38 | import de.mkammerer.argon2.Argon2Factory.Argon2Types; 39 | 40 | /** 41 | * The intent for this hasher is deeper self-inspection of running times of various components. 42 | * It can be used as a testbed for comparative performance. It is not meant to be used for general use 43 | * 44 | * @author ProgrammerDan (Daniel Boston) 45 | * 46 | */ 47 | public class StableHasher extends Hasher { 48 | 49 | public StableHasher() { 50 | 51 | } 52 | 53 | 54 | @Override 55 | public void run() { 56 | active = true; 57 | long start = System.currentTimeMillis(); 58 | long lastUpdate = start; 59 | 60 | byte[] nonce = new byte[32]; 61 | String encNonce = null; 62 | 63 | StringBuilder hashBase = new StringBuilder(); 64 | String base = null; 65 | byte[] byteBase = null; 66 | String argon = null; 67 | 68 | Encoder encoder = Base64.getEncoder(); 69 | char[] nonceChar = null; 70 | StringBuilder nonceSb = null; 71 | 72 | SecureRandom random = new SecureRandom(); 73 | 74 | Argon2 argon2 = Argon2Factory.create(Argon2Types.ARGON2i); 75 | 76 | MessageDigest sha512 = null; 77 | try { 78 | sha512 = MessageDigest.getInstance("SHA-512"); 79 | } catch (NoSuchAlgorithmException e1) { 80 | System.err.println("Unable to find SHA-512 algorithm! Fatal error."); 81 | e1.printStackTrace(); 82 | System.exit(1); 83 | active = false; 84 | } 85 | if (active) { 86 | System.out.println("Spun up Stable hashing worker in " + (System.currentTimeMillis() - start) + "ms"); 87 | start = System.currentTimeMillis(); 88 | } 89 | 90 | long statCycle = 0l; 91 | long statBegin = 0l; 92 | long statArgonBegin = 0l; 93 | long statArgonEnd = 0l; 94 | long statEnd = 0l; 95 | 96 | int cCount = 0; 97 | double speed = 0d; 98 | double avgSpeed = 0d; 99 | 100 | statCycle = System.currentTimeMillis(); 101 | 102 | while (active) { 103 | 104 | if (System.currentTimeMillis()-lastUpdate > 2000) { 105 | System.out.println("--> Last hash rate: " + speed + " H/s Average: " + avgSpeed + " H/s Total hashes: " + hashCount + " Mining Time: " + ((System.currentTimeMillis() - start) / 1000d) + 106 | " Shares: " + shares + " Finds: " + finds); 107 | lastUpdate = System.currentTimeMillis(); 108 | } 109 | 110 | statBegin = System.nanoTime(); 111 | try { 112 | random.nextBytes(nonce); 113 | encNonce = encoder.encodeToString(nonce); 114 | 115 | // shaves a bit off vs regex -- for this operation, about 50% savings 116 | nonceSb = new StringBuilder(encNonce.length()); 117 | nonceChar = encNonce.toCharArray(); 118 | for (char ar : nonceChar) { 119 | if (ar >= '0' && ar <= '9' || ar >= 'a' && ar <= 'z' || ar >= 'A' && ar <= 'Z') { 120 | nonceSb.append(ar); 121 | } 122 | } 123 | 124 | // prealloc probably saves us 10% on this op sequence 125 | // TODO: precompute this length when data is received 126 | hashBase = new StringBuilder(hashBufferSize); // size of key + none + difficult + argon + data + spacers 127 | hashBase.append(this.publicKey).append("-"); 128 | hashBase.append(nonceSb).append("-"); 129 | hashBase.append(this.data).append("-"); 130 | // TODO: precompute difficulty as string 131 | hashBase.append(this.difficultyString); 132 | statArgonBegin = System.nanoTime(); 133 | argon = argon2.hash(4, 16384, 4, hashBase.toString()); 134 | statArgonEnd = System.nanoTime(); 135 | hashBase.append(argon); 136 | 137 | base = hashBase.toString(); 138 | byteBase = base.getBytes(); 139 | for (int i = 0; i < 5; i++) { 140 | byteBase = sha512.digest(byteBase); 141 | } 142 | byteBase = sha512.digest(byteBase); 143 | 144 | StringBuilder duration = new StringBuilder(25); 145 | duration.append(byteBase[10] & 0xFF).append(byteBase[15] & 0xFF).append(byteBase[20] & 0xFF) 146 | .append(byteBase[23] & 0xFF).append(byteBase[31] & 0xFF).append(byteBase[40] & 0xFF) 147 | .append(byteBase[45] & 0xFF).append(byteBase[55] & 0xFF); 148 | 149 | long finalDuration = new BigInteger(duration.toString()).divide(this.difficulty).longValue(); 150 | if (finalDuration > 0 && finalDuration <= this.limit) { 151 | if (finalDuration < 240) { 152 | finds++; 153 | } else { 154 | shares++; 155 | } 156 | } 157 | 158 | hashCount++; 159 | cCount++; 160 | statEnd = System.nanoTime(); 161 | 162 | if (cCount == 100) { 163 | cCount = 0; 164 | long cycleEnd = System.currentTimeMillis(); 165 | speed = 100d / ((cycleEnd - statCycle)/ 1000d); 166 | avgSpeed = (double) hashCount / ((cycleEnd - start) / 1000d); 167 | statCycle = cycleEnd; 168 | } 169 | 170 | } catch (Exception e) { 171 | System.err.println("This worker failed somehow. Killing it."); 172 | e.printStackTrace(); 173 | active = false; 174 | } 175 | } 176 | System.out.println("This worker is now inactive."); 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /arionum-miner/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | *.exe 3 | *.jar 4 | /*.sh 5 | /*.bat 6 | *.cfg 7 | **/dependency-reduced-pom.xml 8 | dependency-reduced-pom.xml 9 | .project 10 | .classpath 11 | .settings/ 12 | -------------------------------------------------------------------------------- /arionum-miner/DOCS.md: -------------------------------------------------------------------------------- 1 | Extended Documentation for Java port of Arionum-miner 2 | ========================== 3 | 4 | # Installation 5 | 6 | Choose which image you want to install from the downloads below. 7 | 8 | If your hardware supports AVX or AVX2 and you are a Windows user, download the correctly named EXE and run. If your system does not support it, you will know on launch as it will fail noisily. If you know your system supports AVX512F, please contact me on Discord. All prior testing of AVX512F builds on Windows were unsuccessful, but I can work with you to see what we can accomplish. 9 | 10 | If you see crazy high hashrates, you are using 32bit java and these programs will _not_ function. Immediately shut off miner and go here: http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html and download a 64 bit jre. 11 | 12 | # Release details: 13 | 14 | * Replaced `standard` and `enhanced` hashers with a hasher that trades a very small (0.1% or so) hashrate for significantly improved accept rate, as the method of creating salts and nonces matches the code that will verify them. In addition, improved affinity and memory handling quickly moves away from memory that results in rejected hashes; only "successful" memory is kept, and its kept for a long time. 15 | 16 | * Better handling in general of the differences in affinity and memory handling between Windows and Linux 17 | 18 | # General Details: 19 | 20 | * Auto-adapt to block 10800-hf-resistance to remain compliance with primary node code. 21 | 22 | * Re-architected to use CPU affinity and single-threaded argon, so that each core can be pegged individually. 23 | 24 | * Improved stats, including colorized display for Windows and \*nix 25 | 26 | * Advanced installation options for maximizing performance. 27 | 28 | ## Advanced installation: 29 | 30 | ### Windows: 31 | 32 | 1. Install Maven, git, and Java 8 JDK (minimum). 33 | 34 | 2. Clone this repository locally, and navigate to `arionum-miner` folder. 35 | 36 | 3. Run `mvn clean package` 37 | 38 | 4. Run in a Command Prompt: `pick-argon.bat` -- it will ask for Administrator escalation, this is to check for hardware features, please accept. 39 | 40 | 5. Run miner using `run.bat`. 41 | 42 | Alternatively, if you know that your CPU supports AVX2 or AVX instructions, download the appropriate pre-built .exe here. 43 | 44 | If you are truly a glutton for punishment, you can download Visual Studio, import the argon2i library that I host here: http://github.com/ProgrammerDan/arionum-argon2 import it into visual studio, and build it locally. No warrantees, but you can use the Static Release profiles. 45 | 46 | 47 | ### Linux: 48 | 49 | 1. Install Maven, git, 64bit Java 8 JDK, make, and gcc. 50 | 51 | 2. Clone this repository locally, and navigate to `arionum-miner` folder. 52 | 53 | 3. Run `mvn clean package` 54 | 55 | 4. Run `chmod +x build-argon.sh` 56 | 57 | 5. Run `build-argon.sh` 58 | 59 | 6. Run miner using `run.sh` -- this will use the locally built high-performance argon2i libraries automatically. 60 | 61 | For Linux users, I _strongly_ recommend using these instructions. 62 | 63 | --------------------------- 64 | 65 | # Advanced Runtime Support 66 | 67 | New as of 0.1.0 and later versions, just run the miner! Don't mess with command line flags. 68 | 69 | If you want to use command line flags, however, follow along: 70 | 71 | ---------------------------- 72 | 73 | For multi-preconfigs, simply pass the name of the config file as the first and only command line flag: 74 | 75 | ``` java -jar arionum-miner-java.jar my-custom-config.cfg``` 76 | 77 | And if that config doesn't exist, it will create it with the answers you give to the prompts. If it does exist, it will load it and use it. 78 | 79 | 80 | ---------------------------- 81 | 82 | For pool-based mining, full command line flags: 83 | 84 | ``` java -jar arionum-miner-java.jar pool [pool address] [wallet address] [# hashers] [hasher core] [colored output]``` 85 | 86 | **pool address**: If you use the main pool, http://aropool.com -- if you host your own pool, use that pool's address. 87 | 88 | **wallet address**: Your Wallet address, or the wallet address you want to gain credit for your mining. 89 | 90 | **# hashers**: A number greater then 0 indicating how many hashers to run within this single miner instance. Be warned, more hashers does not always lead to higher Hr/s -- experiment with this value to find the best for your system. 91 | 92 | **hasher core**: Currently, one of: 93 | 94 | `standard`: Stable, best, most improved with best speed AND acceptance. 95 | 96 | `experimental`: Less handling for affinity and memory, use standard. 97 | 98 | **colored output**: Only supported in linux, set to true to add some color to the output of the statistic updates. Note, this isn't working yet. 99 | 100 | -------------------- 101 | 102 | For solo mining, full command line flags: 103 | 104 | ``` java -jar arionum-miner-java.jar solo [node address] [public-key] [private-key] [# hashers] [hasher core] [colored output]``` 105 | 106 | **node address**: The address of the node you want to directly send discovered blocks 107 | 108 | **public key**: Your Wallet address, or the wallet address you want to gain credit for your mining. 109 | 110 | **private key**: Private key to use to sign the payout transaction. 111 | 112 | **# hashers**: A number greater then 0 indicating how many hashers to run within this single miner instance. Be warned, more hashers does not always lead to higher Hr/s -- experiment with this value to find the best for your system. 113 | 114 | **hasher core**: Currently, one of: 115 | 116 | `standard`: Stable, best, most improved with best speed AND acceptance. 117 | 118 | `experimental`: Less handling for affinity and memory, use standard. 119 | 120 | **colored output**: Only supported in linux, set to true to add some color to the output of the statistic updates. 121 | 122 | Be sure you trust the node you are using; and be aware that the hasher's host needs to be specifically allowed by the node you are connecting to. 123 | 124 | Be aware that at present, all data is sent in the clear -- you might want to use a secure VPN or exclusively mine towards local network nodes. 125 | -------------------------------------------------------------------------------- /arionum-miner/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 3 | 4 | www.arionum.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of 15 | the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 21 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 23 | OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | 26 | Libraries used (such as argon2-jvm and json-simple) are covered by their own licenses, I use them as-is without warranty. 27 | -------------------------------------------------------------------------------- /arionum-miner/README.md: -------------------------------------------------------------------------------- 1 | Arionum Miner port for Java 2 | ==================== 3 | by ProgrammerDan, Jan 2018 4 | based on https://github.com/arionum/miner a work by AroDev 5 | 6 | Originally a fun evening project to port the PHP miner to Java that has become a bit of a passion. 7 | 8 | Confirmed working, has been used to find blocks. 9 | 10 | Includes a few improvements over the reference implementation, mostly with threaded handling of update requests and submit requests. 11 | 12 | That way, the miner threads can keep on mining, and aren't held up by the update / submit cycles. 13 | 14 | Please note this release includes at-your-own-risk 80k hardfork support. This is a work in progress! It will likely do weird things! 15 | As always, refer back to reference PHP miner if issues emerge. 16 | 17 | Enjoy! 18 | 19 | #### How to run 20 | 21 | Get dependency: 22 | 23 | * 64bit Java 8 JRE (OpenJRE 8 and Oracle Java 8 both work just fine) 24 | 25 | * On Linux or MacOSX, download the latest ".jar" release file 26 | 27 | * If you've got it on your desktop, you should be able to doubleclick it. 28 | 29 | * If that doesn't work, open a shell, go to the download folder, and run `./jar -java arionum-miner-java.jar` 30 | 31 | * On Windows, download the latest ".exe" release file 32 | 33 | * Double click the .exe to run 34 | 35 | * For some advanced systems, a special, optimized version is available. Check if your CPU supports AVX or AVX2 and use that version if supported. 36 | 37 | * Follow interactive prompt -- if a default is offered, press "enter" key to accept it. I recommend using defaults. 38 | 39 | * Configuration is automatically saved and used again when you open the miner up next 40 | 41 | * Default config is `config.cfg` in the same folder as the .jar or .exe -- open it in any text editor to modify 42 | 43 | #### To compile on Linux 44 | 45 | Get depencies 46 | 47 | * Maven 48 | 49 | * git 50 | 51 | * 64bit Java 8 SDK (OpenJDK8 and Oracle Java 8 both work just fine) 52 | 53 | Clone this repository, change directory into ```arionum-miner``` 54 | 55 | Execute ```mvn clean package``` -- this will build the miner 56 | 57 | Run ```./build-argon.sh``` -- this will build the argon and run shell 58 | 59 | Launch with ```./run.sh``` -- follow interactive prompts to set up. 60 | 61 | For windows build instructions, check DOCS.md 62 | 63 | ##### Summary: 64 | 65 | Pick a place to install. For linux: 66 | 67 | `sudo apt-get install maven git openjdk-8-jdk` 68 | 69 | `git clone git://github.com/ProgrammerDan/arionum-java` 70 | 71 | `cd arionum-java/arionum-miner` 72 | 73 | `mvn clean package` 74 | 75 | `./run.sh` 76 | 77 | #### To run 78 | 79 | ##### For Linux / Mac OS: 80 | 81 | Execute `java -jar arionum-miner-java.jar` and follow the prompts on screen 82 | 83 | ##### For Windows: 84 | 85 | Execute `arionum-miner-java.exe` and follow the prompts on screen 86 | 87 | ##### For All: 88 | 89 | Take the defaults to run the pool, pointing at http://aropool.com , using your wallet address. If you take the defaults, the "standard" miner will run, using cores - 1. 90 | 91 | 92 | ##### For solo mining in Linux: 93 | 94 | Execute `java -jar arionum-miner-java.jar` and follow the prompts on screen, choose "solo" instead of pool. 95 | 96 | Please note solo hasn't been tested, but it's 99.9% the same code so should be fine. Pool has been extensively tested. 97 | 98 | 99 | ##### General advice: 100 | 101 | Rule of thumb is no more then 1 miner per core/vcore. 102 | 103 | ---------------------- 104 | # Comparison to php-miner 105 | 106 | Similarities: 107 | 108 | * Can be used for solo or pool mining 109 | 110 | * Checks for valid address (on pool) before activating 111 | 112 | * Command Line tool 113 | 114 | * Same hashing functions, same CPU bounds -- both bind to a C library version of argon2i hashing, so both are fast 115 | 116 | * Can run multiple instances if desired -- I recommend you use screen, tmux, or byobu for better monitoring 117 | 118 | Differences: 119 | 120 | * One JVM can run multiple hashers and share the same "update" requests to the pool, reducing traffic to the pool or node 121 | 122 | * The hashers never stop -- the reference PHP implementation pauses the hasher during update requests and nonce submissions. The java hashers never stop. 123 | 124 | * Hasher threads will try to affine to CPU cores -- "reserve" the core all to themselves. 125 | 126 | * Small to significant optimizations -- some "micro", some significant via cpu instruction sets. See release instructions for more details. 127 | 128 | * More information on active hashing activity -- screen updates every few seconds with details on active hashers, how many hashes they have checked, and how close to finding shares or blocks 129 | 130 | * Visual notice when a new block has started, with the "difficulty" for the new block, and your personal best DL for the prior block. Note that difficulty is inverse -- smaller is harder, larger is easier. 131 | 132 | --------------------- 133 | ### TODO 134 | 135 | * ~~Add address checking as here: https://github.com/arionum/miner/commit/e14b696362fb79d60c4ff8bc651185740b8021d9~~ Done in commit dd5388c 136 | 137 | * ~~Colored screen output to better see labels / stats and keep track of hashers~~ 138 | 139 | * Auto-adaptive hashing -- automatically add more hasher instances until peak rate is achieved 140 | 141 | * Variable intensity -- allow hashing to take a back seat to other computer activity 142 | 143 | --------------------- 144 | ### Known caveates 145 | 146 | * For multi-socket systems, try running a single full instance of the Java miner per socket. Use operating system tools to bind to the socket. 147 | 148 | * Hash Rate is "best effort", and might be imprecise. Pool Hash Rate is based on self-reporting, so minor inaccuracies are not an issue. Difficulty is based on median time between block discovery. 149 | 150 | --------------------- 151 | ### Testing 152 | 153 | If you encounter any problems, open an issue here or email me at programmerdan@gmail.com -- I'm also ProgrammerDan#7586 in the Arionum discord -- feel free to PM me. 154 | 155 | --------------------- 156 | ### Advanced Use 157 | 158 | See DOCS.md for advanced use, compilation, and other instructions. 159 | -------------------------------------------------------------------------------- /arionum-miner/WINDOWS_COMPILE_GUIDE.md: -------------------------------------------------------------------------------- 1 | ## Compile guide for Windows 10 for using ProgrammerDan's Java mining tool 2 | 3 | ### Do you need to compile? 4 | 5 | Only use these steps if the release `.exe` doesn't work for you, otherwise, prefer to use the release as it will be most stable, most tested, and easiest to run. If it fails, follow the steps below. 6 | 7 | ### Necessary program's 8 | - [Maven](https://maven.apache.org/) 9 | - [Git](https://desktop.github.com/) 10 | - [64Bit JavaSDK](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) 11 | 12 | ### Installation 13 | 1. Install all above programs 14 | 2. Create a [GitHub account](https://github.com/join?source=header-repo) 15 | 3. Log in with GitHub Desktop client. 16 | 4. Via File → Clone Repository go to tab URL and paste the following URL https://github.com/ProgrammerDan/arionum-java. 17 | 18 | You'll now have a clone of the Aronium-miner on your PC. 19 | 20 | 5. **Important**: Check system variables and see whether JAVA_HOME is registered as a system variable. 21 | - Windows 10: Press windows key, go to “Edit the system environment variables” . If prior to Windows 10, check this guide instead: https://www.java.com/en/download/help/path.xml 22 | - On the bottom click “Environment variables” 23 | - Bottom variables are the “System variables” 24 | - Check whether JAVA_HOME is in there, else; 25 | - Click new 26 | - In “Name of” fill in JAVA_HOME 27 | - In “Value of” put in the directory of the JavaSDK 28 | 6. Start up a command prompt 29 | 7. Navigate towards the aronium-miner directory (the one with the POM.xml file) 30 | 8. Execute “mvn clean package” (do not used quotes) 31 | 9. Run "pick-argon.bat" and follow the prompts to choose a pre-built argon2i library. 32 | 10. **Run the run.bat file.** Follow on screen prompts to configure your miner. 33 | 11. **Rename the aronium-java folder** to arionum-miner to avoid conflicts with GitHub Desktop if you want to catch the latest release. 34 | 35 | Need help? Contact Rezegen or ProgrammerDan for more help! 36 | 37 | Do not forget to donate ProgrammerDan for his awesome program: 38 | 4SxMLLWRCxoL42gqgf2C2x5T5Q1BJhgfQvMsfwrcFAu4owsBYncsvVnBBVqtxVrkqHVZ8nJGrcJB7yWnV92j5Rca 39 | 40 | -------------------------------------------------------------------------------- /arionum-miner/pom-arm.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.programmerdan.arionum 6 | arionum-miner 7 | 0.2.6.1-SNAPSHOT 8 | jar 9 | 10 | arionum-miner-java 11 | https://github.com/ProgrammerDan/arionum-java/arionum-miner 12 | 13 | 14 | UTF-8 15 | 1.8 16 | 1.8 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-compiler-plugin 24 | 3.5.1 25 | 26 | 1.8 27 | 1.8 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-shade-plugin 33 | 2.4.3 34 | 35 | 36 | package 37 | 38 | shade 39 | 40 | 41 | 42 | 43 | true 44 | 45 | 47 | 48 | com.programmerdan.arionum.arionum_miner.Miner 49 | 50 | 51 | 53 | META-INF/README.md 54 | README.md 55 | 56 | 58 | META-INF/LICENSE.md 59 | LICENSE.md 60 | 61 | 62 | 63 | 64 | junit:junit 65 | 66 | 67 | 68 | 69 | com.googlecode.json-simple:* 70 | 71 | ** 72 | 73 | 74 | 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-antrun-plugin 80 | 81 | 82 | Generate Executions 83 | prepare-package 84 | 85 | run 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | Forward one-clicks 102 | package 103 | 104 | run 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | net.java.dev.jna 124 | jna 125 | 4.5.0 126 | compile 127 | 128 | 129 | junit 130 | junit 131 | 3.8.1 132 | test 133 | 134 | 135 | com.googlecode.json-simple 136 | json-simple 137 | 1.1.1 138 | 139 | 140 | net.openhft 141 | affinity 142 | 3.1.7 143 | 144 | 145 | com.diogonunes 146 | JCDP 147 | 2.0.3.1 148 | 149 | 150 | org.slf4j 151 | slf4j-nop 152 | 1.7.25 153 | 154 | 155 | A full featured Java implementation of the Arionum miner. Portions derived from the php reference miner. 156 | 157 | -------------------------------------------------------------------------------- /arionum-miner/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.programmerdan.arionum 6 | arionum-miner 7 | 0.2.6.1-SNAPSHOT 8 | jar 9 | 10 | arionum-miner-java 11 | https://github.com/ProgrammerDan/arionum-java/arionum-miner 12 | 13 | 14 | UTF-8 15 | 1.8 16 | 1.8 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-compiler-plugin 24 | 3.5.1 25 | 26 | 1.8 27 | 1.8 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-shade-plugin 33 | 2.4.3 34 | 35 | 36 | package 37 | 38 | shade 39 | 40 | 41 | 42 | 43 | true 44 | 45 | 47 | 48 | com.programmerdan.arionum.arionum_miner.Miner 49 | 50 | 51 | 53 | META-INF/README.md 54 | README.md 55 | 56 | 58 | META-INF/LICENSE.md 59 | LICENSE.md 60 | 61 | 62 | 63 | 64 | junit:junit 65 | 66 | 67 | 68 | 69 | com.googlecode.json-simple:* 70 | 71 | ** 72 | 73 | 74 | 75 | 76 | 77 | 78 | com.akathist.maven.plugins.launch4j 79 | launch4j-maven-plugin 80 | 1.7.21 81 | 82 | 83 | l4j-cli-pkg 84 | package 85 | 86 | launch4j 87 | 88 | 89 | false 90 | console 91 | target/${project.build.finalName}-shaded.jar 92 | target/arionum-java-miner-${project.version}.exe 93 | 94 | 95 | . 96 | normal 97 | http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html 98 | 99 | true 100 | false 101 | 102 | 103 | 104 | 105 | false 106 | false 107 | 1.8.0 108 | 109 | preferJre 110 | 64 111 | 112 | 113 | 0.2.5.0 114 | ${project.version} 115 | Arionum Miner in Java 116 | 2018 AroDev, portions ProgrammerDan (MIT license) 117 | 0.2.5.0 118 | ${project.version} 119 | Arionum Miner in Java 120 | 121 | Arionum Miner 122 | ${project.name}.exe 123 | 124 | ENGLISH_US 125 | 126 | 127 | An error occurred while starting the application. 128 | This application was configured to use a bundled 129 | Java Runtime Environment but the runtime is missing or 130 | corrupted. 131 | This application requires a Java 8 Runtime 132 | Environment 133 | The registry refers to a nonexistent Java Runtime 134 | Environment installation or the runtime is corrupted. 135 | An application instance is already 136 | running. 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | org.apache.maven.plugins 145 | maven-antrun-plugin 146 | 147 | 148 | Generate Executions 149 | prepare-package 150 | 151 | run 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | Forward one-clicks 184 | package 185 | 186 | run 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | net.java.dev.jna 211 | jna 212 | 4.5.0 213 | compile 214 | 215 | 216 | junit 217 | junit 218 | 3.8.1 219 | test 220 | 221 | 222 | com.googlecode.json-simple 223 | json-simple 224 | 1.1.1 225 | 226 | 227 | net.openhft 228 | affinity 229 | 3.1.7 230 | 231 | 232 | com.diogonunes 233 | JCDP 234 | 2.0.3.1 235 | 236 | 237 | org.slf4j 238 | slf4j-nop 239 | 1.7.25 240 | 241 | 242 | A full featured Java implementation of the Arionum miner. Portions derived from the php reference miner. 243 | 244 | -------------------------------------------------------------------------------- /arionum-miner/src/main/bin/build-argon-arm.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo This runscript will attempt to clone and build a custom version of argon2i for use with this miner. 4 | echo It will also modify the run.sh file to use the built version of argon2i. 5 | echo It requires git, make, and gcc to be installed. It assumes you have a 64 bit cpu. 6 | 7 | git clone git://github.com/ProgrammerDan/arionum-argon2 arionum-argon 8 | 9 | cd arionum-argon 10 | 11 | git remote set-url origin git://github.com/ProgrammerDan/arionum-argon2 12 | 13 | git pull 14 | 15 | echo "We will use master branch, as it is the most stable arionum-focused trimmed release of argon2i available." 16 | 17 | git checkout master 18 | 19 | git pull 20 | 21 | echo "For ARM, we remove the 64bit target" 22 | echo " You will need to customize these flags if on other architectures." 23 | 24 | make clean && CFLAGS="" OPTTARGET=native NO_THREADS=1 make 25 | 26 | echo "#! /bin/bash 27 | java -Djna.library.path=\"`pwd`\" -jar arionum-miner-java.jar" > ../run.sh 28 | 29 | chmod +x ../run.sh 30 | 31 | -------------------------------------------------------------------------------- /arionum-miner/src/main/bin/build-argon.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo This runscript will attempt to clone and build a custom version of argon2i for use with this miner. 4 | echo It will also modify the run.sh file to use the built version of argon2i. 5 | echo It requires git, make, and gcc to be installed. It assumes you have a 64 bit cpu. 6 | 7 | git clone git://github.com/ProgrammerDan/arionum-argon2 arionum-argon 8 | 9 | cd arionum-argon 10 | 11 | git remote set-url origin git://github.com/ProgrammerDan/arionum-argon2 12 | 13 | git pull 14 | 15 | echo "We will use master branch, as it is the most stable arionum-focused trimmed release of argon2i available." 16 | 17 | git checkout master 18 | 19 | git pull 20 | 21 | echo "By default we will use a variety of flags to ensure best build for 64 bit x86 compatible hardware." 22 | echo " You will need to customize these flags if on other architectures." 23 | 24 | make clean && CFLAGS="-m64" OPTTARGET=native NO_THREADS=1 make 25 | 26 | case "$OSTYPE" in 27 | darwin*) cp libargon2.1.dylib libargon2.dylib;; 28 | *) cp libargon2.so.1 libargon2.so;; 29 | esac 30 | 31 | echo "#! /bin/bash 32 | java -Djna.library.path=\"`pwd`\" -jar arionum-miner-java.jar" > ../run.sh 33 | 34 | chmod +x ../run.sh 35 | 36 | -------------------------------------------------------------------------------- /arionum-miner/src/main/bin/pick-argon.bat: -------------------------------------------------------------------------------- 1 | @Echo OFF 2 | echo This runscript will attempt to pick the best custom version of argon2i for use with this miner. 3 | echo It will also modify the run.bat file to use the built version of argon2i. 4 | echo Note that this script will launch a powershell in administrator mode. Please give it permission. 5 | 6 | pause 7 | 8 | set scriptFileName=%~n0 9 | set scriptFolderPath=%~dp0 10 | set powershellScriptFileName=%scriptFileName%.ps1 11 | 12 | powershell -Command "Start-Process powershell \"-ExecutionPolicy Bypass -NoProfile -Command `\"cd \`\"%scriptFolderPath%`\"; & \`\".\%powershellScriptFileName%\`\"`\"\" -Verb RunAs" 13 | 14 | -------------------------------------------------------------------------------- /arionum-miner/src/main/bin/pick-argon.ps1: -------------------------------------------------------------------------------- 1 | If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) 2 | 3 | { 4 | $arguments = "& '" + $myinvocation.mycommand.definition + "'" 5 | Start-Process powershell -Verb runAs -ArgumentList $arguments 6 | Break 7 | } 8 | 9 | $source = "https://download.sysinternals.com/files/Coreinfo.zip" 10 | 11 | rmdir -Force -Recurse Coreinfo 12 | 13 | curl $source -OutFile Coreinfo.zip 14 | 15 | Expand-Archive Coreinfo.zip 16 | 17 | cd Coreinfo 18 | 19 | $extensions = .\Coreinfo.exe /accepteula 20 | 21 | $avx512f = select-string -Pattern "AVX512F" -InputObject $extensions 22 | $avx2 = select-string -Pattern "AVX2" -InputObject $extensions 23 | $avx = select-string -Pattern "AVX" -InputObject $extensions 24 | 25 | cd .. 26 | 27 | if (-not ([string]::IsNullOrEmpty($avx512f))) 28 | { 29 | echo "Found AVX512F extensions." 30 | cp src\main\resources\win32-x86-64\argon2-avx512f.dll argon2.dll 31 | } 32 | elseif (-not ([string]::IsNullOrEmpty($avx2))) 33 | { 34 | echo "Found AVX2 extensions." 35 | cp src\main\resources\win32-x86-64\argon2-avx2.dll argon2.dll 36 | } 37 | elseif (-not ([string]::IsNullOrEmpty($avx))) 38 | { 39 | echo "Found AVX extensions." 40 | cp src\main\resources\win32-x86-64\argon2-avx.dll argon2.dll 41 | } 42 | else 43 | { 44 | echo "Did not find any CPU acceleration." 45 | cp src\main\resources\win32-x86-64\argon2.dll argon2.dll 46 | } 47 | 48 | echo "Run the run.bat file to launch java using the best discovered argon2 library." 49 | 50 | sleep 8 51 | exit 52 | 53 | -------------------------------------------------------------------------------- /arionum-miner/src/main/bin/run.bat: -------------------------------------------------------------------------------- 1 | java -Djna.library.path="%cd%" -jar arionum-miner-java.jar 2 | -------------------------------------------------------------------------------- /arionum-miner/src/main/bin/run.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | java -jar arionum-miner-java.jar 4 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/AdvMode.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_miner; 28 | 29 | /** 30 | * Controls runmode 31 | * 32 | * @author ProgrammerDan 33 | * 34 | */ 35 | public enum AdvMode { 36 | 37 | standard(true), 38 | enhanced(true), 39 | legacy(false), 40 | experimental(false), 41 | basic(false), 42 | stable(false), 43 | auto(false), 44 | gpu(false), 45 | mixed(false); 46 | 47 | boolean use; 48 | 49 | AdvMode(boolean use) { 50 | this.use = use; 51 | } 52 | 53 | public boolean useThis() { 54 | return use; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/AggressiveAffinityThreadFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_miner; 28 | 29 | import java.util.ArrayList; 30 | import java.util.concurrent.ConcurrentHashMap; 31 | 32 | import java.util.concurrent.ThreadFactory; 33 | 34 | import net.openhft.affinity.AffinityLock; 35 | import net.openhft.affinity.AffinityStrategies; 36 | import net.openhft.affinity.AffinitySupport; 37 | import net.openhft.affinity.CpuLayout; 38 | import net.openhft.affinity.Affinity; 39 | 40 | public class AggressiveAffinityThreadFactory implements ThreadFactory { 41 | 42 | /** 43 | * Windows apparently allows the affine but fails to properly report the affine later... 44 | */ 45 | public static ConcurrentHashMap AffineMap = new ConcurrentHashMap<>(); 46 | 47 | private final String name; 48 | private final boolean daemon; 49 | 50 | private int id = 1; 51 | 52 | private AffinityLock lastLock = null; 53 | 54 | private ArrayList plan = new ArrayList(); 55 | private int planFailures = 0; 56 | 57 | public AggressiveAffinityThreadFactory(String name) { 58 | this(name, true); 59 | } 60 | 61 | public AggressiveAffinityThreadFactory(String name, boolean daemon) { 62 | this.name = name; 63 | this.daemon = daemon; 64 | 65 | // make a CPU layout plan. 66 | try { 67 | CpuLayout layout = AffinityLock.cpuLayout(); 68 | if (layout != null) { 69 | // we want to fill out each socket's cores, then do virtualized threads if any. 70 | System.out.println("Using Affinity Plan Socket / Core / Thread by CPU: "); 71 | for (int t = 0; t < layout.threadsPerCore(); t++) { 72 | for (int s = 0; s < layout.sockets(); s++) { 73 | for (int c = 0; c < layout.coresPerSocket(); c++) { 74 | for (int i = 0; i < layout.cpus(); i++) { 75 | if (layout.socketId(i) == s && 76 | layout.coreId(i) == c && 77 | layout.threadId(i) == t) { 78 | plan.add(i); 79 | System.out.println(String.format("%d: Socket %d, Core %d, Thread %d", 80 | plan.size(), s, c, t)); 81 | break; 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } else { 88 | plan = null; 89 | } 90 | } catch (Exception e) { 91 | System.err.println("Unable to determine CPU layout, disabling affinity just to be safe."); 92 | Miner.disableAffinity(); 93 | } 94 | 95 | } 96 | 97 | @Override 98 | public synchronized Thread newThread(final Runnable r) { 99 | final int myid = id; 100 | String name2 = myid <= 1 ? name : (name + '-' + myid); 101 | id++; 102 | // System.err.println("Creating a new thread: " + name2); 103 | Thread t = new Thread(new Runnable() { 104 | @Override 105 | public void run() { 106 | if (Miner.PERMIT_AFINITY) { 107 | try { 108 | if (plan == null || myid > plan.size() || planFailures * 2 > plan.size()) { 109 | if (myid < AffinityLock.cpuLayout().cpus()) { 110 | AffinityLock lock = lastLock == null ? AffinityLock.acquireLock() : lastLock.acquireLock(AffinityStrategies.SAME_SOCKET, AffinityStrategies.DIFFERENT_CORE, AffinityStrategies.ANY); 111 | if (!lock.isBound()) { 112 | lock = AffinityLock.acquireCore(); 113 | } 114 | if (!lock.isBound()) { 115 | lock = AffinityLock.acquireLock(myid); 116 | } 117 | if (!lock.isBound()) { 118 | System.err.println("Not a problem, but thread " + name2 119 | + " could not immediately reserve a core, it may experience decayed performance."); 120 | AffineMap.put(Affinity.getThreadId(), -1); 121 | } else { 122 | System.out.println("Awesome! Thread " + name2 + " affined! CPU ID " + lock.cpuId() + " Process Thread ID " 123 | + Affinity.getThreadId()); 124 | AffineMap.put(Affinity.getThreadId(), lock.cpuId()); 125 | lastLock = lock; 126 | } 127 | 128 | r.run(); 129 | 130 | lock.close(); 131 | } else { 132 | System.err.println("Not a problem, but thread " + name2 133 | + " could not immediately reserve a core, as there are no more cores to assign to. If performance is degraded, attempt with fewer hashers."); 134 | 135 | AffineMap.put(Affinity.getThreadId(), -1); 136 | 137 | r.run(); 138 | } 139 | } else { 140 | System.out.println("The plan: Affine Thread " + name2 + " to CPU ID " + plan.get(myid - 1) + ". Note: Process Thread ID " 141 | + Affinity.getThreadId()); 142 | AffinityLock lock = AffinityLock.acquireLock(plan.get(myid - 1)); 143 | 144 | if (!lock.isBound()) { 145 | System.err.println("Our plan failed :(. Thread " + name2 + " did not affine to CPU ID " + plan.get(myid - 1) + " as we had hoped. Note: Process Thread ID " 146 | + Affinity.getThreadId()); 147 | AffineMap.put(Affinity.getThreadId(), -1); 148 | } else { 149 | System.out.println("Awesome! Thread " + name2 + " affined! CPU ID " + lock.cpuId() + " Process Thread ID " 150 | + Affinity.getThreadId()); 151 | AffineMap.put(Affinity.getThreadId(), lock.cpuId()); 152 | lastLock = lock; 153 | } 154 | 155 | r.run(); 156 | 157 | lock.close(); 158 | } 159 | } catch (Throwable e) { 160 | System.err.println("Ouch: thread " + name2 + " died with error:"); 161 | e.printStackTrace(); 162 | System.err.println("Depending on the error, you might consider shutting this down."); 163 | System.err.println("For now though, we're disabling affinity."); 164 | Miner.disableAffinity(); 165 | } 166 | } 167 | 168 | if (!Miner.PERMIT_AFINITY) { 169 | r.run(); 170 | 171 | System.out.println("Awesome! Thread " + name2 + " running in affinity-free mode!"); 172 | } 173 | } 174 | }, name2); 175 | t.setDaemon(daemon); 176 | return t; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/CPrint.java: -------------------------------------------------------------------------------- 1 | package com.programmerdan.arionum.arionum_miner; 2 | 3 | import com.diogonunes.jcdp.color.ColoredPrinter; 4 | import com.diogonunes.jcdp.color.api.Ansi.Attribute; 5 | import com.diogonunes.jcdp.color.api.Ansi.BColor; 6 | import com.diogonunes.jcdp.color.api.Ansi.FColor; 7 | 8 | public class CPrint { 9 | private ColoredPrinter coPrint = new ColoredPrinter.Builder(2, false).build(); 10 | private boolean color = false; 11 | 12 | private Attribute attr = Attribute.NONE; 13 | private FColor fore = FColor.WHITE; 14 | private BColor back = BColor.BLACK; 15 | 16 | public CPrint(boolean color) { 17 | this.color = color; 18 | } 19 | 20 | public CPrint p(Object msg) { 21 | if (color) { 22 | coPrint.print(msg, attr, fore, back); 23 | } else { 24 | System.out.print(msg); 25 | } 26 | return this; 27 | } 28 | 29 | public CPrint fp(String format, Object msg) { 30 | if (color) { 31 | coPrint.print(String.format(format, msg), attr, fore, back); 32 | } else { 33 | System.out.print(String.format(format, msg)); 34 | } 35 | return this; 36 | } 37 | 38 | public CPrint fs(Object msg) { 39 | if (color) { 40 | coPrint.print(String.format("%s", msg), attr, fore, back); 41 | } else { 42 | System.out.print(String.format("%s", msg)); 43 | } 44 | return this; 45 | } 46 | 47 | public CPrint fd(Object msg) { 48 | if (color) { 49 | coPrint.print(String.format("%d", msg), attr, fore, back); 50 | } else { 51 | System.out.print(String.format("%d", msg)); 52 | } 53 | return this; 54 | } 55 | 56 | public CPrint ff(Object msg) { 57 | if (color) { 58 | coPrint.print(String.format("%f", msg), attr, fore, back); 59 | } else { 60 | System.out.print(String.format("%f", msg)); 61 | } 62 | return this; 63 | } 64 | 65 | public CPrint ln(Object msg) { 66 | if (color) { 67 | coPrint.println(msg, attr, fore, back); 68 | } else { 69 | System.out.println(msg); 70 | } 71 | return this; 72 | } 73 | 74 | public CPrint ln() { 75 | if (color) { 76 | coPrint.println("", attr, fore, back); 77 | } else { 78 | System.out.println(); 79 | } 80 | return this; 81 | } 82 | 83 | public CPrint a(Attribute attr) { 84 | if (color) coPrint.setAttribute(attr); 85 | this.attr = attr; 86 | return this; 87 | } 88 | 89 | public CPrint f(FColor color) { 90 | if (this.color) coPrint.setForegroundColor(color); 91 | this.fore = color; 92 | return this; 93 | } 94 | 95 | public CPrint b(BColor color) { 96 | if (this.color) coPrint.setBackgroundColor(color); 97 | this.back = color; 98 | return this; 99 | } 100 | 101 | public CPrint clr() { 102 | if (color) coPrint.clear(); 103 | this.attr = Attribute.NONE; 104 | this.fore = FColor.WHITE; 105 | this.back = BColor.BLACK; 106 | return this; 107 | } 108 | 109 | public CPrint updateLabel() { 110 | return this.clr().a(Attribute.BOLD).f(FColor.CYAN).b(BColor.BLACK); 111 | } 112 | 113 | public CPrint updateMsg() { 114 | return this.clr().a(Attribute.NONE).f(FColor.CYAN).b(BColor.BLACK); 115 | } 116 | 117 | public CPrint info() { 118 | return this.clr().a(Attribute.DARK).f(FColor.CYAN).b(BColor.BLACK); 119 | } 120 | 121 | public CPrint statusLabel() { 122 | return this.clr().a(Attribute.NONE).f(FColor.CYAN).b(BColor.BLACK); 123 | } 124 | 125 | public CPrint normData() { 126 | return this.clr().a(Attribute.BOLD).f(FColor.GREEN).b(BColor.BLACK); 127 | } 128 | 129 | public CPrint dlData() { 130 | return this.clr().a(Attribute.BOLD).f(FColor.YELLOW).b(BColor.BLACK); 131 | } 132 | 133 | public CPrint unitLabel() { 134 | return this.clr().a(Attribute.NONE).f(FColor.WHITE).b(BColor.BLACK); 135 | } 136 | 137 | public CPrint textData() { 138 | return this.clr().a(Attribute.DARK).f(FColor.WHITE).b(BColor.BLACK); 139 | } 140 | 141 | public CPrint headers() { 142 | return this.clr().f(FColor.CYAN).a(Attribute.LIGHT).b(BColor.BLACK); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/ExperimentalHasher.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_miner; 28 | 29 | import java.math.BigInteger; 30 | import java.security.MessageDigest; 31 | import java.security.NoSuchAlgorithmException; 32 | import java.security.SecureRandom; 33 | import java.util.Base64; 34 | import java.util.Base64.Encoder; 35 | import java.util.Random; 36 | import java.util.BitSet; 37 | 38 | import net.openhft.affinity.Affinity; 39 | import net.openhft.affinity.AffinityLock; 40 | 41 | import com.programmerdan.arionum.arionum_miner.jna.*; 42 | 43 | /** 44 | * The intent for this hasher is deeper self-inspection of running times of various components. It can be used as a testbed for comparative performance. It is not meant to be used for general use 45 | * 46 | * This particular experimental hasher takes advantage of an observation: namely, that we're doing double the randomness minimally necessary for the scheme, since the argon2i implementation here and in the php reference internally salts the 47 | * "password" with 32 bytes of random data. So the nonce itself can be considered just a payload, fixed entity, and allow the salting of the argon2i to control uniformly random generation of SHA-512 DL outcomes. 48 | * 49 | * This gives me 5-10% H/s speedups on isolated testing with no increase in rejections. Block finds and shares remain as expected for H/s observed. 50 | * 51 | * Another indicator of improved performance is tracking reveals 99.97% of time is spent in argon2i codepath, vs. between 99.8 and 99.9% for other cores. This might sound small but adds up in a big way over time, as its a per-hash 52 | * improvement. 53 | * 54 | * Once a nonce is submitted, it is discarded and a new one generated, as the pool does not allow resubmission of prior nonces. 55 | * 56 | * This is deprecated; use MappedHasher instead. 57 | * 58 | * @author ProgrammerDan (Daniel Boston) 59 | * 60 | */ 61 | public class ExperimentalHasher extends Hasher { 62 | 63 | private final JnaUint32 iterations = new JnaUint32(1); 64 | private final JnaUint32 memory = new JnaUint32(524288); 65 | private final JnaUint32 parallelism = new JnaUint32(1); 66 | private final JnaUint32 saltLenI = new JnaUint32(16); 67 | private final JnaUint32 hashLenI = new JnaUint32(32); 68 | private final Size_t saltLen = new Size_t(16l); 69 | private final Size_t hashLen = new Size_t(32l); 70 | private final Size_t encLen; 71 | private byte[] encoded; 72 | private final Argon2Library argonlib; 73 | private final Argon2_type argonType = new Argon2_type(1l); 74 | 75 | public ExperimentalHasher(Miner parent, String id, long target, long maxTime) { 76 | super(parent, id, target, maxTime); 77 | 78 | // SET UP ARGON FOR DIRECT-TO-JNA-WRAPPER-EXEC 79 | argonlib = Argon2Library.INSTANCE; 80 | encLen = argonlib.argon2_encodedlen(iterations, memory, parallelism, saltLenI, hashLenI, 81 | argonType); 82 | encoded = new byte[encLen.intValue()]; 83 | } 84 | 85 | private SecureRandom random = new SecureRandom(); 86 | private Random insRandom = new Random(random.nextLong()); 87 | private Encoder encoder = Base64.getEncoder(); 88 | private String rawHashBase; 89 | private byte[] nonce = new byte[32]; 90 | private byte[] salt = new byte[16]; 91 | private String rawNonce; 92 | private byte[] hashBaseBuffer; 93 | private Size_t hashBaseBufferSize; 94 | private byte[] fullHashBaseBuffer; 95 | 96 | @Override 97 | public void update(BigInteger difficulty, String data, long limit, String publicKey, long blockHeight, 98 | boolean pause, int iters, int mem, int threads) { 99 | super.update(difficulty, data, limit, publicKey, blockHeight, pause, iters, mem, threads); 100 | 101 | genNonce(); 102 | } 103 | 104 | @Override 105 | public void newHeight(long oldBlockHeight, long newBlockHeight) { 106 | // no-op, we are locked into 10800 > territory now 107 | } 108 | 109 | private void genNonce() { 110 | insRandom = new Random(random.nextLong()); 111 | String encNonce = null; 112 | StringBuilder hashBase; 113 | random.nextBytes(nonce); 114 | encNonce = encoder.encodeToString(nonce); 115 | 116 | char[] nonceChar = encNonce.toCharArray(); 117 | 118 | // shaves a bit off vs regex -- for this operation, about 50% savings 119 | StringBuilder nonceSb = new StringBuilder(encNonce.length()); 120 | for (char ar : nonceChar) { 121 | if (ar >= '0' && ar <= '9' || ar >= 'a' && ar <= 'z' || ar >= 'A' && ar <= 'Z') { 122 | nonceSb.append(ar); 123 | } 124 | } 125 | 126 | // prealloc probably saves us 10% on this op sequence 127 | hashBase = new StringBuilder(hashBufferSize); // size of key + nonce + difficult + argon + data + spacers 128 | hashBase.append(this.publicKey).append("-"); 129 | hashBase.append(nonceSb).append("-"); 130 | hashBase.append(this.data).append("-"); 131 | hashBase.append(this.difficultyString); 132 | 133 | rawNonce = nonceSb.toString(); 134 | rawHashBase = hashBase.toString(); 135 | 136 | hashBaseBuffer = rawHashBase.getBytes(); 137 | hashBaseBufferSize = new Size_t(hashBaseBuffer.length); 138 | fullHashBaseBuffer = new byte[hashBaseBuffer.length + encLen.intValue()]; 139 | 140 | System.arraycopy(hashBaseBuffer, 0, fullHashBaseBuffer, 0, hashBaseBuffer.length); 141 | } 142 | 143 | @Override 144 | public void go() { 145 | boolean doLoop = true; 146 | this.hashBegin = System.currentTimeMillis(); 147 | 148 | this.parent.hasherCount.getAndIncrement(); 149 | byte[] byteBase = null; 150 | 151 | MessageDigest sha512 = null; 152 | try { 153 | sha512 = MessageDigest.getInstance("SHA-512"); 154 | } catch (NoSuchAlgorithmException e1) { 155 | System.err.println("Unable to find SHA-512 algorithm! Fatal error."); 156 | e1.printStackTrace(); 157 | active = false; 158 | doLoop = false; 159 | System.exit(1); 160 | } 161 | if (active) { 162 | parent.workerInit(id); 163 | } 164 | 165 | long statCycle = 0l; 166 | long statBegin = 0l; 167 | long statArgonBegin = 0l; 168 | long statArgonEnd = 0l; 169 | long statShaBegin = 0l; 170 | long statShaEnd = 0l; 171 | long statEnd = 0l; 172 | 173 | try { 174 | boolean bound = Miner.PERMIT_AFINITY; 175 | if (Miner.PERMIT_AFINITY) { 176 | BitSet affinity = Affinity.getAffinity(); 177 | if (affinity == null || affinity.isEmpty() || affinity.cardinality() > 1) { // no affinity? 178 | Integer lastChance = AggressiveAffinityThreadFactory.AffineMap.get(Affinity.getThreadId()); 179 | if (lastChance == null || lastChance < 0) { 180 | bound = false; 181 | } 182 | } 183 | } 184 | while (doLoop && active) { 185 | statCycle = System.currentTimeMillis(); 186 | statBegin = System.nanoTime(); 187 | try { 188 | insRandom.nextBytes(salt); // 47 ns 189 | 190 | statArgonBegin = System.nanoTime(); 191 | 192 | int res = argonlib.argon2i_hash_encoded(iterations, memory, parallelism, hashBaseBuffer, hashBaseBufferSize, salt, 193 | saltLen, hashLen, encoded, encLen); // refactor saves like 30,000-200,000 ns per hash // 34.2 ms 194 | // -- 34,200,000 ns 195 | if (res != Argon2Library.ARGON2_OK) { 196 | System.out.println("HASH FAILURE!" + res); 197 | System.out.println(" hashes: " + hashCount); 198 | System.exit(res); 199 | } 200 | statArgonEnd = System.nanoTime(); 201 | 202 | System.arraycopy(encoded, 0, fullHashBaseBuffer, hashBaseBufferSize.intValue(), encLen.intValue()); 203 | // 10-20ns (vs. 1200ns of strings in former StableHasher) 204 | 205 | statShaBegin = System.nanoTime(); 206 | 207 | byteBase = sha512.digest(fullHashBaseBuffer); 208 | for (int i = 0; i < 5; i++) { 209 | byteBase = sha512.digest(byteBase); 210 | } 211 | 212 | statShaEnd = System.nanoTime(); 213 | // shas total 4900-5000ns for all 6 digests, or < 1000ns ea 214 | 215 | StringBuilder duration = new StringBuilder(25); 216 | duration.append(byteBase[10] & 0xFF).append(byteBase[15] & 0xFF).append(byteBase[20] & 0xFF) 217 | .append(byteBase[23] & 0xFF).append(byteBase[31] & 0xFF).append(byteBase[40] & 0xFF) 218 | .append(byteBase[45] & 0xFF).append(byteBase[55] & 0xFF); 219 | 220 | long finalDuration = new BigInteger(duration.toString()).divide(this.difficulty).longValue(); 221 | // 385 ns for duration 222 | 223 | if (finalDuration > 0 && finalDuration <= this.limit) { 224 | 225 | parent.submit(rawNonce, new String(encoded), finalDuration, this.difficulty.longValue(), this.getType(), this.blockHeight, this); 226 | if (finalDuration <= 240) { 227 | finds++; 228 | } else { 229 | shares++; 230 | } 231 | genNonce(); // only gen a new nonce once we exhaust the one we had 232 | } 233 | 234 | hashCount++; 235 | statEnd = System.nanoTime(); 236 | 237 | if (finalDuration < this.bestDL) { 238 | this.bestDL = finalDuration; 239 | } 240 | 241 | this.argonTime += statArgonEnd - statArgonBegin; 242 | this.shaTime += statShaEnd - statShaBegin; 243 | this.nonArgonTime += (statArgonBegin - statBegin) + (statEnd - statArgonEnd); 244 | 245 | } catch (Exception e) { 246 | System.err.println(id + "] This worker failed somehow. Killing it."); 247 | e.printStackTrace(); 248 | doLoop = false; 249 | } 250 | this.loopTime += System.currentTimeMillis() - statCycle; 251 | 252 | if (this.hashCount > this.targetHashCount || this.loopTime > this.maxTime) { 253 | if (!bound) { // no affinity? 254 | if (Miner.PERMIT_AFINITY) { 255 | // make an attempt to grab affinity. 256 | AffinityLock lock = AffinityLock.acquireLock(false); //myid); 257 | if (!lock.isBound()) { 258 | lock = AffinityLock.acquireLock(); 259 | } 260 | if (!lock.isBound()) { 261 | lock = AffinityLock.acquireCore(); 262 | } 263 | if (!lock.isBound()) { 264 | bound = false; 265 | } else { 266 | bound = true; 267 | } 268 | } 269 | } 270 | if (!bound) { 271 | doLoop = false; 272 | } else { 273 | this.hashEnd = System.currentTimeMillis(); 274 | this.hashTime = this.hashEnd - this.hashBegin; 275 | this.hashBegin = System.currentTimeMillis(); 276 | completeSession(); 277 | this.loopTime = 0l; 278 | } 279 | } 280 | } 281 | } catch (Throwable e) { 282 | e.printStackTrace(); 283 | } 284 | this.hashEnd = System.currentTimeMillis(); 285 | this.hashTime = this.hashEnd - this.hashBegin; 286 | this.parent.hasherCount.decrementAndGet(); 287 | } 288 | 289 | public String getType() { 290 | return "Legacy"; 291 | } 292 | 293 | public void kill() { 294 | active = false; 295 | } 296 | } -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/GPUHasher.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_miner; 28 | 29 | import java.math.BigInteger; 30 | import java.security.MessageDigest; 31 | import java.security.NoSuchAlgorithmException; 32 | import java.security.SecureRandom; 33 | import java.util.Base64; 34 | import java.util.Base64.Encoder; 35 | 36 | //import com.programmerdan.arionum.arionum_miner.jna.*; 37 | 38 | /** 39 | * Proof of concept GPU miner. Once I figure out how. 40 | * Someday... 41 | * 42 | * @author ProgrammerDan (Daniel Boston) 43 | * 44 | */ 45 | public class GPUHasher extends Hasher { 46 | 47 | public GPUHasher(Miner parent, String id, long target, long maxTime) { 48 | super(parent, id, target, maxTime); 49 | } 50 | 51 | private SecureRandom random = new SecureRandom(); 52 | private Encoder encoder = Base64.getEncoder(); 53 | private String rawHashBase; 54 | private byte[] nonce = new byte[32]; 55 | private String rawNonce; 56 | 57 | @Override 58 | public void update(BigInteger difficulty, String data, long limit, String publicKey, long blockHeight, 59 | boolean pause, int iters, int mem, int threads) { 60 | super.update(difficulty, data, limit, publicKey, blockHeight, pause, iters, mem, threads); 61 | 62 | genNonce(); 63 | } 64 | 65 | @Override 66 | public void newHeight(long oldH, long newH) {} 67 | 68 | /** 69 | */ 70 | private void genNonce() { 71 | String encNonce = null; 72 | StringBuilder hashBase; 73 | random.nextBytes(nonce); 74 | encNonce = encoder.encodeToString(nonce); 75 | 76 | char[] nonceChar = encNonce.toCharArray(); 77 | 78 | // shaves a bit off vs regex -- for this operation, about 50% savings 79 | StringBuilder nonceSb = new StringBuilder(encNonce.length()); 80 | for (char ar : nonceChar) { 81 | if (ar >= '0' && ar <= '9' || ar >= 'a' && ar <= 'z' || ar >= 'A' && ar <= 'Z') { 82 | nonceSb.append(ar); 83 | } 84 | } 85 | 86 | // prealloc probably saves us 10% on this op sequence 87 | hashBase = new StringBuilder(hashBufferSize); // size of key + nonce + difficult + argon + data + spacers 88 | hashBase.append(this.publicKey).append("-"); 89 | hashBase.append(nonceSb).append("-"); 90 | hashBase.append(this.data).append("-"); 91 | hashBase.append(this.difficultyString); 92 | 93 | rawNonce = nonceSb.toString(); 94 | rawHashBase = hashBase.toString(); 95 | } 96 | 97 | @Override 98 | public void go() { 99 | this.parent.hasherCount.incrementAndGet(); 100 | active = true; 101 | long start = System.currentTimeMillis(); 102 | 103 | StringBuilder hashBase = null; 104 | byte[] byteBase = null; 105 | 106 | String argon = null; 107 | //Argon2 argon2 = Argon2Factory.create(Argon2Types.ARGON2i); 108 | 109 | MessageDigest sha512 = null; 110 | try { 111 | sha512 = MessageDigest.getInstance("SHA-512"); 112 | } catch (NoSuchAlgorithmException e1) { 113 | System.err.println("Unable to find SHA-512 algorithm! Fatal error."); 114 | e1.printStackTrace(); 115 | System.exit(1); 116 | active = false; 117 | } 118 | if (active) { 119 | parent.workerInit(id); 120 | System.out.println(id + "] Spun up EXPERIMENTAL hashing worker in " + (System.currentTimeMillis() - start) + "ms"); 121 | } 122 | 123 | long statCycle = 0l; 124 | long statBegin = 0l; 125 | long statArgonBegin = 0l; 126 | long statArgonEnd = 0l; 127 | long statShaBegin = 0l; 128 | long statShaEnd = 0l; 129 | long statEnd = 0l; 130 | 131 | while (active) { 132 | statCycle = System.currentTimeMillis(); 133 | statBegin = System.nanoTime(); 134 | try { 135 | hashBase = new StringBuilder(this.hashBufferSize); 136 | statArgonBegin = System.nanoTime(); 137 | // this is all TODO: argon = argon2.hash(4, 16384, 4, rawHashBase); 138 | statArgonEnd = System.nanoTime(); 139 | hashBase.append(rawHashBase).append(argon); 140 | 141 | byteBase = hashBase.toString().getBytes(); 142 | statShaBegin = System.nanoTime(); 143 | for (int i = 0; i < 5; i++) { 144 | byteBase = sha512.digest(byteBase); 145 | } 146 | byteBase = sha512.digest(byteBase); 147 | statShaEnd = System.nanoTime(); 148 | 149 | StringBuilder duration = new StringBuilder(25); 150 | duration.append(byteBase[10] & 0xFF).append(byteBase[15] & 0xFF).append(byteBase[20] & 0xFF) 151 | .append(byteBase[23] & 0xFF).append(byteBase[31] & 0xFF).append(byteBase[40] & 0xFF) 152 | .append(byteBase[45] & 0xFF).append(byteBase[55] & 0xFF); 153 | 154 | long finalDuration = new BigInteger(duration.toString()).divide(this.difficulty).longValue(); 155 | if (finalDuration > 0 && finalDuration <= this.limit) { 156 | parent.submit(rawNonce, argon, finalDuration, this.difficulty.longValue(), this.getType(), this.blockHeight, this); 157 | if (finalDuration <= 240) { 158 | finds++; 159 | } else { 160 | shares++; 161 | } 162 | genNonce(); // only gen a new nonce once we exhaust the one we had 163 | } 164 | 165 | hashCount++; 166 | statEnd = System.nanoTime(); 167 | 168 | if (finalDuration < this.bestDL) { // split the difference; if we're not getting movement after a while, just move on 169 | this.bestDL = finalDuration; 170 | } 171 | 172 | 173 | this.argonTime += statArgonEnd - statArgonBegin; 174 | this.shaTime += statShaEnd - statShaBegin; 175 | this.nonArgonTime += (statArgonBegin - statBegin) + (statEnd - statArgonEnd); 176 | } catch (Exception e) { 177 | System.err.println(id + "] This worker failed somehow. Killing it."); 178 | e.printStackTrace(); 179 | active = false; 180 | } 181 | this.loopTime += System.currentTimeMillis() - statCycle; 182 | 183 | if (this.hashCount > this.targetHashCount || this.loopTime > this.maxTime) { 184 | this.active = false; 185 | } 186 | } 187 | System.out.println(id + "] This worker is now inactive."); 188 | this.parent.hasherCount.decrementAndGet(); 189 | } 190 | 191 | public String getType() { 192 | return "GPU"; 193 | } 194 | 195 | public void kill() { 196 | active = false; 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/Hasher.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_miner; 28 | 29 | import java.math.BigInteger; 30 | 31 | /** 32 | * Abstraction layer to allow multiple miner definitions. 33 | * 34 | * @author ProgrammerDan (Daniel Boston) 35 | */ 36 | public abstract class Hasher implements Runnable{ 37 | 38 | protected Miner parent; 39 | 40 | public void run() { 41 | try { 42 | active = true; 43 | go(); 44 | active = false; 45 | } catch (Throwable e) { 46 | System.err.println("Detected thread " + Thread.currentThread().getName() + " death due to error: " + e.getMessage()); 47 | e.printStackTrace(); 48 | 49 | System.err.println("\n\nThis is probably fatal, so exiting now."); 50 | System.exit(1); 51 | } 52 | HasherStats stats = new HasherStats(id, argonTime, shaTime, nonArgonTime, hashTime, hashCount, bestDL, shares, finds, getType()); 53 | parent.workerFinish(stats, this); 54 | } 55 | 56 | /** 57 | * Instead of run, go -- since we now wrap run() into a catch block since our Executors don't support UncaughtExceptions in an intuitive way 58 | */ 59 | public abstract void go(); 60 | 61 | /** 62 | * If some condition exists where this should die, kill it. 63 | */ 64 | public abstract void kill(); 65 | 66 | protected boolean active; 67 | protected String id; 68 | protected long hashCount; 69 | protected long targetHashCount; 70 | protected long hashBegin; 71 | protected long hashEnd; 72 | protected long hashTime; 73 | protected long maxTime; 74 | protected long blockHeight; 75 | 76 | // local copy of data, updated "off thread" 77 | protected BigInteger difficulty; 78 | protected String difficultyString; 79 | protected String data; 80 | protected int hashBufferSize; 81 | protected long limit; 82 | protected String publicKey; 83 | protected String argonString; 84 | protected boolean pause; 85 | protected int iters; 86 | protected int mem; 87 | protected int threads; 88 | 89 | // local stats stores, retrieved "off thread" 90 | protected long bestDL; 91 | protected long shares; 92 | protected long finds; 93 | 94 | /* Timing Stats, current */ 95 | protected long loopTime; 96 | protected long argonTime; 97 | protected long shaTime; 98 | protected long nonArgonTime; 99 | 100 | public Hasher(Miner parent, String id, long target, long maxTime) { 101 | super(); 102 | this.parent = parent; 103 | this.id = id; 104 | this.active = false; 105 | this.hashCount = 0l; 106 | this.targetHashCount = target; 107 | this.maxTime = maxTime; 108 | } 109 | 110 | public void completeSession() { 111 | // emulate shutdown / restart, but on dedi thread so don't. 112 | HasherStats stats = new HasherStats(id, argonTime, shaTime, nonArgonTime, hashTime, hashCount, bestDL, shares, finds, getType()); 113 | argonTime = 0l; 114 | shaTime = 0l; 115 | nonArgonTime = 0l; 116 | hashTime = 0l; 117 | hashCount = 0l; 118 | bestDL = Long.MAX_VALUE; 119 | shares = 0l; 120 | finds = 0l; 121 | long[] sessionUpd = parent.sessionFinish(stats, this); 122 | this.targetHashCount = sessionUpd[0]; 123 | this.maxTime = sessionUpd[1]; 124 | } 125 | 126 | public String getID() { 127 | return this.id; 128 | } 129 | 130 | public long getBestDL() { 131 | return bestDL; 132 | } 133 | 134 | public long getShares() { 135 | return shares; 136 | } 137 | 138 | public long getFinds() { 139 | return finds; 140 | } 141 | 142 | public long getArgonTime() { 143 | return argonTime; 144 | } 145 | 146 | public long getShaTime() { 147 | return shaTime; 148 | } 149 | 150 | public long getNonArgonTime() { 151 | return nonArgonTime; 152 | } 153 | 154 | public long getLoopTime() { 155 | return loopTime; 156 | } 157 | 158 | public long getHashTime() { 159 | return this.hashTime; 160 | } 161 | 162 | public void update(BigInteger difficulty, String data, long limit, String publicKey, long blockHeight, 163 | boolean pause, int iters, int mem, int threads) { 164 | this.difficulty = difficulty; 165 | this.difficultyString = difficulty.toString(); 166 | if (!data.equals(this.data)) { 167 | bestDL = Long.MAX_VALUE; 168 | } 169 | this.data = data; 170 | this.hashBufferSize = 280 + this.data.length(); 171 | this.limit = limit; 172 | this.publicKey = publicKey; 173 | if (blockHeight != this.blockHeight) { 174 | newHeight(this.blockHeight, blockHeight); 175 | } 176 | this.blockHeight = blockHeight; 177 | 178 | boolean doreinit = false; 179 | 180 | if (this.iters != iters || this.mem != mem || this.threads != threads) { 181 | //System.out.println("hasher: reinit to " + mem); 182 | doreinit = true; 183 | } 184 | this.pause = pause; 185 | this.iters = iters; 186 | this.mem = mem; 187 | this.threads = threads; 188 | } 189 | 190 | /** 191 | * Called to let hashers handle new block heights in a flexible fashion. This will allow 192 | * us to accommodate hard forks easily. 193 | * 194 | * @param oldBlockHeight 195 | * @param newBlockHeight 196 | * 197 | */ 198 | public abstract void newHeight(long oldBlockHeight, long newBlockHeight); 199 | 200 | /** 201 | * Lets the hasher return a "type" classification. Should NOT be null. 202 | * @return 203 | */ 204 | public abstract String getType(); 205 | 206 | public long getHashes() { 207 | return this.hashCount; 208 | } 209 | 210 | public boolean isActive() { 211 | return active; 212 | } 213 | 214 | } 215 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/HasherFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_miner; 28 | 29 | /** 30 | * Straightforward factory-esque pattern, tied to optional AdvMod enum. 31 | * 32 | * @author ProgrammerDan (Daniel Boston) 33 | * 34 | */ 35 | public class HasherFactory { 36 | 37 | /* Generate a new hasher */ 38 | public static Hasher createHasher(AdvMode mode, Miner parent, String id, long lifeTime, long maxSession) { 39 | switch(mode) { 40 | case experimental: 41 | return new MappedHasher(parent, id, lifeTime, maxSession); 42 | case standard: 43 | return new SafeMappedHasher(parent, id, lifeTime, maxSession); 44 | case enhanced: 45 | return new SafeMappedHasher(parent, id, lifeTime, maxSession); 46 | case legacy: 47 | return new ExperimentalHasher(parent, id, lifeTime, maxSession); 48 | case gpu: 49 | return new GPUHasher(parent, id, lifeTime, maxSession); 50 | default: 51 | return new MappedHasher(parent, id, lifeTime, maxSession); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/HasherStats.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_miner; 28 | 29 | /** 30 | * Offload container for hasher runtime statistics 31 | * 32 | * @author ProgrammerDan (Daniel Boston) 33 | */ 34 | public class HasherStats { 35 | public long argonTime; 36 | public long shaTime; 37 | public long nonArgonTime; 38 | public long hashes; 39 | public long bestDL; 40 | public long shares; 41 | public long finds; 42 | public long hashTime; 43 | public long scheduledTime; 44 | public String id; 45 | public String type; 46 | 47 | public HasherStats(String id, long argonTime, long shaTime, long nonArgonTime, long hashTime, long hashes, long bestDL, long shares, long finds, String type) { 48 | this.id = id; 49 | this.argonTime = argonTime; 50 | this.shaTime = shaTime; 51 | this.nonArgonTime = nonArgonTime; 52 | this.hashTime = hashTime; 53 | this.hashes = hashes; 54 | this.bestDL = bestDL; 55 | this.shares = shares; 56 | this.finds = finds; 57 | this.type = type; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/MappedHasher.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_miner; 28 | 29 | import java.lang.reflect.Method; 30 | import java.math.BigInteger; 31 | import java.security.MessageDigest; 32 | import java.security.NoSuchAlgorithmException; 33 | import java.security.SecureRandom; 34 | import java.util.Base64; 35 | import java.util.Base64.Encoder; 36 | import java.util.BitSet; 37 | import java.util.LinkedList; 38 | 39 | import com.sun.jna.Memory; 40 | import com.sun.jna.Pointer; 41 | import com.sun.jna.ptr.PointerByReference; 42 | 43 | import net.openhft.affinity.Affinity; 44 | import net.openhft.affinity.AffinityLock; 45 | 46 | import com.programmerdan.arionum.arionum_miner.jna.*; 47 | 48 | /** 49 | * This function means serious business. 50 | * With the Hard Fork in Arionum increasing by nearly two orders of magnitude the per-hash memory requirements, 51 | * the allocation of memory became a serious performance bottleneck for most normal CPU miners. 52 | * This hasher attempts to address that, by generating a pool of massive memory "blocks" that are passed 53 | * to the argon2 function and reused. It is conservative, keeping a static "scratch" pool of available memory 54 | * blocks and passing them to new Hashers as needed, who (should) return them to the scratch pool when they are 55 | * done. New memory blocks are only allocated if the scratch pool is exhausted, but then again, those new 56 | * blocks are then preserved within the pool. 57 | * Initial testing showed about 25% performance gain by removing the bulk of memory alloc and dealloc calls in this 58 | * fashion. 59 | * 60 | * @author ProgrammerDan (Daniel Boston) 61 | * 62 | */ 63 | public class MappedHasher extends Hasher implements Argon2Library.AllocateFunction, Argon2Library.DeallocateFunction { 64 | /* Scratch "pool" of massive memory pages. */ 65 | private static LinkedList scratches = new LinkedList<>(); 66 | private static Memory getScratch() { 67 | Memory mem = scratches.poll(); 68 | if (mem == null) { 69 | return new Memory(524288l*1024l); 70 | } else { 71 | return mem; 72 | } 73 | } 74 | private static void returnScratch(Memory mem) { 75 | scratches.push(mem); 76 | } 77 | 78 | /*Local Hasher vars*/ 79 | private Memory scratch = null; 80 | 81 | private boolean kill = false; 82 | 83 | private final Argon2_Context context; 84 | 85 | private final JnaUint32 iterations = new JnaUint32(1); 86 | private final JnaUint32 memory = new JnaUint32(524288); 87 | private final JnaUint32 parallelism = new JnaUint32(1); 88 | private final JnaUint32 saltLenI = new JnaUint32(16); 89 | private final JnaUint32 hashLenI = new JnaUint32(32); 90 | private final Size_t saltLen = new Size_t(16l); 91 | private final Size_t hashLen = new Size_t(32l); 92 | private final Size_t encLen; 93 | private byte[] encoded; 94 | private final Argon2Library argonlib; 95 | private final Argon2_type argonType = new Argon2_type(1l); 96 | 97 | private final JnaUint32 iterations2 = new JnaUint32(4); 98 | private final JnaUint32 memory2 = new JnaUint32(16384); 99 | private final JnaUint32 parallelism2 = new JnaUint32(4); 100 | private final Size_t encLen2; 101 | private byte[] encoded2; 102 | 103 | private int mode = 1; 104 | 105 | public MappedHasher(Miner parent, String id, long target, long maxTime) { 106 | super(parent, id, target, maxTime); 107 | 108 | // SET UP ARGON FOR DIRECT-TO-JNA-WRAPPER-EXEC 109 | argonlib = Argon2Library.INSTANCE; 110 | context = new Argon2_Context(); 111 | context.outlen = new JnaUint32(32); 112 | context.out = new Memory(32l); 113 | // assigned per hash context.pwdLen 114 | context.saltlen = new JnaUint32(16); 115 | context.secret = null; 116 | context.secretlen = new JnaUint32(0); 117 | context.ad = null; 118 | context.adlen = new JnaUint32(0); 119 | 120 | context.t_cost = iterations; 121 | context.m_cost = memory; 122 | context.lanes = parallelism; 123 | context.threads = parallelism; 124 | context.flags = Argon2Library.ARGON2_DEFAULT_FLAGS; 125 | context.version = new JnaUint32(0x13); 126 | 127 | encLen = argonlib.argon2_encodedlen(iterations, memory, parallelism, saltLenI, hashLenI, 128 | argonType); 129 | 130 | encoded = new byte[encLen.intValue() - 1]; 131 | 132 | encLen2 = argonlib.argon2_encodedlen(iterations2, memory2, parallelism2, saltLenI, hashLenI, 133 | argonType); 134 | encoded2 = new byte[encLen2.intValue() - 1]; 135 | } 136 | 137 | private SecureRandom random = new SecureRandom(); 138 | private Encoder encoder = Base64.getEncoder(); 139 | private String rawHashBase; 140 | private byte[] nonce = new byte[32]; 141 | private byte[] salt = new byte[16]; 142 | private Memory m_salt = new Memory(16l); 143 | private String rawNonce; 144 | private byte[] hashBaseBuffer; 145 | private Memory m_hashBaseBuffer; 146 | private Size_t hashBaseBufferSize; 147 | private byte[] fullHashBaseBuffer; 148 | 149 | @Override 150 | public void update(BigInteger difficulty, String data, long limit, String publicKey, long blockHeight, 151 | boolean pause, int iters, int mem, int threads) { 152 | super.update(difficulty, data, limit, publicKey, blockHeight, pause, iters, mem, threads); 153 | 154 | if (this.mem == 16384) { 155 | mode = 2; 156 | } else { 157 | mode = 1; // assume only the two modes for now. 158 | } 159 | 160 | genNonce(); 161 | } 162 | 163 | @Override 164 | public void newHeight(long oldBlockHeight, long newBlockHeight) { 165 | // no-op, we are locked into 10800 > territory now 166 | } 167 | 168 | private void genNonce() { 169 | String encNonce = null; 170 | StringBuilder hashBase; 171 | random.nextBytes(nonce); 172 | encNonce = encoder.encodeToString(nonce); 173 | 174 | char[] nonceChar = encNonce.toCharArray(); 175 | 176 | // shaves a bit off vs regex -- for this operation, about 50% savings 177 | StringBuilder nonceSb = new StringBuilder(encNonce.length()); 178 | for (char ar : nonceChar) { 179 | if (ar >= '0' && ar <= '9' || ar >= 'a' && ar <= 'z' || ar >= 'A' && ar <= 'Z') { 180 | nonceSb.append(ar); 181 | } 182 | } 183 | 184 | // prealloc probably saves us 10% on this op sequence 185 | hashBase = new StringBuilder(hashBufferSize); // size of key + nonce + difficult + argon + data + spacers 186 | hashBase.append(this.publicKey).append("-"); 187 | hashBase.append(nonceSb).append("-"); 188 | hashBase.append(this.data).append("-"); 189 | hashBase.append(this.difficultyString); 190 | 191 | rawNonce = nonceSb.toString(); 192 | rawHashBase = hashBase.toString(); 193 | 194 | hashBaseBuffer = rawHashBase.getBytes(); 195 | hashBaseBufferSize = new Size_t(hashBaseBuffer.length); 196 | m_hashBaseBuffer = new Memory(hashBaseBuffer.length); 197 | 198 | if (mode == 1) { 199 | fullHashBaseBuffer = new byte[hashBaseBuffer.length + encLen.intValue() - 1]; 200 | } else if (mode == 2) { 201 | fullHashBaseBuffer = new byte[hashBaseBuffer.length + encLen2.intValue() - 1]; 202 | } 203 | 204 | m_hashBaseBuffer.write(0, hashBaseBuffer, 0, hashBaseBuffer.length); 205 | System.arraycopy(hashBaseBuffer, 0, fullHashBaseBuffer, 0, hashBaseBuffer.length); 206 | } 207 | 208 | @Override 209 | public void go() { 210 | try { 211 | scratch = MappedHasher.getScratch(); 212 | } catch (OutOfMemoryError oome) { 213 | System.err.println("Please reduce the number of requested hashing workers. Your system lacks sufficient memory to run this many!"); 214 | System.err.println("Regrettably, this is a fatal error."); 215 | oome.printStackTrace(); 216 | active = false; 217 | System.exit(1); 218 | } 219 | context.allocate_cbk = this; 220 | context.free_cbk = this; 221 | 222 | boolean doLoop = true; 223 | this.hashBegin = System.currentTimeMillis(); 224 | 225 | this.parent.hasherCount.getAndIncrement(); 226 | byte[] byteBase = null; 227 | 228 | MessageDigest sha512 = null; 229 | try { 230 | sha512 = MessageDigest.getInstance("SHA-512"); 231 | } catch (NoSuchAlgorithmException e1) { 232 | System.err.println("Unable to find SHA-512 algorithm! Fatal error."); 233 | e1.printStackTrace(); 234 | active = false; 235 | doLoop = false; 236 | System.exit(1); 237 | } 238 | if (active) { 239 | parent.workerInit(id); 240 | } 241 | 242 | long statCycle = 0l; 243 | long statBegin = 0l; 244 | long statArgonBegin = 0l; 245 | long statArgonEnd = 0l; 246 | long statShaBegin = 0l; 247 | long statShaEnd = 0l; 248 | long statEnd = 0l; 249 | 250 | try { 251 | boolean bound = Miner.PERMIT_AFINITY; 252 | if (Miner.PERMIT_AFINITY) { 253 | BitSet affinity = Affinity.getAffinity(); 254 | if (affinity == null || affinity.isEmpty() || affinity.cardinality() > 1) { // no affinity? 255 | Integer lastChance = AggressiveAffinityThreadFactory.AffineMap.get(Affinity.getThreadId()); 256 | if (lastChance == null || lastChance < 0) { 257 | bound = false; 258 | } 259 | } 260 | } 261 | while (doLoop && active) { 262 | if (this.pause) { 263 | try { 264 | Thread.sleep(50); 265 | } catch (InterruptedException e) { 266 | // ok, moving on. 267 | } 268 | continue; 269 | } 270 | statCycle = System.currentTimeMillis(); 271 | statBegin = System.nanoTime(); 272 | try { 273 | random.nextBytes(salt); 274 | m_salt.write(0, salt, 0, 16); 275 | 276 | statArgonBegin = System.nanoTime(); 277 | // set argon params in context.. 278 | 279 | int smode = mode; 280 | if (mode == 2) { 281 | context.t_cost = iterations2; 282 | context.m_cost = memory2; 283 | context.lanes = parallelism2; 284 | context.threads = parallelism2; 285 | } else { 286 | context.t_cost = iterations; 287 | context.m_cost = memory; 288 | context.lanes = parallelism; 289 | context.threads = parallelism; 290 | } 291 | context.out = new Memory(32l); 292 | context.salt = m_salt; 293 | context.pwdlen = new JnaUint32(hashBaseBuffer.length); 294 | m_hashBaseBuffer.write(0, hashBaseBuffer, 0, hashBaseBuffer.length); 295 | context.pwd = m_hashBaseBuffer; 296 | int res = argonlib.argon2_ctx(context, argonType); 297 | if (res != Argon2Library.ARGON2_OK) { 298 | System.out.println("HASH FAILURE!" + res); 299 | System.out.println(" hashes: " + hashCount); 300 | System.exit(res); 301 | } 302 | int res2 = 0; 303 | long finalDuration = 0; 304 | if (smode == mode) { // check if mode changed 305 | if (mode == 1) { 306 | res2 = argonlib.encode_ctx(encoded, encLen, context, argonType); 307 | } else if (mode == 2) { 308 | res2 = argonlib.encode_ctx(encoded2, encLen2, context, argonType); 309 | } 310 | if (res2 != Argon2Library.ARGON2_OK) { 311 | System.out.println("ENCODE FAILURE! " + res2); 312 | } 313 | statArgonEnd = System.nanoTime(); 314 | 315 | if (mode == 1) { 316 | if (encoded[encoded.length - 1] == 0) { 317 | System.out.print("Encoded length failure."); 318 | } 319 | 320 | System.arraycopy(encoded, 0, fullHashBaseBuffer, hashBaseBufferSize.intValue(), encLen.intValue() - 1); 321 | } else if (mode == 2) { 322 | if (encoded2[encoded2.length - 1] == 0) { 323 | System.out.print("Encoded length failure."); 324 | } 325 | 326 | System.arraycopy(encoded2, 0, fullHashBaseBuffer, hashBaseBufferSize.intValue(), encLen2.intValue() - 1); 327 | } 328 | 329 | // 10-20ns (vs. 1200ns of strings in former StableHasher) 330 | 331 | statShaBegin = System.nanoTime(); 332 | 333 | byteBase = sha512.digest(fullHashBaseBuffer); 334 | for (int i = 0; i < 5; i++) { 335 | byteBase = sha512.digest(byteBase); 336 | } 337 | 338 | statShaEnd = System.nanoTime(); 339 | // shas total 4900-5000ns for all 6 digests, or < 1000ns ea 340 | 341 | StringBuilder duration = new StringBuilder(25); 342 | duration.append(byteBase[10] & 0xFF).append(byteBase[15] & 0xFF).append(byteBase[20] & 0xFF) 343 | .append(byteBase[23] & 0xFF).append(byteBase[31] & 0xFF).append(byteBase[40] & 0xFF) 344 | .append(byteBase[45] & 0xFF).append(byteBase[55] & 0xFF); 345 | 346 | finalDuration = new BigInteger(duration.toString()).divide(this.difficulty).longValue(); 347 | // 385 ns for duration 348 | 349 | if (finalDuration > 0 && finalDuration <= this.limit) { 350 | 351 | if (mode == 1 ) { 352 | // why trim? the raw encoded has a trailing \x00 null char. Trim will remove it, same as we do in the arraycopy by doing encLen - 1. 353 | parent.submit(rawNonce, new String(encoded).trim(), finalDuration, this.difficulty.longValue(), this.getType(), this.blockHeight, this); 354 | } else if (mode == 2) { 355 | parent.submit(rawNonce, new String(encoded2).trim(), finalDuration, this.difficulty.longValue(), this.getType(), this.blockHeight, this); 356 | } 357 | 358 | if (finalDuration <= 240) { 359 | finds++; 360 | } else { 361 | shares++; 362 | } 363 | genNonce(); // only gen a new nonce once we exhaust the one we had 364 | } 365 | } 366 | 367 | hashCount++; 368 | statEnd = System.nanoTime(); 369 | 370 | if (finalDuration < this.bestDL) { 371 | this.bestDL = finalDuration; 372 | } 373 | 374 | this.argonTime += statArgonEnd - statArgonBegin; 375 | this.shaTime += statShaEnd - statShaBegin; 376 | this.nonArgonTime += (statArgonBegin - statBegin) + (statEnd - statArgonEnd); 377 | 378 | } catch (Exception e) { 379 | System.err.println(id + "] This worker failed somehow. Killing it."); 380 | e.printStackTrace(); 381 | System.err.println("Please report this to ProgrammerDan"); 382 | doLoop = false; 383 | } 384 | this.loopTime += System.currentTimeMillis() - statCycle; 385 | 386 | if (this.hashCount > this.targetHashCount || this.loopTime > this.maxTime) { 387 | if (!bound) { // no affinity? 388 | if (Miner.PERMIT_AFINITY) { 389 | // make an attempt to grab affinity. 390 | AffinityLock lock = AffinityLock.acquireLock(false); //myid); 391 | if (!lock.isBound()) { 392 | lock = AffinityLock.acquireLock(); 393 | } 394 | if (!lock.isBound()) { 395 | lock = AffinityLock.acquireCore(); 396 | } 397 | if (!lock.isBound()) { 398 | bound = false; 399 | } else { 400 | bound = true; 401 | } 402 | } 403 | } 404 | if (!bound) { 405 | //System.out.println("Ending worker " + this.id); 406 | doLoop = false; 407 | } else { 408 | //System.out.println("Ending a session for worker " + this.id); 409 | this.hashEnd = System.currentTimeMillis(); 410 | this.hashTime = this.hashEnd - this.hashBegin; 411 | this.hashBegin = System.currentTimeMillis(); 412 | completeSession(); 413 | this.loopTime = 0l; 414 | } 415 | } 416 | } 417 | } catch (Throwable e) { 418 | e.printStackTrace(); 419 | } 420 | 421 | if (kill) { 422 | scratch.clear(); // we don't return it. Let's clear it, and let it be GC'd. The Scratch will gen a new one. 423 | try { 424 | Method dispose = scratch.getClass().getDeclaredMethod("dispose"); 425 | dispose.setAccessible(true); 426 | dispose.invoke(scratch); 427 | } catch (Throwable t) { 428 | System.err.println("After a potential memory failure was identified, our attempt to release this memory buffer has failed."); 429 | t.printStackTrace(); 430 | } 431 | scratch = null; 432 | Memory.purge(); 433 | } else { 434 | MappedHasher.returnScratch(scratch); 435 | } 436 | this.hashEnd = System.currentTimeMillis(); 437 | this.hashTime = this.hashEnd - this.hashBegin; 438 | this.parent.hasherCount.decrementAndGet(); 439 | } 440 | 441 | // allocate 442 | public int invoke(PointerByReference memory, Size_t byte_to_allocate) { 443 | 444 | if (byte_to_allocate.intValue() > this.scratch.size()) { 445 | return -22; // memory allocation error 446 | } 447 | 448 | memory.setValue(this.scratch); 449 | 450 | return 0; 451 | } 452 | 453 | // no-op deallcate, wipe? 454 | public void invoke(Pointer memory, Size_t byte_to_allocate) { 455 | // we ignore this, since we manage memory on our own. 456 | } 457 | 458 | public String getType() { 459 | return "CPU"; 460 | } 461 | 462 | public void kill() { 463 | kill = true; 464 | active = false; 465 | } 466 | } 467 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/MinerType.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_miner; 28 | 29 | public enum MinerType { 30 | solo, 31 | pool, 32 | benchmark, 33 | test 34 | } 35 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/Profile.java: -------------------------------------------------------------------------------- 1 | package com.programmerdan.arionum.arionum_miner; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * Lightweight tracking profile to keep track of how well a particular configuration did 7 | * 8 | * @author ProgrammerDan 9 | * 10 | */ 11 | public class Profile implements Comparable { 12 | AdvMode[] workerTypes; 13 | long hashes; 14 | long sampleBegin; 15 | long samplePlannedEnd; 16 | long sampleTime; 17 | double rate; 18 | double TiC; 19 | 20 | public Profile(int workers) { 21 | workerTypes = new AdvMode[workers]; 22 | hashes = 0l; //new long[workers]; 23 | sampleTime = 0; 24 | rate = 0.0d; 25 | this.sampleBegin = 0l; 26 | this.samplePlannedEnd = 0l; 27 | } 28 | 29 | public Profile(AdvMode ...workers) { 30 | workerTypes = workers; 31 | hashes = 0l;//new long[ workers.length ]; 32 | sampleTime = 0; 33 | rate = 0.0d; 34 | this.sampleBegin = 0; 35 | this.samplePlannedEnd = 0; 36 | } 37 | 38 | public void begin() { 39 | long now = System.currentTimeMillis(); 40 | this.sampleBegin = now + Miner.INIT_DELAY; 41 | this.samplePlannedEnd = this.sampleBegin + Miner.TEST_PERIOD; 42 | } 43 | 44 | public Status getStatus() { 45 | if (this.sampleBegin == 0) { 46 | return Status.QUEUED; 47 | } 48 | long now = System.currentTimeMillis(); 49 | if (now < this.sampleBegin) { 50 | return Status.WAITING; 51 | } else if (now > this.samplePlannedEnd) { 52 | return Status.DONE; 53 | } else { 54 | return Status.PROFILING; 55 | } 56 | } 57 | 58 | public AdvMode[] getWorkers() { 59 | return workerTypes; 60 | } 61 | 62 | public void register(int idx, AdvMode worker) { 63 | this.workerTypes[idx] = worker; 64 | } 65 | 66 | public void update(long hashes, long time) { 67 | this.hashes += hashes; //[idx] = hashes; 68 | if (time > this.sampleTime) 69 | this.sampleTime = time; 70 | } 71 | 72 | public double getHashSec() { 73 | return getRawRate() / 1000d; 74 | } 75 | public double getRawRate() { 76 | /*long accumulate = 0l; 77 | for (long hash : hashes) { 78 | accumulate += hash; 79 | }*/ 80 | if (this.sampleTime > 0 ) { 81 | rate = (double) hashes / (double) this.sampleTime; //accumulate / this.sampleTime; 82 | } else { 83 | rate = 0d; 84 | } 85 | return rate; 86 | } 87 | 88 | public long getHashes() { 89 | /*long accumulate = 0l; 90 | for (long hash : hashes) { 91 | accumulate += hash; 92 | } 93 | return accumulate;*/ 94 | return hashes; 95 | } 96 | 97 | @Override 98 | public boolean equals(Object o) { 99 | if (o instanceof Profile) { 100 | Profile p = (Profile) o; 101 | return p == this || (Arrays.deepEquals(p.workerTypes, this.workerTypes)); 102 | } 103 | 104 | return false; 105 | } 106 | 107 | @Override 108 | public int compareTo(Profile o) { 109 | return Double.compare(this.getRawRate(), o.getRawRate()); 110 | } 111 | 112 | enum Status { 113 | QUEUED, 114 | WAITING, 115 | PROFILING, 116 | DONE 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/Report.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_miner; 28 | 29 | /** 30 | * A run statistics package 31 | * 32 | * @author ProgrammerDan (Daniel Boston) 33 | * 34 | */ 35 | public class Report { 36 | /** 37 | * When report was run 38 | */ 39 | long reportTime = System.currentTimeMillis(); 40 | 41 | /** 42 | * Number of active streams/threads 43 | */ 44 | long streams; 45 | 46 | /** 47 | * Number of microtasks run 48 | */ 49 | long runs; 50 | 51 | /** 52 | * Number of hashes 53 | */ 54 | long hashes; 55 | 56 | /** 57 | * Hashes per run 58 | */ 59 | double hashPerRun; 60 | 61 | double curHashPerSecond; 62 | double curTimeInCore; 63 | double curWaitLoss; 64 | 65 | double shaEff; 66 | double argonEff; 67 | 68 | long totalTime; 69 | long argonTime; 70 | long nonArgontime; 71 | long shaTime; 72 | long shares; 73 | long finds; 74 | long rejects; 75 | 76 | } 77 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/SafeMappedHasher.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_miner; 28 | 29 | import java.lang.reflect.Method; 30 | import java.math.BigInteger; 31 | import java.security.MessageDigest; 32 | import java.security.NoSuchAlgorithmException; 33 | import java.security.SecureRandom; 34 | import java.util.Base64; 35 | import java.util.Base64.Encoder; 36 | import java.util.Random; 37 | import java.util.BitSet; 38 | import java.util.LinkedList; 39 | 40 | import com.sun.jna.Memory; 41 | import com.sun.jna.Pointer; 42 | import com.sun.jna.ptr.PointerByReference; 43 | 44 | import net.openhft.affinity.Affinity; 45 | import net.openhft.affinity.AffinityLock; 46 | 47 | import com.programmerdan.arionum.arionum_miner.jna.*; 48 | 49 | /** 50 | * This function means serious business. 51 | * With the Hard Fork in Arionum increasing by nearly two orders of magnitude the per-hash memory requirements, 52 | * the allocation of memory became a serious performance bottleneck for most normal CPU miners. 53 | * This hasher attempts to address that, by generating a pool of massive memory "blocks" that are passed 54 | * to the argon2 function and reused. It is conservative, keeping a static "scratch" pool of available memory 55 | * blocks and passing them to new Hashers as needed, who (should) return them to the scratch pool when they are 56 | * done. New memory blocks are only allocated if the scratch pool is exhausted, but then again, those new 57 | * blocks are then preserved within the pool. 58 | * Initial testing showed about 25% performance gain by removing the bulk of memory alloc and dealloc calls in this 59 | * fashion. 60 | * 61 | * This version uses a PHP-esque "safe" salt, for easier verification and to avoid verification problems against the 62 | * reference implementation. As a consequence of the loss of byte space (2/3 reduction possibly) in the salt, I've added 63 | * in a hopefully clever NONCE rotation scheme, that will mutate the nonce along with the salt. 64 | * 65 | * @author ProgrammerDan (Daniel Boston) 66 | * 67 | */ 68 | public class SafeMappedHasher extends Hasher implements Argon2Library.AllocateFunction, Argon2Library.DeallocateFunction { 69 | /* Scratch "pool" of massive memory pages. */ 70 | private static LinkedList scratches = new LinkedList<>(); 71 | private static Memory getScratch() { 72 | Memory mem = scratches.poll(); 73 | if (mem == null) { 74 | return new Memory(524288l*1024l); 75 | } else { 76 | return mem; 77 | } 78 | } 79 | private static void returnScratch(Memory mem) { 80 | scratches.push(mem); 81 | } 82 | 83 | /*Local Hasher vars*/ 84 | private Memory scratch = null; 85 | 86 | private boolean kill = false; 87 | 88 | private final Argon2_Context context; 89 | 90 | private final JnaUint32 iterations = new JnaUint32(1); 91 | private final JnaUint32 memory = new JnaUint32(524288); 92 | private final JnaUint32 parallelism = new JnaUint32(1); 93 | private final JnaUint32 saltLenI = new JnaUint32(16); 94 | private final JnaUint32 hashLenI = new JnaUint32(32); 95 | private final Size_t saltLen = new Size_t(16l); 96 | private final Size_t hashLen = new Size_t(32l); 97 | private final Size_t encLen; 98 | private byte[] encoded; 99 | private final Argon2Library argonlib; 100 | private final Argon2_type argonType = new Argon2_type(1l); 101 | 102 | private final JnaUint32 iterations2 = new JnaUint32(4); 103 | private final JnaUint32 memory2 = new JnaUint32(16384); 104 | private final JnaUint32 parallelism2 = new JnaUint32(4); 105 | private final Size_t encLen2; 106 | private byte[] encoded2; 107 | 108 | private int mode = 1; 109 | 110 | public SafeMappedHasher(Miner parent, String id, long target, long maxTime) { 111 | super(parent, id, target, maxTime); 112 | 113 | // SET UP ARGON FOR DIRECT-TO-JNA-WRAPPER-EXEC 114 | argonlib = Argon2Library.INSTANCE; 115 | context = new Argon2_Context(); 116 | context.outlen = new JnaUint32(32); 117 | context.out = new Memory(32l); 118 | // assigned per hash context.pwdLen 119 | context.saltlen = new JnaUint32(16); 120 | context.secret = null; 121 | context.secretlen = new JnaUint32(0); 122 | context.ad = null; 123 | context.adlen = new JnaUint32(0); 124 | 125 | context.t_cost = iterations; 126 | context.m_cost = memory; 127 | context.lanes = parallelism; 128 | context.threads = parallelism; 129 | context.flags = Argon2Library.ARGON2_DEFAULT_FLAGS; 130 | context.version = new JnaUint32(0x13); 131 | 132 | encLen = argonlib.argon2_encodedlen(iterations, memory, parallelism, saltLenI, hashLenI, 133 | argonType); 134 | encoded = new byte[encLen.intValue() - 1]; 135 | 136 | encLen2 = argonlib.argon2_encodedlen(iterations2, memory2, parallelism2, saltLenI, hashLenI, 137 | argonType); 138 | encoded2 = new byte[encLen2.intValue() - 1]; 139 | } 140 | 141 | private SecureRandom random = new SecureRandom(); 142 | private Random insRandom = new Random(random.nextLong()); 143 | private Encoder encoder = Base64.getEncoder(); 144 | private String rawHashBase; 145 | private byte[] nonce = new byte[32]; 146 | private byte[] salt = new byte[16]; 147 | private Memory m_salt = new Memory(16l); 148 | private String rawNonce; 149 | private int rawNonceOffset; 150 | private int rawNonceLen = 42; 151 | private byte[] hashBaseBuffer; 152 | private Memory m_hashBaseBuffer; 153 | private Size_t hashBaseBufferSize; 154 | private byte[] fullHashBaseBuffer; 155 | 156 | private static final char[] safeEncode = { 157 | 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', 158 | 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', 159 | '0','1','2','3','4','5','6','7','8','9','.','/'}; 160 | 161 | @Override 162 | public void update(BigInteger difficulty, String data, long limit, String publicKey, long blockHeight, 163 | boolean pause, int iters, int mem, int threads) { 164 | super.update(difficulty, data, limit, publicKey, blockHeight, pause, iters, mem, threads); 165 | 166 | if (this.mem == 16384) { 167 | mode = 2; 168 | } else { 169 | mode = 1; // assume only the two modes for now. 170 | } 171 | 172 | genNonce(); 173 | } 174 | 175 | @Override 176 | public void newHeight(long oldBlockHeight, long newBlockHeight) { 177 | // no-op, we are locked into 10800 > territory now 178 | } 179 | 180 | private byte[] safeBase64Random(int bytes) { 181 | char[] buffer = new char[bytes]; 182 | for (int i = 0; i < bytes; i++) { 183 | buffer[i] = safeEncode[random.nextInt(64)]; 184 | } 185 | return String.valueOf(buffer).getBytes(); 186 | } 187 | 188 | private byte[] safeBase64Random(byte[] buffer) { 189 | for (int i = 0; i < buffer.length;) { 190 | byte[] buff = Character.valueOf(safeEncode[random.nextInt(64)]).toString().getBytes(); 191 | buffer[i++] = buff[0]; 192 | if (buff.length>1) buffer[i++] = buff[1]; 193 | if (buff.length>2) buffer[i++] = buff[2]; 194 | if (buff.length>3) buffer[i++] = buff[3]; 195 | } 196 | return buffer; 197 | } 198 | 199 | private String safeAlphaNumericRandom(int bytes) { 200 | char[] buffer = new char[bytes]; 201 | for (int i = 0; i < bytes; i++) { 202 | buffer[i] = safeEncode[random.nextInt(62)]; 203 | } 204 | return new String(buffer); 205 | } 206 | 207 | 208 | private void genNonce() { 209 | StringBuilder hashBase; 210 | 211 | rawNonce = safeAlphaNumericRandom(rawNonceLen); 212 | 213 | // prealloc probably saves us 10% on this op sequence 214 | hashBase = new StringBuilder(hashBufferSize); // size of key + nonce + difficult + argon + data + spacers 215 | hashBase.append(this.publicKey).append("-"); 216 | rawNonceOffset = hashBase.length(); 217 | hashBase.append(rawNonce).append("-"); 218 | hashBase.append(this.data).append("-"); 219 | hashBase.append(this.difficultyString); 220 | 221 | rawHashBase = hashBase.toString(); 222 | 223 | hashBaseBuffer = rawHashBase.getBytes(); 224 | hashBaseBufferSize = new Size_t(hashBaseBuffer.length); 225 | m_hashBaseBuffer = new Memory(hashBaseBuffer.length); 226 | 227 | if (mode == 1) { 228 | fullHashBaseBuffer = new byte[hashBaseBuffer.length + encLen.intValue() - 1]; 229 | } else if (mode == 2) { 230 | fullHashBaseBuffer = new byte[hashBaseBuffer.length + encLen2.intValue() - 1]; 231 | } 232 | 233 | m_hashBaseBuffer.write(0, hashBaseBuffer, 0, hashBaseBuffer.length); 234 | System.arraycopy(hashBaseBuffer, 0, fullHashBaseBuffer, 0, hashBaseBuffer.length); 235 | } 236 | 237 | /* Replace nonce in-place instead of regenerating the entirety. */ 238 | private void regenNonce() { 239 | rawNonce = safeAlphaNumericRandom(rawNonceLen); 240 | byte[] rawNonceBytes = rawNonce.getBytes(); 241 | m_hashBaseBuffer.write(rawNonceOffset, rawNonceBytes, 0, rawNonceBytes.length); 242 | System.arraycopy(rawNonceBytes, 0, hashBaseBuffer, rawNonceOffset, rawNonceBytes.length); 243 | System.arraycopy(rawNonceBytes, 0, fullHashBaseBuffer, rawNonceOffset, rawNonceBytes.length); 244 | } 245 | 246 | @Override 247 | public void go() { 248 | try { 249 | scratch = SafeMappedHasher.getScratch(); 250 | } catch (OutOfMemoryError oome) { 251 | System.err.println("Please reduce the number of requested hashing workers. Your system lacks sufficient memory to run this many!"); 252 | //System.err.println("Regrettably, this is a fatal error."); 253 | //oome.printStackTrace(); 254 | active = false; 255 | //System.exit(1); 256 | return; 257 | } 258 | context.allocate_cbk = this; 259 | context.free_cbk = this; 260 | 261 | boolean doLoop = true; 262 | this.hashBegin = System.currentTimeMillis(); 263 | 264 | this.parent.hasherCount.getAndIncrement(); 265 | byte[] byteBase = null; 266 | 267 | MessageDigest sha512 = null; 268 | try { 269 | sha512 = MessageDigest.getInstance("SHA-512"); 270 | } catch (NoSuchAlgorithmException e1) { 271 | System.err.println("Unable to find SHA-512 algorithm! Fatal error."); 272 | e1.printStackTrace(); 273 | active = false; 274 | doLoop = false; 275 | System.exit(1); 276 | } 277 | if (active) { 278 | parent.workerInit(id); 279 | } 280 | 281 | long statCycle = 0l; 282 | long statBegin = 0l; 283 | long statArgonBegin = 0l; 284 | long statArgonEnd = 0l; 285 | long statShaBegin = 0l; 286 | long statShaEnd = 0l; 287 | long statEnd = 0l; 288 | 289 | try { 290 | boolean bound = Miner.PERMIT_AFINITY; 291 | if (Miner.PERMIT_AFINITY && Miner.CHECK_BIND) { // for some systems, this doesn't work, so we don't check. 292 | int activeCpu = Affinity.getCpu(); 293 | BitSet affinity = Affinity.getAffinity(); 294 | if (affinity == null || affinity.isEmpty() || affinity.cardinality() > 1) { // no affinity? 295 | Integer lastChance = AggressiveAffinityThreadFactory.AffineMap.get(Affinity.getThreadId()); 296 | if (lastChance == null || lastChance < 0) { 297 | bound = false; 298 | } else { // see if lastChance equals actual CPU binding 299 | if (!lastChance.equals(activeCpu)) { 300 | // try to alter! 301 | AffinityLock.acquireLock(lastChance.intValue()); 302 | System.out.println("We had locked on to " + lastChance.intValue() + " but lost it and are running on " + activeCpu); 303 | } 304 | } 305 | } else { // see if BitSet affinity equals actual CPU binding 306 | //BigInteger singleAffine = new BigInteger(affinity.toByteArray()); 307 | //if (singleAffine.intValue() != activeCpu) { 308 | if (affinity.nextSetBit(0) != activeCpu) { 309 | // try to alter! 310 | AffinityLock.acquireLock(affinity.nextSetBit(0)); 311 | System.out.println("We had locked on to " + affinity.nextSetBit(0) + " but lost it and are running on " + activeCpu); 312 | } 313 | } 314 | } 315 | while (doLoop && active) { 316 | if (this.pause) { 317 | try { 318 | Thread.sleep(50); 319 | } catch (InterruptedException e) { 320 | // ok, moving on. 321 | } 322 | continue; 323 | } 324 | statCycle = System.currentTimeMillis(); 325 | statBegin = System.nanoTime(); 326 | try { 327 | regenNonce(); 328 | 329 | this.safeBase64Random(salt); //random.nextBytes(salt); 330 | m_salt.write(0, salt, 0, 16); 331 | 332 | /*System.out.println(String.format("%s\n%s\n%s\n", new String(hashBaseBuffer), 333 | new String(fullHashBaseBuffer), encoder.encodeToString(salt))); 334 | Thread.sleep(500l);*/ 335 | 336 | statArgonBegin = System.nanoTime(); 337 | 338 | int smode = mode; 339 | if (mode == 2) { 340 | context.t_cost = iterations2; 341 | context.m_cost = memory2; 342 | context.lanes = parallelism2; 343 | context.threads = parallelism2; 344 | } else { 345 | context.t_cost = iterations; 346 | context.m_cost = memory; 347 | context.lanes = parallelism; 348 | context.threads = parallelism; 349 | } 350 | 351 | // set argon params in context.. 352 | context.out = new Memory(32l); 353 | context.salt = m_salt; 354 | context.pwdlen = new JnaUint32(hashBaseBuffer.length); 355 | m_hashBaseBuffer.write(0, hashBaseBuffer, 0, hashBaseBuffer.length); 356 | context.pwd = m_hashBaseBuffer; 357 | int res = argonlib.argon2_ctx(context, argonType); 358 | if (res != Argon2Library.ARGON2_OK) { 359 | System.out.println("HASH FAILURE!" + res); 360 | System.out.println(" hashes: " + hashCount); 361 | System.exit(res); 362 | } 363 | int res2 = 0; 364 | long finalDuration = 0; 365 | if (smode == mode) { // check if mode changed 366 | if (mode == 1) { 367 | res2 = argonlib.encode_ctx(encoded, encLen, context, argonType); 368 | } else if (mode == 2) { 369 | res2 = argonlib.encode_ctx(encoded2, encLen2, context, argonType); 370 | } 371 | if (res2 != Argon2Library.ARGON2_OK) { 372 | System.out.println("ENCODE FAILURE! " + res2); 373 | } 374 | statArgonEnd = System.nanoTime(); 375 | 376 | if (mode == 1) { 377 | if (encoded[encoded.length - 1] == 0) { 378 | System.out.print("Encoded length failure."); 379 | } 380 | 381 | System.arraycopy(encoded, 0, fullHashBaseBuffer, hashBaseBufferSize.intValue(), encLen.intValue() - 1); 382 | } else if (mode == 2) { 383 | if (encoded2[encoded2.length - 1] == 0) { 384 | System.out.print("Encoded length failure."); 385 | } 386 | 387 | System.arraycopy(encoded2, 0, fullHashBaseBuffer, hashBaseBufferSize.intValue(), encLen2.intValue() - 1); 388 | } 389 | 390 | // 10-20ns (vs. 1200ns of strings in former StableHasher) 391 | 392 | statShaBegin = System.nanoTime(); 393 | 394 | byteBase = sha512.digest(fullHashBaseBuffer); 395 | for (int i = 0; i < 5; i++) { 396 | byteBase = sha512.digest(byteBase); 397 | } 398 | 399 | statShaEnd = System.nanoTime(); 400 | // shas total 4900-5000ns for all 6 digests, or < 1000ns ea 401 | 402 | StringBuilder duration = new StringBuilder(25); 403 | duration.append(byteBase[10] & 0xFF).append(byteBase[15] & 0xFF).append(byteBase[20] & 0xFF) 404 | .append(byteBase[23] & 0xFF).append(byteBase[31] & 0xFF).append(byteBase[40] & 0xFF) 405 | .append(byteBase[45] & 0xFF).append(byteBase[55] & 0xFF); 406 | 407 | finalDuration = new BigInteger(duration.toString()).divide(this.difficulty).longValue(); 408 | // 385 ns for duration 409 | 410 | if (finalDuration > 0 && finalDuration <= this.limit) { 411 | 412 | if (mode == 1 ) { 413 | // why trim? the raw encoded has a trailing \x00 null char. Trim will remove it, same as we do in the arraycopy by doing encLen - 1. 414 | parent.submit(rawNonce, new String(encoded).trim(), finalDuration, this.difficulty.longValue(), this.getType(), this.blockHeight, this); 415 | } else if (mode == 2) { 416 | parent.submit(rawNonce, new String(encoded2).trim(), finalDuration, this.difficulty.longValue(), this.getType(), this.blockHeight, this); 417 | } 418 | 419 | if (finalDuration <= 240) { 420 | finds++; 421 | } else { 422 | shares++; 423 | } 424 | //genNonce(); // only gen a new nonce once we exhaust the one we had 425 | } 426 | } 427 | 428 | hashCount++; 429 | statEnd = System.nanoTime(); 430 | 431 | if (finalDuration < this.bestDL) { 432 | this.bestDL = finalDuration; 433 | } 434 | 435 | this.argonTime += statArgonEnd - statArgonBegin; 436 | this.shaTime += statShaEnd - statShaBegin; 437 | this.nonArgonTime += (statArgonBegin - statBegin) + (statEnd - statArgonEnd); 438 | 439 | /*System.out.print(String.format("- r%d a%d s%d -", 440 | (statArgonBegin - statBegin), 441 | (statArgonEnd - statArgonBegin), 442 | (statShaEnd - statShaBegin)));*/ 443 | 444 | } catch (Exception e) { 445 | System.err.println(id + "] This worker failed somehow. Killing it."); 446 | e.printStackTrace(); 447 | System.err.println("Please report this to ProgrammerDan"); 448 | doLoop = false; 449 | kill = true; 450 | } 451 | this.loopTime += System.currentTimeMillis() - statCycle; 452 | 453 | if (this.hashCount > this.targetHashCount || this.loopTime > this.maxTime) { 454 | if (Miner.PERMIT_AFINITY && Miner.CHECK_BIND) { // for some systems, this doesn't work, so we don't check. 455 | int activeCpu = Affinity.getCpu(); 456 | BitSet affinity = Affinity.getAffinity(); 457 | if (affinity == null || affinity.isEmpty() || affinity.cardinality() > 1) { // no affinity? 458 | Integer lastChance = AggressiveAffinityThreadFactory.AffineMap.get(Affinity.getThreadId()); 459 | if (lastChance == null || lastChance < 0) { 460 | bound = false; 461 | } else { // see if lastChance equals actual CPU binding 462 | if (!lastChance.equals(activeCpu)) { 463 | // try to alter! 464 | AffinityLock.acquireLock(lastChance.intValue()); 465 | System.out.println("We had locked on to " + lastChance.intValue() + " but lost it and are running on " + activeCpu); 466 | } 467 | } 468 | } else { // see if BitSet affinity equals actual CPU binding 469 | if (affinity.nextSetBit(0) != activeCpu) { 470 | // try to alter! 471 | AffinityLock.acquireLock(affinity.nextSetBit(0)); 472 | System.out.println("We had locked on to " + affinity.nextSetBit(0) + " but lost it and are running on " + activeCpu); 473 | } 474 | } 475 | } 476 | this.hashEnd = System.currentTimeMillis(); 477 | this.hashTime = this.hashEnd - this.hashBegin; 478 | this.hashBegin = System.currentTimeMillis(); 479 | completeSession(); 480 | this.loopTime = 0l; 481 | } 482 | } 483 | } catch (Throwable e) { 484 | e.printStackTrace(); 485 | } 486 | 487 | if (kill) { 488 | scratch.clear(); // we don't return it. Let's clear it, and let it be GC'd. The Scratch will gen a new one. 489 | try { 490 | Method dispose = scratch.getClass().getDeclaredMethod("dispose"); 491 | dispose.setAccessible(true); 492 | dispose.invoke(scratch); 493 | } catch (Throwable t) { 494 | System.err.println("After a potential memory failure was identified, our attempt to release this memory buffer has failed."); 495 | t.printStackTrace(); 496 | } 497 | scratch = null; 498 | Memory.purge(); 499 | } else { 500 | SafeMappedHasher.returnScratch(scratch); 501 | } 502 | this.hashEnd = System.currentTimeMillis(); 503 | this.hashTime = this.hashEnd - this.hashBegin; 504 | this.parent.hasherCount.decrementAndGet(); 505 | } 506 | 507 | // allocate 508 | public int invoke(PointerByReference memory, Size_t byte_to_allocate) { 509 | 510 | if (byte_to_allocate.intValue() > this.scratch.size()) { 511 | return -22; // memory allocation error 512 | } 513 | 514 | memory.setValue(this.scratch); 515 | 516 | return 0; 517 | } 518 | 519 | // no-op deallcate, wipe? 520 | public void invoke(Pointer memory, Size_t byte_to_allocate) { 521 | // we ignore this, since we manage memory on our own. 522 | } 523 | 524 | public String getType() { 525 | return "CPU"; 526 | } 527 | 528 | public void kill() { 529 | kill = true; 530 | active = false; 531 | } 532 | } 533 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/Utility.java: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | Copyright (c) 2018 AroDev, adaptation portions (c) 2018 ProgrammerDan (Daniel Boston) 4 | 5 | www.arionum.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of 16 | the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 24 | OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | */ 27 | package com.programmerdan.arionum.arionum_miner; 28 | 29 | import java.math.BigInteger; 30 | import java.nio.charset.Charset; 31 | import java.util.LinkedList; 32 | 33 | /** 34 | * Low efficiency port of base58 conversion utils from 35 | * commit e14b696362fb79d60c4ff8bc651185740b8021d9 on https://github.com/arionum/miner 36 | * 37 | * Credit to AroDev, and for base58 functions: https://github.com/tuupola/base58/ 38 | * 39 | * @author ProgrammerDan (Daniel Boston) 40 | * 41 | */ 42 | public class Utility { 43 | public static String base58_chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; 44 | 45 | public static byte[] baseConvert(byte[] _source, int sourceBase, int targetBase) { 46 | LinkedList source = new LinkedList(); 47 | for (byte sourceByte : _source ) { 48 | source.add(sourceByte); 49 | } 50 | LinkedList result = new LinkedList(); 51 | 52 | int count = 0; 53 | 54 | while ((count = source.size()) > 0) { 55 | LinkedList quotient = new LinkedList(); 56 | int remainder = 0; 57 | for (int i = 0; i != count; i++) { 58 | int accum = source.get(i) + remainder * sourceBase; 59 | int digit = (int) (accum / targetBase); 60 | remainder = accum % targetBase; 61 | if (quotient.size() > 0 || digit != 0) { 62 | quotient.add((byte) digit); 63 | } 64 | } 65 | result.addFirst((byte) remainder); 66 | source = quotient; 67 | } 68 | 69 | byte[] _result = new byte[result.size()]; 70 | int i = 0; 71 | for(Byte resultByte : result) { 72 | _result[i] = resultByte; 73 | i++; 74 | } 75 | 76 | return _result; 77 | } 78 | 79 | public static String base58_decode(String data) { 80 | char[] dat = data.toCharArray(); 81 | byte[] map = new byte[dat.length]; 82 | for (int i = 0; i < dat.length; i++) { 83 | map[i] = (byte) base58_chars.indexOf(dat[i]); 84 | } 85 | 86 | byte[] converted = baseConvert(map, 58, 256); 87 | return new String(converted, Charset.forName("ASCII")); 88 | } 89 | 90 | /** 91 | * Be aware that php and java deal with number conversion pretty 92 | * divergently. More testing and figurin' needs doing to get this to 93 | * match the output of the php function. 94 | * 95 | * Use base58_decode string variant, where parity is achieved. 96 | * 97 | * @param data 98 | * @return 99 | */ 100 | public static BigInteger base58_decodeInt(String data) { 101 | char[] dat = data.toCharArray(); 102 | byte[] map = new byte[dat.length]; 103 | for (int i = 0; i < dat.length; i++) { 104 | map[i] = (byte) base58_chars.indexOf(dat[i]); 105 | } 106 | 107 | byte[] converted = baseConvert(map, 58, 10); 108 | StringBuilder sb = new StringBuilder(); 109 | for (byte a : converted) { 110 | sb.append(a); 111 | } 112 | return new BigInteger(sb.toString()); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/jna/Argon2Library.java: -------------------------------------------------------------------------------- 1 | package com.programmerdan.arionum.arionum_miner.jna; 2 | 3 | /* 4 | Included from de.mkammerer's library. 5 | de.mkammerer.argon2.jna 6 | 7 | This library is GPL3 8 | 9 | 10 | */ 11 | import com.sun.jna.Library; 12 | import com.sun.jna.Native; 13 | import com.sun.jna.Pointer; 14 | import com.sun.jna.Callback; 15 | import com.sun.jna.ptr.PointerByReference; 16 | 17 | /** 18 | * JNA bindings for Argon2. 19 | */ 20 | public interface Argon2Library extends Library { 21 | /** 22 | * Singleton instance. 23 | */ 24 | Argon2Library INSTANCE = (Argon2Library) Native.loadLibrary("argon2", Argon2Library.class); 25 | 26 | /** 27 | * Return code if everything is okay. 28 | */ 29 | int ARGON2_OK = 0; 30 | 31 | JnaUint32 ARGON2_DEFAULT_FLAGS = new JnaUint32(0); 32 | 33 | 34 | /* 35 | * Uses an Argon Context to generate a hash. The hash is in the "out" field. 36 | */ 37 | int argon2_ctx(Argon2_Context context, Argon2_type type); 38 | 39 | 40 | /* 41 | * Uses an argon context to render a fully encoded Argon2 hash string in a 42 | * standard form. This is an Arionum-specific addition to the library header. 43 | */ 44 | int encode_ctx(byte[] dst, Size_t dst_len, Argon2_Context ctx, Argon2_type type); 45 | 46 | /* 47 | int argon2i_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, 48 | const uint32_t parallelism, const void *pwd, 49 | const size_t pwdlen, const void *salt, 50 | const size_t saltlen, const size_t hashlen, 51 | char *encoded, const size_t encodedlen); 52 | */ 53 | 54 | /** 55 | * Hashes a password with Argon2i, producing an encoded hash. 56 | * 57 | * @param t_cost Number of iterations 58 | * @param m_cost Sets memory usage to m_cost kibibytes 59 | * @param parallelism Number of threads and compute lanes 60 | * @param pwd Pointer to password 61 | * @param pwdlen Password size in bytes 62 | * @param salt Pointer to salt 63 | * @param saltlen Salt size in bytes 64 | * @param hashlen Desired length of the hash in bytes 65 | * @param encoded Buffer where to write the encoded hash 66 | * @param encodedlen Size of the buffer (thus max size of the encoded hash) 67 | * @return {@link #ARGON2_OK} if successful 68 | */ 69 | int argon2i_hash_encoded(JnaUint32 t_cost, JnaUint32 m_cost, JnaUint32 parallelism, byte[] pwd, Size_t pwdlen, byte[] salt, Size_t saltlen, Size_t hashlen, byte[] encoded, Size_t encodedlen); 70 | 71 | /** 72 | * Hashes a password with Argon2i, producing an encoded hash. 73 | * 74 | * @param t_cost Number of iterations 75 | * @param m_cost Sets memory usage to m_cost kibibytes 76 | * @param parallelism Number of threads and compute lanes 77 | * @param pwd Pointer to password 78 | * @param pwdlen Password size in bytes 79 | * @param salt Pointer to salt 80 | * @param saltlen Salt size in bytes 81 | * @param hash Buffer where to write the raw hash 82 | * @param hashlen Desired length of the hash in bytes 83 | * @return {@link #ARGON2_OK} if successful 84 | */ 85 | int argon2i_hash_raw(JnaUint32 t_cost, JnaUint32 m_cost, JnaUint32 parallelism, byte[] pwd, Size_t pwdlen, byte[] salt, Size_t saltlen, byte[] hash, Size_t hashlen); 86 | 87 | /** 88 | * Verifies a password against an Argon2i encoded string. 89 | * 90 | * @param encoded String encoding parameters, salt, hash 91 | * @param pwd Pointer to password 92 | * @param pwdlen Password size in bytes 93 | * @return ARGON2_OK if successful 94 | */ 95 | /* 96 | int argon2i_verify(const char *encoded, const void *pwd, const size_t pwdlen); 97 | */ 98 | int argon2i_verify(byte[] encoded, byte[] pwd, Size_t pwdlen); 99 | 100 | 101 | /** 102 | * Returns the encoded hash length for the given input parameters. 103 | * 104 | * @param t_cost Number of iterations. 105 | * @param m_cost Memory usage in kibibytes. 106 | * @param parallelism Number of threads; used to compute lanes. 107 | * @param saltlen Salt size in bytes. 108 | * @param hashlen Hash size in bytes. 109 | * @param type The argon2 type. 110 | * @return The encoded hash length in bytes. 111 | */ 112 | Size_t argon2_encodedlen(JnaUint32 t_cost, JnaUint32 m_cost, JnaUint32 parallelism, JnaUint32 saltlen, JnaUint32 hashlen, Argon2_type type); 113 | 114 | /** 115 | * Get the associated error message for given error code. 116 | * 117 | * @param error_code Numeric error code. 118 | * @return The error message associated with the given error code. 119 | */ 120 | String argon2_error_message(int error_code); 121 | 122 | 123 | public interface AllocateFunction extends Callback { 124 | int invoke(PointerByReference memory, Size_t byte_to_allocate); 125 | } 126 | 127 | public interface DeallocateFunction extends Callback { 128 | void invoke(Pointer memory, Size_t byte_to_allocate); 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/jna/Argon2_Context.java: -------------------------------------------------------------------------------- 1 | package com.programmerdan.arionum.arionum_miner.jna; 2 | 3 | /* 4 | 5 | JNA wraps for argon context so I can pin memory 6 | 7 | MIT License, Copyright ProgrammerDan 2018 8 | */ 9 | 10 | import java.util.List; 11 | import java.util.Arrays; 12 | 13 | import com.sun.jna.Structure; 14 | import com.sun.jna.Pointer; 15 | 16 | /** 17 | * argon2_context type for C interaction. 18 | */ 19 | public class Argon2_Context extends Structure { 20 | public Pointer out; 21 | public JnaUint32 outlen; 22 | 23 | public Pointer pwd; 24 | public JnaUint32 pwdlen; 25 | 26 | public Pointer salt; 27 | public JnaUint32 saltlen; 28 | 29 | public Pointer secret; 30 | public JnaUint32 secretlen; 31 | 32 | public Pointer ad; 33 | public JnaUint32 adlen; 34 | 35 | public JnaUint32 t_cost; 36 | public JnaUint32 m_cost; 37 | public JnaUint32 lanes; 38 | public JnaUint32 threads; 39 | 40 | public JnaUint32 version; 41 | 42 | public Argon2Library.AllocateFunction allocate_cbk; 43 | public Argon2Library.DeallocateFunction free_cbk; 44 | 45 | public JnaUint32 flags; 46 | 47 | public List getFieldOrder() { 48 | return Arrays.asList( new String[] { 49 | "out", "outlen", 50 | "pwd", "pwdlen", 51 | "salt", "saltlen", 52 | "secret", "secretlen", 53 | "ad", "adlen", 54 | "t_cost", "m_cost", 55 | "lanes", "threads", 56 | "version", 57 | "allocate_cbk", "free_cbk", 58 | "flags" 59 | }); 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/jna/Argon2_type.java: -------------------------------------------------------------------------------- 1 | package com.programmerdan.arionum.arionum_miner.jna; 2 | 3 | /* 4 | Included from de.mkammerer's library. 5 | de.mkammerer.argon2.jna 6 | 7 | This library is GPL3 8 | 9 | 10 | */ 11 | 12 | import com.sun.jna.NativeLong; 13 | 14 | /** 15 | * argon2_type type for C interaction. 16 | */ 17 | public class Argon2_type extends NativeLong { 18 | /** 19 | * Constructor. 20 | */ 21 | public Argon2_type() { 22 | this(0); 23 | } 24 | 25 | /** 26 | * Constructor. 27 | * 28 | * @param value Value. 29 | */ 30 | public Argon2_type(long value) { 31 | super(value); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/jna/JnaUint32.java: -------------------------------------------------------------------------------- 1 | package com.programmerdan.arionum.arionum_miner.jna; 2 | 3 | /* 4 | Included from de.mkammerer's library. 5 | de.mkammerer.argon2.jna 6 | 7 | This library is GPL3 8 | 9 | 10 | */ 11 | 12 | import com.sun.jna.IntegerType; 13 | 14 | /** 15 | * uint32_t type for C interaction. 16 | */ 17 | public class JnaUint32 extends IntegerType { 18 | /** 19 | * Constructor. 20 | */ 21 | public JnaUint32() { 22 | this(0); 23 | } 24 | 25 | /** 26 | * Constructor. 27 | * 28 | * @param value Value. 29 | */ 30 | public JnaUint32(int value) { 31 | super(4, value, true); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/jna/JnaUint8.java: -------------------------------------------------------------------------------- 1 | package com.programmerdan.arionum.arionum_miner.jna; 2 | 3 | /* 4 | Included from de.mkammerer's library. 5 | de.mkammerer.argon2.jna 6 | 7 | This library is GPL3 8 | 9 | 10 | */ 11 | 12 | import com.sun.jna.IntegerType; 13 | 14 | /** 15 | * uint8_t type for C interaction. 16 | */ 17 | public class JnaUint8 extends IntegerType { 18 | /** 19 | * Constructor. 20 | */ 21 | public JnaUint8() { 22 | this(0); 23 | } 24 | 25 | /** 26 | * Constructor. 27 | * 28 | * @param value Value. 29 | */ 30 | public JnaUint8(int value) { 31 | super(1, value, true); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /arionum-miner/src/main/java/com/programmerdan/arionum/arionum_miner/jna/Size_t.java: -------------------------------------------------------------------------------- 1 | package com.programmerdan.arionum.arionum_miner.jna; 2 | 3 | /* 4 | Included from de.mkammerer's library. 5 | de.mkammerer.argon2.jna 6 | 7 | This library is GPL3 8 | 9 | 10 | */ 11 | 12 | import com.sun.jna.IntegerType; 13 | import com.sun.jna.Native; 14 | 15 | /** 16 | * size_t type for C interaction. 17 | */ 18 | public class Size_t extends IntegerType { 19 | /** 20 | * Constructor. 21 | */ 22 | public Size_t() { 23 | this(0); 24 | } 25 | 26 | /** 27 | * Constructor. 28 | * 29 | * @param value Value. 30 | */ 31 | public Size_t(long value) { 32 | super(Native.SIZE_T_SIZE, value); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /arionum-miner/src/main/resources/darwin/libargon2.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammerDan/arionum-java/17d3ecedfbf6106e66b7a1b19702604df53f764d/arionum-miner/src/main/resources/darwin/libargon2.dylib -------------------------------------------------------------------------------- /arionum-miner/src/main/resources/linux-x86-64/libargon2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammerDan/arionum-java/17d3ecedfbf6106e66b7a1b19702604df53f764d/arionum-miner/src/main/resources/linux-x86-64/libargon2.so -------------------------------------------------------------------------------- /arionum-miner/src/main/resources/win32-x86-64/argon2-avx.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammerDan/arionum-java/17d3ecedfbf6106e66b7a1b19702604df53f764d/arionum-miner/src/main/resources/win32-x86-64/argon2-avx.dll -------------------------------------------------------------------------------- /arionum-miner/src/main/resources/win32-x86-64/argon2-avx2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammerDan/arionum-java/17d3ecedfbf6106e66b7a1b19702604df53f764d/arionum-miner/src/main/resources/win32-x86-64/argon2-avx2.dll -------------------------------------------------------------------------------- /arionum-miner/src/main/resources/win32-x86-64/argon2-avx512f.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammerDan/arionum-java/17d3ecedfbf6106e66b7a1b19702604df53f764d/arionum-miner/src/main/resources/win32-x86-64/argon2-avx512f.dll -------------------------------------------------------------------------------- /arionum-miner/src/main/resources/win32-x86-64/argon2-generic.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammerDan/arionum-java/17d3ecedfbf6106e66b7a1b19702604df53f764d/arionum-miner/src/main/resources/win32-x86-64/argon2-generic.dll -------------------------------------------------------------------------------- /arionum-miner/src/main/resources/win32-x86-64/argon2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProgrammerDan/arionum-java/17d3ecedfbf6106e66b7a1b19702604df53f764d/arionum-miner/src/main/resources/win32-x86-64/argon2.dll --------------------------------------------------------------------------------