| var crypto = require("crypto"); | |||||
| exports.createCoinbase = createCoinbase; | |||||
| exports.createCbHash = createCbHash; | |||||
| exports.buildMerkleRoot = buildMerkleRoot; | |||||
| exports.doublesha = doublesha; | |||||
| exports.incbufBE = incbufBE; | |||||
| /* Create a coinbase buffer. | |||||
| * | |||||
| * Args: | |||||
| * cb1, cb2: Buffer, coinbase parts 1 and 2 | |||||
| * ex1, ex2: Buffer, extranounce 1 and 2 | |||||
| * | |||||
| * Returns: | |||||
| * Buffer, a complete coinbase | |||||
| */ | |||||
| function createCoinbase(ex1, ex2, cb1, cb2) { | |||||
| return Buffer.concat([ cb1, ex1, ex2, cb2 ]); | |||||
| } | |||||
| /* Create a coinbase hash. | |||||
| * | |||||
| * Args: | |||||
| * coinbase: Buffer, the coinbase | |||||
| * | |||||
| * Returns: | |||||
| * Buffer, the cbHashBin | |||||
| */ | |||||
| function createCbHash(coinbase) { | |||||
| return doublesha(coinbase); | |||||
| } | |||||
| /* Build a merkle root from a merkle branch and a coinbase hash. | |||||
| * | |||||
| * Returns: | |||||
| * Buffer, the merkle root | |||||
| * | |||||
| * Args: | |||||
| * merkleBranch: Array, hex encoded hashes | |||||
| * cbHash: Buffer, the coinbase hash | |||||
| */ | |||||
| function buildMerkleRoot(merkleBranch, cbHash) { | |||||
| var root = cbHashBin | |||||
| for (var i in merkleBranch) { | |||||
| var h = Buffer.from(merkleBranch[i], "hex"); | |||||
| root = doublesha(Buffer.concat(root, h)); | |||||
| } | |||||
| return root; | |||||
| } | |||||
| /* Run sha256 twice on a buffer. | |||||
| * | |||||
| * Returns: | |||||
| * Buffer, the double-sha256'd buffer | |||||
| * | |||||
| * Args: | |||||
| * buf: Buffer, will be double-sha256'd | |||||
| */ | |||||
| function doublesha(buf) { | |||||
| var tmp = crypto.createHash("sha256").update(buf).digest(); | |||||
| return crypto.createHash("sha256").update(tmp).digest(); | |||||
| } | |||||
| /* Increment a buffer. | |||||
| * | |||||
| * Args: | |||||
| * buf: Buffer, will be incremented. | |||||
| */ | |||||
| function incbufBE(buf) { | |||||
| for (var i = buf.length - 1; i >= 0; --i) { | |||||
| if (buf[i]++ !== 255) | |||||
| break; | |||||
| } | |||||
| } |
| var SerialPort = require("serialport"); | var SerialPort = require("serialport"); | ||||
| class ASICConn { | |||||
| class AsicMiner { | |||||
| constructor(dev, baud) { | constructor(dev, baud) { | ||||
| this.ready = false; | this.ready = false; | ||||
| this.error = null; | this.error = null; | ||||
| } | } | ||||
| } | } | ||||
| module.exports = ASICConn; | |||||
| module.exports = AsicMiner; |
| var spawn = require("child_process").spawn; | |||||
| function hps(n) { | |||||
| var suffix = "Hash/s"; | |||||
| if (n > 1000000) { | |||||
| n = n / 1000000; | |||||
| suffix ="M"+suffix; | |||||
| } else if (n > 1000) { | |||||
| n = n / 1000; | |||||
| suffix = "k"+suffix; | |||||
| } | |||||
| return (n.toFixed(2))+" "+suffix; | |||||
| } | |||||
| function mineProc(cb1, cb2, merk, dif, en1, en2, cnt) { | |||||
| var obj = { | |||||
| coinb1: cb1, coinb2: cb2, merkleBranch: merk, | |||||
| difficulty: dif, exnounce1: en1, exnounce2: en2, iters: cnt | |||||
| }; | |||||
| var child = spawn( | |||||
| "node", [ __dirname+"/mining-process.js", JSON.stringify(obj) ]); | |||||
| child.output = ""; | |||||
| child.hps = 0; | |||||
| child.stderr.on("data", d => process.stderr.write(d)); | |||||
| child.stdout.on("data", d => { | |||||
| var s = d.toString(); | |||||
| if (s[0] === "o") | |||||
| child.output = s.substr(1); | |||||
| else if (s[0] === "h") | |||||
| child.hps = parseInt(s.substr(1)); | |||||
| else | |||||
| console.error("Warning: Unexpected child output,", s); | |||||
| }); | |||||
| return child; | |||||
| } | |||||
| function mine( | |||||
| coinb1, coinb2, merkleBranch, | |||||
| difficulty, exnounce1, exnounce2_len) { | |||||
| var cores = 4; | |||||
| var max = Math.pow(2, exnounce2_len * 8) - 2; | |||||
| var parts = Math.ceil(max / cores); | |||||
| return new Promise((resolve, reject) => { | |||||
| var childs = []; | |||||
| var childsLeft = cores; | |||||
| var inter = null; | |||||
| function rej(hash) { | |||||
| clearInterval(inter); | |||||
| resolve(hash); | |||||
| } | |||||
| for (var i = 0; i < cores; ++i) { | |||||
| var num = parts * i; | |||||
| if (num + parts > max) | |||||
| max - parts; | |||||
| var buf = Buffer.alloc(exnounce2_len, '\0'); | |||||
| buf.writeUInt32BE(num); | |||||
| var exnounce2 = buf.toString("hex"); | |||||
| var child = mineProc( | |||||
| coinb1, coinb2, merkleBranch, | |||||
| difficulty, exnounce1, exnounce2, parts); | |||||
| var obj = { | |||||
| coinb1, coinb2, merkleBranch, | |||||
| difficulty, exnounce1, exnounce2_len | |||||
| }; | |||||
| childs[i] = child; | |||||
| } | |||||
| childs.forEach(child => { | |||||
| child.on("exit", code => { | |||||
| console.error("Child exited with code", code); | |||||
| if (code === 0) { | |||||
| childsLeft -= 1; | |||||
| childs.forEach(x => x !== child && x.kill()); | |||||
| rej(child.output.trim()); | |||||
| } else { | |||||
| childsLeft -= 1; | |||||
| if (childsLeft <= 0) | |||||
| rej(false); | |||||
| } | |||||
| }); | |||||
| }); | |||||
| inter = setInterval(() => { | |||||
| var sum = 0; | |||||
| childs.forEach(c => sum += c.hps); | |||||
| console.log(hps(sum)); | |||||
| }, 2000); | |||||
| }); | |||||
| } | |||||
| class CPUMiner { | |||||
| constructor() { | |||||
| this.work = null; | |||||
| this.difficulty = 1; | |||||
| this.exnounce1 = null; | |||||
| this.exnounce2_len = null; | |||||
| } | |||||
| async startWork(work) { | |||||
| if (this.exnounce1 == null) { | |||||
| console.log("Ignoring work because extranounce is null."); | |||||
| return false; | |||||
| } | |||||
| this.work = work; | |||||
| return await mine( | |||||
| this.work.coinb1, | |||||
| this.work.coinb2, | |||||
| this.work.berkleBranch, | |||||
| this.difficulty, | |||||
| this.exnounce1, | |||||
| this.exnounce2_len); | |||||
| } | |||||
| setDifficulty(difficulty) { | |||||
| this.difficulty = difficulty; | |||||
| } | |||||
| setNounce(en1, en2_len) { | |||||
| this.exnounce1 = en1; | |||||
| this.exnounce2_len = en2_len; | |||||
| } | |||||
| async wait() { | |||||
| } | |||||
| } | |||||
| module.exports = CPUMiner; |
| var cryptutil = require("../cryptutil"); | |||||
| var { | |||||
| coinb1, coinb2, merkleBranch, | |||||
| difficulty, exnounce1, exnounce2, iters | |||||
| } = JSON.parse(process.argv[2]); | |||||
| var start = exnounce2.toString("hex"); | |||||
| coinb1 = Buffer.from(coinb1, "hex"); | |||||
| coinb2 = Buffer.from(coinb2, "hex"); | |||||
| exnounce1 = Buffer.from(exnounce1, "hex"); | |||||
| exnounce2 = Buffer.from(exnounce2, "hex"); | |||||
| difficulty = 3; | |||||
| var hashes = 0; | |||||
| var sd = new Date().getTime(); | |||||
| for (var i = 0; i < iters; ++i) { | |||||
| var coinbase = | |||||
| cryptutil.createCoinbase(exnounce1, exnounce2, coinb1, coinb2); | |||||
| var cbHash = cryptutil.createCbHash(coinbase); | |||||
| var success = true; | |||||
| for (var i = 0; i < difficulty; ++i) { | |||||
| if (cbHash[i] !== 0) { | |||||
| success = false; | |||||
| break; | |||||
| } | |||||
| } | |||||
| hashes += 1; | |||||
| var d = new Date().getTime(); | |||||
| if (d - 2000 >= sd) { | |||||
| console.log("h"+Math.floor(hashes / 2)); | |||||
| sd = d; | |||||
| hashes = 0; | |||||
| } | |||||
| if (success) { | |||||
| console.log("o"+cbHash.toString("hex")); | |||||
| process.exit(0); | |||||
| } | |||||
| cryptutil.incbufBE(exnounce2); | |||||
| if (exnounce2[0] == 255) { | |||||
| console.error("exnounce2[0] reached 255"); | |||||
| process.exit(1); | |||||
| } | |||||
| } | |||||
| console.error("iterated through "+iters); | |||||
| process.exit(1); |
| var RPCConn = require("./rpcconn"); | var RPCConn = require("./rpcconn"); | ||||
| var ASICConn = require("./asicconn"); | |||||
| class Miner { | |||||
| constructor(ip, port, dev, baud) { | |||||
| this.name = dev | |||||
| this.work = null; | |||||
| this.nextDifficulty = 1; | |||||
| this.exnounce1 = null; | |||||
| this.exnounce2_len = null; | |||||
| class StratumClient { | |||||
| constructor(ip, port, miner) { | |||||
| this.name = ip+":"+port; | |||||
| this.asic = new ASICConn(dev, baud); | |||||
| this.waitFor = 2; | |||||
| this.miner = miner; | |||||
| this.rpc = new RPCConn(ip, port); | this.rpc = new RPCConn(ip, port); | ||||
| } | } | ||||
| log(...msgs) { | log(...msgs) { | ||||
| process.stdout.write(this.name+": "); | process.stdout.write(this.name+": "); | ||||
| console.log(msgs); | |||||
| console.log.apply(console, msgs); | |||||
| } | |||||
| async startWork() { | |||||
| this.log("Starting work"); | |||||
| var res; | |||||
| try { | |||||
| res = await this.miner.startWork(this.work); | |||||
| } catch (err) { | |||||
| console.trace(err); | |||||
| return; | |||||
| } | |||||
| this.log("Work done,", res.toString("hex")); | |||||
| } | } | ||||
| async connect() { | async connect() { | ||||
| await this.asic.wait(); | |||||
| await this.rpc.wait(); | await this.rpc.wait(); | ||||
| await this.miner.wait(); | |||||
| if (this.asic.error) | |||||
| throw this.asic.error; | |||||
| this.rpc.on("mining.notify", params => { | |||||
| this.rpc.on("mining.notify", async params => { | |||||
| var work = { | var work = { | ||||
| id: params[0], prevHash: params[1], | id: params[0], prevHash: params[1], | ||||
| coinb1: params[2], coinb2: params[3], | coinb1: params[2], coinb2: params[3], | ||||
| nBits: params[6], nTime: params[7], cleanJobs: params[8], | nBits: params[6], nTime: params[7], cleanJobs: params[8], | ||||
| }; | }; | ||||
| this.notify(work); | |||||
| this.work = work; | |||||
| if (this.ready <= 0) | |||||
| this.startWork(); | |||||
| }); | }); | ||||
| this.rpc.on("mining.set_difficulty", params => { | this.rpc.on("mining.set_difficulty", params => { | ||||
| this.log("difficulty", params); | |||||
| this.difficulty(params[0]); | |||||
| this.log("difficulty", params[0]); | |||||
| this.miner.setDifficulty(params[0]); | |||||
| if (--this.waitFor == 0) | |||||
| this.startWork(); | |||||
| }); | }); | ||||
| var sub = await this.rpc.call("mining.subscribe"); | var sub = await this.rpc.call("mining.subscribe"); | ||||
| this.exnounce1 = sub[1]; | |||||
| this.exnounce2_len = sub[2]; | |||||
| this.miner.setNounce(sub[1], sub[2]); | |||||
| if (--this.waitFor == 0) | |||||
| this.startWork(); | |||||
| } | } | ||||
| async auth(user, pass) { | async auth(user, pass) { | ||||
| var success = await this.rpc.call("mining.authorize", user, pass); | var success = await this.rpc.call("mining.authorize", user, pass); | ||||
| if (!success) | if (!success) | ||||
| throw new Error("Incorrect username/password."); | throw new Error("Incorrect username/password."); | ||||
| console.log("Authenticated "+user+"."); | |||||
| } | |||||
| notify(work) { | |||||
| if (this.exnounce1 == null) | |||||
| return console.log("Ignoring work because extranounce is null."); | |||||
| this.log("Notification"); | |||||
| this.log(work); | |||||
| this.work = work; | |||||
| this.log( | |||||
| "Using extranounce1 '"+this.exnounce1+ | |||||
| "', and exnounce2 length "+this.exnounce2_len); | |||||
| } | |||||
| difficulty(diff) { | |||||
| this.nextDifficulty = diff; | |||||
| console.log("Authenticated "+user+"."); | |||||
| } | } | ||||
| writePayload() { | writePayload() { | ||||
| } | } | ||||
| } | } | ||||
| module.exports = Miner; | |||||
| module.exports = StratumClient; |
| #!/usr/bin/env node | #!/usr/bin/env node | ||||
| var dns = require("dns"); | var dns = require("dns"); | ||||
| var Miner = require("./js/miner"); | |||||
| var StratumClient = require("./js/stratum-client"); | |||||
| var CPUMiner = require("./js/miner-cpu"); | |||||
| var ASICMiner = require("./js/miner-asic"); | |||||
| var domain = "stratum.slushpool.com" | var domain = "stratum.slushpool.com" | ||||
| var port = 3333; | var port = 3333; | ||||
| dns.lookup(domain, async (err, ip) => { | dns.lookup(domain, async (err, ip) => { | ||||
| if (err) throw err; | if (err) throw err; | ||||
| var m = new Miner(ip, port, "/dev/ttyACM0", 115200); | |||||
| var m = new CPUMiner(); | |||||
| var c = new StratumClient(ip, port, m); | |||||
| try { | try { | ||||
| await m.connect(); | |||||
| await m.auth("mort96.worker1", "test"); | |||||
| await c.connect(); | |||||
| await c.auth("mort96.worker1", "test"); | |||||
| } catch (err) { | } catch (err) { | ||||
| console.log(err); | console.log(err); | ||||
| process.exit(1); | process.exit(1); |