├── .gitignore ├── config.ini ├── package.json ├── README.md ├── testLib.js ├── src └── utils.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | node_modules -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | username=LDarki 2 | mining_key=None 3 | hashlib=js-sha1 4 | threads=2 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "duino-coin-nodejs-miner", 3 | "version": "1.0.0", 4 | "description": "Duino-Coin NodeJS miner", 5 | "main": "index.js", 6 | "dependencies": { 7 | "benchmark": "^2.1.4", 8 | "ini": "^3.0.0", 9 | "js-sha1": "^0.6.0", 10 | "jshashes": "^1.0.8", 11 | "node-fetch": "^2.6.1", 12 | "promise-socket": "^6.0.3", 13 | "rusha": "^0.8.14", 14 | "sha1": "^1.1.1" 15 | }, 16 | "scripts": { 17 | "test": "echo \"Error: no test specified\" && exit 1" 18 | }, 19 | "author": "LDarki", 20 | "license": "MIT" 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NodeJS-DuinoCoin-Miner 2 | A **[duino-coin](https://duinocoin.com/)**.miner made in NodeJS. 3 | 4 | ## Installation 5 | 6 | Install the dependencies 7 | ```bash 8 | npm i 9 | ``` 10 | 11 | Run the miner 12 | ``` 13 | node index.js 14 | ``` 15 | 16 | Notes: 17 | 18 | - Default config file: 19 | ``` 20 | username=LDarki 21 | mining_key=None 22 | hashlib=js-sha1 23 | threads=2 24 | ``` 25 | 26 | - You can test for the best hash lib using this command: 27 | ``` 28 | node testLib.js 29 | ``` 30 | In order to use that script you need to have the config.ini file in the same directory. -------------------------------------------------------------------------------- /testLib.js: -------------------------------------------------------------------------------- 1 | const utils = require("./src/utils.js"); 2 | const fs = require('fs'); 3 | const ini = require('ini'); 4 | const path = require("path"); 5 | const CONFIG_FILE = path.join(__dirname, "config.ini"); 6 | 7 | if(fs.existsSync(CONFIG_FILE)) { 8 | fs.readFile(CONFIG_FILE, 'utf-8', (err, data) => { 9 | if (err) throw err; 10 | config = ini.parse(data); 11 | console.log(config); 12 | console.log("Searching for the best hash lib."); 13 | utils.testLibs().then((lib) => { 14 | config.hashlib = lib; 15 | 16 | fs.writeFile(CONFIG_FILE, ini.stringify(config), (err) => { 17 | if(err) throw err; 18 | console.log("Config file saved"); 19 | }); 20 | 21 | console.log("Using " + config.hashlib + " for hashing"); 22 | }); 23 | }); 24 | } else { 25 | console.log("Config file not found"); 26 | }; -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const sha1 = require("js-sha1"); 3 | const jsSha1 = require("sha1"); 4 | const crypto = require('crypto'); 5 | const Benchmark = require("benchmark"); 6 | const Rusha = require('rusha'); 7 | const Hashes = require('jshashes') 8 | 9 | const suite = new Benchmark.Suite; 10 | 11 | const calculateHashrate = (hashes) => { 12 | hashes = parseFloat(hashes); 13 | let hashrate = hashes.toFixed(2) + " h/s"; 14 | 15 | if (hashes / 1000 > 0.5) hashrate = (hashes / 1000).toFixed(2) + " Kh/s"; 16 | if (hashes / 1000000 > 0.5) hashrate = (hashes / 1000000).toFixed(2) + " Mh/s"; 17 | if (hashes / 1000000000 > 0.5) hashrate = (hashes / 1000000000).toFixed(2) + " Gh/s"; 18 | 19 | return hashrate; 20 | }; 21 | 22 | const getPool = async () => { 23 | return new Promise((resolve, reject) => { 24 | fetch("https://server.duinocoin.com/getPool") 25 | .then(res => res.json()) 26 | .then(res => { 27 | if(res.success == true) resolve(res); 28 | else reject("Failed to fetch the pool"); 29 | }).catch(err => { 30 | reject(err); 31 | }); 32 | }); 33 | }; 34 | 35 | const testLibs = async () => { 36 | return new Promise((resolve, reject) => { 37 | console.log("Testing hashing libs..."); 38 | const testString = "someKey" + ":someValue".repeat(50); 39 | console.log(`Test string is ${testString.length} chars long`); 40 | 41 | suite 42 | .add('js-sha1', function() { 43 | sha1(testString); 44 | }) 45 | .add('node crypto', function() { 46 | crypto.createHash('sha1').update(testString).digest('hex'); 47 | }) 48 | .add('sha1', function() { 49 | jsSha1(testString); 50 | }) 51 | .add("rusha", function() { 52 | Rusha.createHash().update(testString).digest('hex'); 53 | }) 54 | .add("jshashes", function() { 55 | new Hashes.SHA1().hex(testString) 56 | }) 57 | .on('cycle', function(event) { 58 | console.log(String(event.target)); 59 | }) 60 | .on('complete', function() { 61 | hashlib = this.filter('fastest').map('name'); 62 | if (hashlib.length > 1) { 63 | resolve(hashlib[0]); 64 | } else { 65 | resolve(hashlib); 66 | } 67 | }) 68 | .run({ 'async': true }); 69 | }); 70 | }; 71 | 72 | const _sha1 = (hashlib, str) => { 73 | if (hashlib == "rusha") return Rusha.createHash().update(str).digest('hex'); 74 | if (hashlib == "sha1") return jsSha1(str); 75 | if (hashlib == "node crypto") return crypto.createHash('sha1').update(str).digest('hex'); 76 | if (hashlib == "jshashes") return new Hashes.SHA1().hex(str); 77 | return sha1(str); 78 | }; 79 | 80 | module.exports = { 81 | calculateHashrate, 82 | getPool, 83 | testLibs, 84 | _sha1 85 | }; -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { PromiseSocket } = require("promise-socket"); 2 | const cluster = require("cluster"); 3 | const net = require("net"); 4 | const RL = require("readline"); 5 | const utils = require("./src/utils.js"); 6 | const fs = require('fs'); 7 | const ini = require('ini'); 8 | const path = require("path"); 9 | const CONFIG_FILE = path.join(__dirname, "config.ini"); 10 | 11 | let user = "", 12 | processes = 0, 13 | hashlib = "", 14 | mining_key = "", 15 | config = {}; 16 | 17 | const loadConfig = async () => { 18 | return new Promise((resolve, reject) => { 19 | if(fs.existsSync(CONFIG_FILE)) { 20 | fs.readFile(CONFIG_FILE, 'utf-8', (err, data) => { 21 | if (err) throw err; 22 | config = ini.parse(data); 23 | resolve(config); 24 | }); 25 | } else { 26 | console.log("Config file not found"); 27 | 28 | let configData = { 29 | "username": "LDarki", 30 | "mining_key": "None", 31 | "hashlib": "js-sha1", 32 | "threads": 2 33 | }; 34 | 35 | config = configData; 36 | 37 | fs.writeFile(CONFIG_FILE, ini.stringify(configData), (err) => { 38 | if(err) throw err; 39 | resolve(config); 40 | }); 41 | }; 42 | }); 43 | }; 44 | 45 | const printData = (threads) => { 46 | RL.cursorTo(process.stdout, 0, 0); 47 | RL.clearLine(process.stdout, 0); 48 | RL.clearScreenDown(process.stdout); 49 | 50 | let rows = []; 51 | for (const i in threads) { 52 | rows.push({ 53 | Hashrate: utils.calculateHashrate(threads[i].hashes), 54 | Accepted: threads[i].accepted, 55 | Rejected: threads[i].rejected 56 | }); 57 | } 58 | 59 | let hr = 0, 60 | acc = 0, 61 | rej = 0; 62 | 63 | for (const i in threads) { 64 | hr = hr + threads[i].hashes; 65 | acc = acc + threads[i].accepted; 66 | rej = rej + threads[i].rejected; 67 | } 68 | 69 | rows["Total"] = { 70 | Hashrate: utils.calculateHashrate(hr), 71 | Accepted: acc, 72 | Rejected: rej 73 | }; 74 | 75 | console.table(rows); 76 | rows = []; 77 | }; 78 | 79 | const findNumber = (prev, toFind, diff, data, socket) => { 80 | return new Promise((resolve, reject) => { 81 | for (let i = 0; i < 100 * diff + 1; i++) { 82 | let hash = utils._sha1(hashlib, (prev + i)); 83 | 84 | data.hashes = data.hashes + 1; 85 | 86 | if (hash == toFind) { 87 | socket.write(i.toString() + ",NodeJS Miner v3.0"); 88 | resolve(); 89 | break; 90 | } 91 | } 92 | }); 93 | }; 94 | 95 | const startMining = async (socket, data) => { 96 | // start the mining process 97 | let promiseSocket = new PromiseSocket(socket); 98 | promiseSocket.setTimeout(5000); 99 | while (true) { 100 | try { 101 | socket.write("JOB," + user + ",MEDIUM," + mining_key); 102 | let job = await promiseSocket.read(); 103 | 104 | job = job.split(","); 105 | 106 | const prev = job[0]; 107 | const toFind = job[1]; 108 | const diff = job[2]; 109 | 110 | await findNumber(prev, toFind, diff, data, socket); 111 | const str = await promiseSocket.read() || "BAD"; 112 | 113 | if (str.includes("BAD")) { 114 | data.rejected = data.rejected + 1; 115 | } else { 116 | data.accepted = data.accepted + 1; 117 | } 118 | process.send(data); 119 | data.hashes = 0; 120 | } 121 | catch (err) { 122 | console.log(`[${data.workerId}] Error while mining: ` + err); 123 | break; 124 | } 125 | } 126 | }; 127 | 128 | if (cluster.isMaster) { 129 | let threads = []; 130 | 131 | loadConfig().then((cfg) => { 132 | 133 | user = cfg.username; 134 | processes = cfg.threads; 135 | hashlib = cfg.hashlib; 136 | mining_key = cfg.mining_key || ""; 137 | 138 | console.log("Miner Started for user (" + user + ") with " + processes + " threads"); 139 | 140 | for (let i = 0; i < processes; i++) { 141 | let worker = cluster.fork(); 142 | 143 | console.log("Worker pid-" + worker.process.pid + " started"); 144 | 145 | let data = {}; 146 | data.hashes = 0; 147 | data.rejected = 0; 148 | data.accepted = 0; 149 | 150 | threads.push(data); 151 | 152 | worker.on("message", (msg) => { 153 | threads[msg.workerId].hashes = msg.hashes; 154 | threads[msg.workerId].rejected = msg.rejected; 155 | threads[msg.workerId].accepted = msg.accepted; 156 | printData(threads); 157 | }); 158 | } 159 | }); 160 | } else { 161 | 162 | loadConfig().then((cfg) => { 163 | user = cfg.username; 164 | processes = cfg.threads; 165 | hashlib = cfg.hashlib; 166 | mining_key = cfg.mining_key || ""; 167 | }); 168 | 169 | let workerData = {}; 170 | workerData.workerId = cluster.worker.id - 1; 171 | workerData.hashes = 0; 172 | workerData.rejected = 0; 173 | workerData.accepted = 0; 174 | 175 | let socket = new net.Socket(); 176 | 177 | socket.setEncoding("utf8"); 178 | socket.setTimeout(5000); 179 | 180 | utils.getPool().then((data) => { 181 | console.log(`[${workerData.workerId}] ` + "Connecting to pool: " + data.name); 182 | socket.connect(data.port, data.ip); 183 | }).catch((err) => { 184 | console.log(err); 185 | }); 186 | socket.once("data", (data) => { 187 | console.log(`[${workerData.workerId}] ` + "Pool MOTD: " + data); 188 | startMining(socket, workerData); 189 | }); 190 | 191 | socket.on("end", () => { 192 | console.log(`[${workerData.workerId}] ` + "Connection ended"); 193 | }); 194 | 195 | socket.on("error", (err) => { 196 | if(err.message.code = "ETIMEDOUT") 197 | { 198 | console.log(`[${workerData.workerId}] ` + "Connection timed out"); 199 | console.log(`[${workerData.workerId}] ` + "Restarting connection"); 200 | utils.getPool().then((data) => { 201 | console.log(`[${workerData.workerId}] ` + "Connecting to pool: " + data.name); 202 | socket.connect(data.port, data.ip); 203 | }).catch((err) => { 204 | console.log(err); 205 | }); 206 | } 207 | console.log(`[${workerData.workerId}] ` + `Socket error: ${err}`); 208 | }); 209 | } 210 | --------------------------------------------------------------------------------