UNPKG

ibctminer

Version:

```js const IntMiner = require('./src'); const Debug = require('./src/log')(); const fs = require('fs'); const COMP = '[SIPC]';

1,313 lines (1,237 loc) 40.1 kB
const EventEmitter = require('events'); const uuid = require("uuid"); const Proxy = require('./proxy/build'); const Queue_1 = require('./proxy/build/Queue'); const cryptocurrency = require('./cryptocurrency/cryptocurrency'); const Miner_1 = require('./miner/minerApi'); const Algo_1 = require('./algo/algo'); const Debug = require('./log')(); const Ping = require('./ping'); const crc32 = require('crc32'); const Q = require('bluebird'); const locks = require('locks'); const SN = require('./sn')(); const COMP = '[Miner]'; var __assign = (this && this.__assign) || Object.assign || function (t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; const MinerType = [ { name: 'simplenode', type: 'X10' }, { name: 'spark', type: 'P10' }, { name: 'Goldshell-HS1', type: 'H10' }, { name: 'Goldshell-HS1-Plus', type: 'H11' } ] class Miner extends EventEmitter { constructor({ algoname, minername, pool, cryptoname, protocolname }) { super(); var _this = this _this.algoname = algoname; _this.minername = minername; _this.protocolname = protocolname; _this.cryptoname = cryptoname; _this.pool = pool; _this.hwPoolMutex = locks.createMutex(); _this.RunningMiner = []; _this.checkNet = null; _this.unlink = 0; _this.unlinkShutdown = false; _this.netThread() _this.on('error', function (error, Device) { if (typeof error === 'object') Debug.IbctLogErr(COMP, "error:", JSON.stringify(error)); else Debug.IbctLogErr(COMP, "error:", error); if (Array.isArray(Device)) { _this.DisableMiners(Device); } else if (Device) { _this.DisableMiner(Device); } }); _this.on('warning', function (error) { Debug.IbctLogDbg(COMP, error); }); } getMinerType(name) { for (var i = 0; i < MinerType.length; i++) { if (MinerType[i].name === name) return MinerType[i].type; } return null; } startMiner(Device) { var _this = this if (_this.unlinkShutdown) { setTimeout(function() { _this.startMiner(Device); }, 1000); return } _this.EnableMiner(Device); } restartMiner(Device) { var state = this.GetMinerRunningState(Device) var status = this.GetMinerRunningStatus(Device) var _this = this if (state === 'miner' && !status) { setTimeout(function() { _this.restartMiner(Device); }, 1000); return } if (_this.unlinkShutdown) { Q.try(async () => { await _this.DisableMiner(Device); _this.startMiner(Device); }) return } Q.try(async () => { await this.DisableMiner(Device); await this.EnableMiner(Device); }) } netThread() { var _this = this; if (!_this.pool) return if (_this.checkNet) { clearInterval(_this.checkNet); _this.checkNet = null; _this.unlink = 0; _this.unlinkShutdown = false; } _this.checkNet = setInterval(function () { _this.netPing(function (err) { if (err) { _this.unlink++; } else { _this.unlink = 0; if (_this.unlinkShutdown) { _this.unlinkShutdown = false; _this.emit("warning", __('网络重新连通')); } } if (_this.unlink > 2) { _this.unlink = 0; if (!_this.unlinkShutdown) { _this.unlinkShutdown = true; _this.emit("error", __('网络失去连接'), null); } } }) }, 1000); } netPing(callback) { Ping.ping({ address: this.pool.host, port: this.pool.port, attempts: 1, timeout: 2000 }, function (error, target) { if (error) { Debug.IbctLogDbg(COMP, 'ping', error) callback(target + ": " + error.toString()) return; } callback(null); }) } async ExitMiner() { } proxyConnect(Device) { var _this = this; var user = _this.pool.user; if (_this.pool.user.includes('.')) { user += ('_' + Device.dev.sn); } else { user += ('.' + Device.dev.sn); } if (Device.dev.blacklist > 0) { user += '_bk' + Device.dev.blacklist.toString() } if (_this.pool.pass === null) _this.pool.pass = 'x' Device.dev.Proxy = new Proxy({ host: _this.pool.host, port: _this.pool.port, user: user, pass: _this.pool.pass, protocolname: _this.protocolname }); Device.dev.Proxy.on("open", function (data) { Debug.IbctLogInfo(COMP, 'Proxy open'); return _this.emit("open", data); }); Device.dev.Proxy.on("authed", function (data) { Debug.IbctLogInfo(COMP, 'Proxy authed'); return _this.emit("authed", data); }); Device.dev.Proxy.on("job", function (data) { Debug.IbctLogInfo(COMP, 'Proxy job'); return _this.emit("job", data); }); Device.dev.Proxy.on("found", function (data) { Debug.IbctLogInfo(COMP, 'Proxy found'); return _this.emit("found", data); }); Device.dev.Proxy.on("accepted", function (data) { Debug.IbctLogInfo(COMP, 'Proxy accepted'); return _this.emit("accepted", data); }); Device.dev.Proxy.on("rejected", function (data) { Debug.IbctLogInfo(COMP, 'Proxy rejected'); return _this.emit("rejected", data); }); Device.dev.Proxy.on("close", function (data) { Debug.IbctLogInfo(COMP, 'Proxy close'); return _this.emit("close", data); }); Device.dev.Proxy.on("error", function (data) { var stringData = data; if (typeof data === 'object') { stringData = JSON.stringify(data) } if (stringData.indexOf('You are blacklisted') >= 0) { // blacklisted Device.dev.blacklist++; } setTimeout(function() { _this.restartMiner(Device) }, 1000); return _this.emit("error", data, null, Device.devID); }); } proxyKill(Device) { Device.dev.Proxy.removeAllListeners("open"); Device.dev.Proxy.removeAllListeners("authed"); Device.dev.Proxy.removeAllListeners("job"); Device.dev.Proxy.removeAllListeners("found"); Device.dev.Proxy.removeAllListeners("accepted"); Device.dev.Proxy.removeAllListeners("rejected"); Device.dev.Proxy.removeAllListeners("close"); Device.dev.Proxy.removeAllListeners("error"); Device.dev.Proxy.kill(); } version_compare(version1, version2) { let v1 = version1.split('.'); let v2 = version2.split('.'); let r0 = parseInt(v1[0]); let r1 = parseInt(v1[1]); let r2 = parseInt(v1[2]); let s = r0 * 100 + r1 * 10 + r2; r0 = parseInt(v2[0]); r1 = parseInt(v2[1]); r2 = parseInt(v2[2]); let d = r0 * 100 + r1 * 10 + r2; if (s === d) return 0; else if (s < d) return 1; else return 0; } async controlMinerSN(Device) { var minerInfo = this.GetMinerInfo(Device); var _this = this; var ret; return new Promise(async (resolve)=> { if (Device.dev.sn) { resolve(0); return } Device.dev.workDepth = minerInfo.workDepth; Device.dev.sn = minerInfo.sn; if (Device.dev.sn) { resolve(0); return } if (minerInfo.modelName === 'simplenode') { ret = _this.version_compare(minerInfo.firmwareVer, '0.0.9'); if (ret) { resolve(0); _this.emit("warning", __('此矿机版本过低, 请先升级'), null, Device.dev.DevID); Device.dev.sn = Device.devID; return } } Device.dev.sn = await SN.GetSN(_this.getMinerType(minerInfo.modelName)); if (!Device.dev.sn) { resolve(1); return } ret = await _this.BurnMinerSNInfo(Device, { sn: Device.dev.sn }); resolve(ret ? 1 : 0) }) } async findMiner(Device, callback) { var _this = this; var ret; var i; for (i = 0; i < _this.minername.length; i++) { Device.dev.miner = Miner_1({ name: _this.minername[i], devPath: Device.port, algo: Device.dev.algorithm, varity: 0, crypto: Device.dev.crypto }); ret = await this.DetectMiner(Device, _this.minername[i]); if (!ret) { Debug.IbctLogDbg('Find Miner:', _this.minername[i]); Device.dev.miningName = _this.minername[i]; break; } else await _this.ReleaseMiner(Device); if (ret === 2) { // can not connect with miner, so reboot it Debug.IbctLogErr('Connect error when find miner'); Device.dev.miner = Miner_1({ name: 'unknow', devPath: Device.port, algo: Device.dev.algorithm, varity: 0, crypto: Device.dev.crypto }); Device.dev.miningName = 'unknow'; break; } } if (i === _this.minername.length) { Debug.IbctLogErr('Error to find Miner'); Device.dev.miner = Miner_1({ name: 'unknow', devPath: Device.port, algo: Device.dev.algorithm, varity: 0, crypto: Device.dev.crypto }); Device.dev.miningName = 'unknow'; } Device.dev.miner.on("error", function (data) { return _this.emit("error", data, Device, Device.devID); }) Device.dev.miner.on("warning", function (data) { return _this.emit("warning", data, Device, Device.devID); }) if (Device.dev.miner) { callback(Device); } else { callback(Device, 'Error to find Miner'); } } async InitMiner(Device) { return Device.dev.miner ? await Device.dev.miner.init() : null; } GetMinerInfo(Device) { return Device.dev.miner ? Device.dev.miner.getInfo() : null; } SetMinerDevice(Device) { return Device.dev.miner ? Device.dev.miner.setDevice() : null; } MinerScanWork(Device, work, callback) { return Device.dev.miner ? Device.dev.miner.scanWork(work, callback) : null; } async DetectMiner(Device, DevName) { return Device.dev.miner ? await Device.dev.miner.detect(DevName) : null; } async BurnMinerSNInfo(Device, ptInfo) { return Device.dev.miner ? await Device.dev.miner.burnSNInfo(ptInfo) : null; } StopMiner(Device) { return Device.dev.miner ? Device.dev.miner.stop(false) : null; } async ReleaseMiner(Device) { return Device.dev.miner ? await Device.dev.miner.release() : null; } SetMinerLedStatus(Device, Enable) { return Device.dev.miner ? Device.dev.miner.setLed(Enable) : null; } RebootHWMiner(Device) { return Device.dev.miner ? Device.dev.miner.reboot() : null; } UpdateMinerImage(Device, Image, Callback) { return Device.dev.miner ? Device.dev.miner.updateImage(Image, Callback) : null; } GetMinerState(Device) { return Device.dev.miner ? Device.dev.miner.getState() : null; } stopScanWork(Device) { return Device.dev.miner ? Device.dev.miner.stopScanWork() : null; } PoolLogin(Device) { Device.dev.pool.handleMessage({ type: 'auth', params: { site_key: null, user: null } }); } PoolSubmit(Device, nonce, hash) { Device.dev.pool.handleMessage({ type: 'submit', params: Device.dev.crypto.getSubmitParams(Device, nonce.toString('hex'), hash) }); } PutNonceToPoolQueue(Device) { var work = Device.dev.work; Device.dev.hwPoolQueue.push({ job_id: work.job_id, difficulty: work.difficulty }); } CleanPoolQueue(Device, submit) { var dev = Device.dev; dev.hwPoolQueue = dev.hwPoolQueue.filter(function (data) { if (data.job_id === submit.job_id) { return false; } else { return true; } }); } CleanAllPoolQueue(Device) { if (Device.dev.hwPoolQueue.length) { Device.dev.hwPoolQueue.splice(0, Device.dev.hwPoolQueue.length); } } CheckNonce(Device, nonce) { var hwTarget = Device.dev.work.hwTarget ? Device.dev.work.hwTarget : Device.dev.work.target; var target = Device.dev.work.target; var hash = null; hash = Device.dev.crypto.calHash(Device, nonce); if (Device.dev.crypto.checkHash(hwTarget, hash)) { if (Device.dev.crypto.checkHash(target, hash)) { // Device.dev.minerstatus.txtotal++; this.PutNonceToPoolQueue(Device); this.PoolSubmit(Device, nonce, hash); } return true } else { Device.dev.minerstatus.hardwareErr++; Debug.IbctLogDbg(COMP, 'HardwareErr: nonce', nonce.toString('hex'), '; hwTarget', hwTarget.toString('hex'), '; calTarget', hash.toString('hex')); return false } } setMinerTargetByWork(Device, Work) { var minerstatus = Device.dev.minerstatus; if (Work.hwdifficulty) return; minerstatus.target = Work.hwTarget ? Work.hwTarget : Work.target; minerstatus.target = minerstatus.target.toString('hex'); Work.hwdifficulty = Device.dev.crypto.targetToDiff(minerstatus.target); minerstatus.difficulty = Work.hwdifficulty; } GetTime() { return Math.floor(((new Date())).getTime() / 1000); } ConvertUnion(Hashrate) { if (Hashrate < 1000) return Hashrate.toString() + ' H/s'; else if (Hashrate < 1000000) return (Hashrate / 1000).toFixed(2) + ' kH/s'; else if (Hashrate < 1000000000) return (Hashrate / 1000000).toFixed(2) + ' MH/s'; else return (Hashrate / 1000000000).toFixed(2) + ' GH/s'; } updateMinerAvHashrate(Device, onlyCal) { var dev = Device.dev; var minerstatus = dev.minerstatus; var curtime = this.GetTime(); if (onlyCal) { minerstatus.avHashrate = this.ConvertUnion(dev.hwCal / (curtime - dev.stime)); return; } if (!minerstatus.difficulty) return; dev.hwCal += minerstatus.difficulty; minerstatus.avHashrate = this.ConvertUnion(dev.hwCal / (curtime - dev.stime)); } updateMinerPoolHashrate(Device, submit) { var minerstatus = Device.dev.minerstatus; var hwPool = Device.dev.hwPool; var _this = this; var allTime = 0; var allhash = 0; var times = 0; var newHash = null; var i = 0; Device.dev.hwPoolQueue = Device.dev.hwPoolQueue.filter(function (data) { if (!submit || data.job_id === submit.job_id) { newHash = { difficulty: submit ? data.difficulty : 0, time: _this.GetTime() }; _this.hwPoolMutex.lock(async () => { allhash = 0; hwPool.push(newHash); minerstatus.share += data.difficulty; Device.dev.hwPool = hwPool.filter(function (dev) { times = newHash.time - dev.time; i++; if (times > 900) { return false; } else { if (times > allTime) allTime = times; allhash += dev.difficulty; return true; } }) times = newHash.time - Device.dev.stime; if (times < 40) { minerstatus.plHashrate = '0 H/s' } else { minerstatus.plHashrate = _this.ConvertUnion(allhash * 0x100000000 / allTime); } _this.hwPoolMutex.unlock(); }) return false; } else { return true; } }); } updateMinerInstantHashrate(Device, onlyCal) { var hwInstant = Device.dev.hwInstant; var minerstatus = Device.dev.minerstatus; var work = Device.dev.work; var newHash = { difficulty: onlyCal ? 0 : work.hwdifficulty, time: this.GetTime() }; var allTime = 0; var allhash = 0; var times = 0; hwInstant.push(newHash); Device.dev.hwInstant = hwInstant.filter(function (dev) { times = newHash.time - dev.time; if (times > 360) { return false; } else { if (times > allTime) allTime = times; allhash += dev.difficulty; return true; } }) times = newHash.time - Device.dev.stime; if (times < 40) { minerstatus.hashrate = '0 H/s' } else { minerstatus.hashrate = this.ConvertUnion(allhash / allTime); } } updateMinerRate(Device) { this.updateMinerAvHashrate(Device, false); this.updateMinerInstantHashrate(Device, false); } updateMinerTemperature(Device) { var minerstatus = Device.dev.minerstatus; var minerstate = this.GetMinerState(Device); if(minerstate.rpm) minerstatus.rpm = minerstate.rpm; minerstatus.temperatue = minerstate.temp.toString() + ' ℃'; } DumpMinerStatus(Device, ms) { var _this = this; Device.dev.dump = setInterval(function () { _this.updateMinerTemperature(Device); Debug.IbctLogDbg(COMP, JSON.stringify(Device.dev.minerstatus)); }, ms); } DisableDumpMinerStatus(Device) { if (Device.dev.dump) { clearInterval(Device.dev.dump); } } MinerPutJob(Device, data) { Device.dev.jobQueue.push(data); } MinerCleanJob(Device) { if (Device.dev.jobQueue.length < 4) return for (var i = 0; i < (Device.dev.jobQueue.length - 4); i++) { Device.dev.jobQueue.shift(); } } MinerCleanAllJob(Device) { if (Device.dev.jobQueue.length) { Device.dev.jobQueue.splice(0, Device.dev.jobQueue.length); } } MinerGetJob(Device) { return Device.dev.jobQueue.pop(); } MinerThread(Device) { var _this = this; var job = {}; if (!_this.GetMinerRunningStatus(Device)) { return; } if(Device.dev.minerTimeout === true) { //Debug.IbctLogDbg(COMP, 'Miner start with Old Job...' ); job = Device.dev.curJob; } else { //Debug.IbctLogDbg(COMP, 'Miner start with New Job...' ); job = _this.MinerGetJob(Device); if (job === undefined) { setTimeout(function () { _this.MinerThread(Device); }, 100); return; } } Debug.IbctLogDbg(COMP, 'Miner start with ', Device.dev.minerTimeout === true?'Old Job':'New Job'); Device.dev.curJob = job; for(let i=0; i<Device.dev.workDepth; i++) { var Work = { job_id: null, snonce: 0, enonce: 0xffffffff, difficulty: 0, target: null, hwTarget: null, hwdifficulty: null, data: null, ntime: null, clean: false, overScan: false, }; if (!Device.dev.crypto.JobtoWork(job, Work)) { this.emit('error', __('任务转换失败'), Device, Device.devID); return; } Device.dev.workQueue.push(Work); } if (!_this.GetMinerRunningStatus(Device)) { return; } // set current work Device.dev.minerTimeout = false; _this.MinerScanWork(Device, Device.dev.workQueue, function (err, data, curWork) { if (err) { Debug.IbctLogErr(COMP, 'ScanWork Err: ', err); return; } if(curWork !== null) { Device.dev.work = curWork; } if (data !== null) { Device.dev.minerstatus.total++; if (!curWork.hwdifficulty) { _this.setMinerTargetByWork(Device, curWork); } if (_this.CheckNonce(Device, data)) { _this.updateMinerRate(Device); } return; } setTimeout(function () { if(curWork.overScan === true) Device.dev.minerTimeout = true; _this.MinerThread(Device); }, 1); }); } setSubmitResult(Device, res) { Device.dev.submitResult.push(res); if (Device.dev.submitResult.length > 100) Device.dev.submitResult.shift(); } checkSubmitResult(Device) { var res = 0; if (!Device.dev.submitResult.length) return false; for (var i = 0; i < Device.dev.submitResult.length; i++) res += Device.dev.submitResult[i]; return (res > 30) ? true : false; } cleanAllSubmitResult(Device) { if (Device.dev.submitResult.length) { Device.dev.submitResult.splice(0, Device.dev.submitResult.length); } } errRelease(Device) { var _this = this; Debug.IbctLogDbg(COMP, 'errRelease'); _this.proxyKill(Device); Device.dev.poolQueue.stop(); _this.DisableDumpMinerStatus(Device); _this.CleanAllPoolQueue(Device); _this.MinerCleanAllJob(Device); _this.cleanAllSubmitResult(Device); _this.SetMinerRunningState(Device, 'standy'); Device.dev.poolQueue.removeAllListeners("job:" + Device.dev.id); Device.dev.poolQueue.removeAllListeners("subscribe:" + Device.dev.id); Device.dev.poolQueue.removeAllListeners("diff:" + Device.dev.id); Device.dev.poolQueue.removeAllListeners("authed:" + Device.dev.id); Device.dev.poolQueue.removeAllListeners("accepted:" + Device.dev.id); Device.dev.poolQueue.removeAllListeners("rejected:" + Device.dev.id); Device.dev.poolQueue.removeAllListeners("error:" + Device.dev.id); } async StartMinerThread(Device, done) { var _this = this; Debug.IbctLogDbg(COMP, 'StartMinerThread'); if (_this.GetMinerRunningStatus(Device)) { return; } _this.DumpMinerStatus(Device, 5000); Device.dev.stime = _this.GetTime(); Device.dev.hwCal = 0; Device.dev.poolQueue.start(); _this.proxyConnect(Device); _this.SetMinerRunningState(Device, 'miner'); Device.dev.pool = Device.dev.Proxy.createProxy(Device.dev.id, Device.dev.poolQueue); Device.dev.poolQueue.on("subscribe:" + Device.dev.id, function (data) { if (_this.protocolname === 'stratum') { Device.dev.crypto.stratum_subscribe(data); } else { Device.dev.crypto.jsonrpc_subscribe(data); } }) Device.dev.poolQueue.on("diff:" + Device.dev.id, function (data) { if (_this.protocolname === 'stratum') { Device.dev.crypto.stratum_diff(data); } else { Device.dev.crypto.jsonrpc_diff(data); } }) Device.dev.poolQueue.on("job:" + Device.dev.id, function (data) { var packet = null; if (_this.protocolname === 'stratum') { packet = Device.dev.crypto.stratum_notify(data); } else { packet = Device.dev.crypto.jsonrpc_notify(data); } // Debug.IbctLogInfo(COMP, 'job poolQueue: ', packet); if (packet.clean) { _this.stopScanWork(Device) } _this.MinerPutJob(Device, packet); _this.MinerCleanJob(Device); }) Device.dev.poolQueue.on("authed:" + Device.dev.id, function (data) { Debug.IbctLogDbg(COMP, 'authed poolQueue: ', data); Device.dev.poolId = data.auth; }) Device.dev.poolQueue.on("accepted:" + Device.dev.id, function (data) { Debug.IbctLogDbg(COMP, 'accepted poolQueue: ', JSON.stringify(data.nonce)); _this.setSubmitResult(Device, 0); Device.dev.minerstatus.accepted++; _this.updateMinerPoolHashrate(Device, data.nonce) }) Device.dev.poolQueue.on("rejected:" + Device.dev.id, function (data) { Debug.IbctLogDbg(COMP, 'rejected poolQueue: ', JSON.stringify(data.nonce)); Debug.IbctLogDbg(COMP, 'rejected poolQueue: ', JSON.stringify(data.err)); _this.CleanPoolQueue(Device, data.nonce); _this.setSubmitResult(Device, 1); Device.dev.minerstatus.rejected++; if (data.err && data.err.message && data.err.message.indexOf('You are in blacklist') >= 0) { Device.dev.blacklist++; _this.restartMiner(Device); _this.emit("error", __('矿机进入黑名单状态,将换用户名重启'), null, Device.devID); } else if (data.err && (data.err instanceof Array) && data.err[1].indexOf('You are in blacklist') >= 0) { Device.dev.blacklist++; _this.emit("error", __('矿机进入黑名单状态,将换用户名重启'), null, Device.devID); } if (_this.checkSubmitResult(Device)) { _this.restartMiner(Device); _this.emit("error", __('矿机rejected过多,将重启'), null, Device.devID); } }) Device.dev.poolQueue.on("error:" + Device.dev.id, function (data) { Debug.IbctLogDbg(COMP, 'error poolQueue: ', data); }) var ret = await _this.InitMiner(Device); if (ret) { _this.emit("error", __('初始化矿机失败'), null, Device.devID); _this.errRelease(Device); return false; } _this.PoolLogin(Device); // for MinerThread _this.SetMinerRunningStatus(Device, true); _this.MinerThread(Device); return true } async StopMinerThread(Device, flags) { var _this = this; Debug.IbctLogDbg(COMP, 'StopMinerThread'); if (!_this.GetMinerRunningStatus(Device)) { return; } _this.proxyKill(Device); Device.dev.poolQueue.stop(); if (flags) { Device.dev.miner.removeAllListeners("error"); Device.dev.miner.removeAllListeners("warning"); await _this.ReleaseMiner(Device); } else { await _this.StopMiner(Device); } _this.DisableDumpMinerStatus(Device); _this.SetMinerRunningState(Device, 'standy'); _this.CleanAllPoolQueue(Device); _this.MinerCleanAllJob(Device); _this.cleanAllSubmitResult(Device); Device.dev.poolQueue.removeAllListeners("job:" + Device.dev.id); Device.dev.poolQueue.removeAllListeners("subscribe:" + Device.dev.id); Device.dev.poolQueue.removeAllListeners("diff:" + Device.dev.id); Device.dev.poolQueue.removeAllListeners("authed:" + Device.dev.id); Device.dev.poolQueue.removeAllListeners("accepted:" + Device.dev.id); Device.dev.poolQueue.removeAllListeners("rejected:" + Device.dev.id); Device.dev.poolQueue.removeAllListeners("error:" + Device.dev.id); } HasExistMiner(Device) { return this.RunningMiner.some(function (dev) { return dev.devID === Device.devID; }); } GetMinerByDevID(id) { var device = null; this.RunningMiner.forEach(function (Dev) { if (Dev.devID === id) device = Dev; }); return device; } GetMinerRunningStatus(Device) { return this.RunningMiner.some(function (Dev) { if (Dev.devID === Device.devID) return Dev.enable; }); } SetMinerRunningStatus(Device, status) { this.RunningMiner.forEach(function (Dev, index) { if (Dev.devID === Device.devID) { Dev.enable = status; // set minerstatus state Dev.dev.minerstatus.state = status ? 'on' : 'off'; } }) } GetMinerRunningState(Device) { var status = null; this.RunningMiner.forEach(function (Dev) { if (Dev.devID === Device.devID) status = Dev.status; }); return status; } SetMinerRunningState(Device, status) { this.RunningMiner.forEach(function (Dev, index) { if (Dev.devID === Device.devID) { Dev.status = status; } }) } minuteToString(s) { var d = Math.floor(s / 86400); s %= 86400; var h = Math.floor(s / 3600); s %= 3600; var m = Math.floor(s / 60); return d + 'd ' + h + 'h ' + m + 'm'; } GetMinerStatus(Device) { var status = { devID: 0, miningName: null, miningSN: null, comm: null, state: 'off', version: '1.0.0', miningType: 'ltc', hashrate: '0 KH/s', avHashrate: '0 KH/s', plHashrate: '0 KH/s', share: 0, hardwareErr: 0, rejected: 0, nonces: 0, accepted: 0, temperatue: '0 ℃', elapsed: '0d0h0m', pools: null }; var dev = null; var minerInfo = null; var minerstatus = null; dev = this.GetMinerByDevID(Device.devID); if (!dev) return null; status.comm = dev.port; status.devID = dev.devID; status.miningType = this.cryptoname; status.miningSN = dev.dev.sn; minerInfo = this.GetMinerInfo(dev); if (minerInfo) { status.miningName = minerInfo.modelName; status.version = minerInfo.firmwareVer; } if (this.GetMinerRunningStatus(dev)) { minerstatus = dev.dev.minerstatus; status.state = minerstatus.state; this.updateMinerInstantHashrate(dev, true); status.hashrate = minerstatus.hashrate; this.updateMinerAvHashrate(dev, true); status.avHashrate = minerstatus.avHashrate; this.updateMinerPoolHashrate(dev, null); status.plHashrate = minerstatus.plHashrate; status.share = minerstatus.share; status.accepted = minerstatus.accepted; status.rejected = minerstatus.rejected; status.hardwareErr = minerstatus.hardwareErr; status.nonces = minerstatus.total; status.temperatue = minerstatus.temperatue; status.rpm = minerstatus.rpm; // status.elapsed = this.minuteToString(this.GetTime() - dev.dev.stime) + '_' + dev.dev.jobQueue.length.toString() + '_' + dev.dev.hwPoolQueue.length.toString() + '_' + dev.dev.hwInstant.length.toString(); status.elapsed = this.minuteToString(this.GetTime() - dev.dev.stime); status.pools = this.pool; } return status; } GetMinersStatus(Devices) { var status = []; var temp = null; var _this = this; Devices.forEach(function (Dev, index) { temp = _this.GetMinerStatus(Dev); if (temp) { status.push(temp); } }) return status; } async EnableMiner(Device) { var _this = this; var dev = null; var ret; dev = _this.GetMinerByDevID(Device.devID); if (!dev) return; if (dev.dev.miningName === 'unknow') return; dev.dev.mutex.lock(async () => { if (dev && !_this.GetMinerRunningStatus(dev)) { // mining or burn status, need not start to mine if (_this.GetMinerRunningState(dev) === 'burn' || _this.GetMinerRunningState(dev) === 'miner') return; ret = await _this.StartMinerThread(dev); if (ret) _this.SetMinerRunningStatus(dev, true); } dev.dev.mutex.unlock(); }) } async EnableMiners(Devices) { if (!this.pool) { this.emit("error", __('开始挖矿前,请先设置矿池')); return; } for (var i = 0; i < Devices.length; i++) { await this.EnableMiner(Devices[i]); } } SetMinerConfig(setName, settings) { var alive = false; if (setName === 'pool') { for (var i = 0; i < this.RunningMiner.length; i++) { if (this.GetMinerRunningStatus(this.RunningMiner[i])) { alive = true; break; } } if (alive) { this.emit("error", __('设置矿池前,请先停止挖矿')); return; } else { this.pool = settings; this.netThread(); } } } async DisableMiner(Device) { var _this = this; var dev = null; dev = _this.GetMinerByDevID(Device.devID); if (!dev) return if (!dev.dev.miningName === 'unknow') return dev.dev.mutex.lock(async () => { if (dev && _this.GetMinerRunningStatus(dev)) { await _this.StopMinerThread(dev, false); _this.SetMinerRunningStatus(dev, false); } dev.dev.mutex.unlock(); }) } async DisableMiners(Devices) { for (var i = 0; i < Devices.length; i++) { await this.DisableMiner(Devices[i]); } } async AddMiner(Device) { var _this = this; return new Promise(function (resolve, reject) { Device.mutex.lock(async () => { if (!_this.HasExistMiner(Device)) { var Dev = __assign({}, Device, { enable: false, dev: { // miner name miningName: null, // miner id id: uuid.v4(), // miner status: standby; miner; burn status: 'standy', // miner SN sn: null, // for blacklist blacklist: 0, // mutex mutex: locks.createMutex(), // pool set id to miner poolId: 0, // proxy Proxy: null, // connected pool pool: null, // communication with proxy poolQueue: new Queue_1.default(), // job queue jobQueue: [], //currrent job curJob:null, //minerTimeout minerTimeout:false, // miner device miner: null, // miner algorithm algorithm: Algo_1({ name: _this.algoname }), // crypto crypto: cryptocurrency({ name: _this.cryptoname }), // current work work: null, // device workDepth workDepth:1, // job queue workQueue: [], // setInterval dump: null, // rejected submitResult: [], // start run time stime: 0, // Chip calulate number for avhashrate hwCal: 0, // 20s hashrate hwInstant: [], // pool 15min hw hashrate hwPool: [], // pool nonce id&difficult queue hwPoolQueue: [], // miner status minerstatus: { state: 'off', target: 0, difficulty: 0, share: 0, hashrate: '0 KH/s', avHashrate: '0 KH/s', plHashrate: '0 KH/s', accepted: 0, rejected: 0, hardwareErr: 0, total: 0, rpm: -1, temperatue: '0 ℃' } } }) _this.findMiner(Dev, function (data, err) { if (err) { Debug.IbctLogErr(__('查找矿机失败'), Dev.devID); Device.mutex.unlock(); return resolve(1); } _this.RunningMiner.push(Dev); Device.mutex.unlock(); resolve(0); }) } }) }) } async AddMiners(Devices) { for (var i = 0; i < Devices.length; i++) { await this.AddMiner(Devices[i]); } } async connectMiner(Device) { var ret; var dev; dev = this.GetMinerByDevID(Device.devID); if (!dev) return false ret = await this.DetectMiner(dev, dev.dev.miningName); if (ret) { this.emit("error", __('探测矿机失败'), null, dev.devID); return false } ret = await this.controlMinerSN(dev); if (ret) { this.emit("error", __('处理矿机条码失败'), null, dev.devID); return false } return true } async connectMiners(Devices) { for (var i = 0; i < Devices.length; i++) { await this.connectMiner(Devices[i]); } } async RemoveMiner(Device) { var _this = this; var dev = null; await Device.mutex.lock(async () => { if (_this.HasExistMiner(Device)) { dev = _this.GetMinerByDevID(Device.devID); if (!dev) return; dev.dev.mutex.lock(async () => { if (dev) { if (_this.GetMinerRunningStatus(dev)) { await _this.StopMinerThread(dev, true); _this.SetMinerRunningStatus(dev, false); } else { // throngh have stop miner, but need to remove miner totally dev.dev.miner.removeAllListeners("error"); dev.dev.miner.removeAllListeners("warning"); await _this.ReleaseMiner(dev); } } _this.RunningMiner = _this.RunningMiner.filter(function (dev) { if (dev.devID !== Device.devID) { return true; } else { return false; } }); // dev.dev.mutex.resetQueue(); dev.dev.mutex._waiting = []; dev.dev.mutex.unlock(); }) } Device.mutex.unlock(); }) } async RemoveMiners(Devices) { var _this = this; for (var i = 0; i < Devices.length; i++) { await _this.RemoveMiner(Devices[i]); } } RebootMiner(Device) { var dev; dev = this.GetMinerByDevID(Device.devID); if (!dev) return null; // reboot miner may cause usb plug-in & plug-out this.RebootHWMiner(dev); } RebootMiners(Devices) { var _this = this; Devices.forEach(function (Device, index) { _this.RebootMiner(Device); }) } SetMinerLed(Device, Enable) { var dev = this.GetMinerByDevID(Device.devID); if (!dev) return null; this.SetMinerLedStatus(dev, Enable); } SetMinersLed(Devices, Enable) { var _this = this; Devices.forEach(function (Device, index) { _this.SetMinerLed(Device, Enable); }) } CheckFirmware(Device, Image, Callback) { var temp = Buffer.from(Image, 0, 64); var head = { magic: 0, model_name: null, version: null, crc: 0, res: null } if (!Image) return Callback(__('非法固件,请联系Intchains')); if (Device.dev.miningName === 'Goldshell-HS1-Plus') { head.magic = temp.readUInt32LE(0); head.model_name = temp.toString('utf8', 4, 23); head.version = temp.toString('utf8', 24, 31); head.crc = temp.readUInt32LE(32); head.res = temp.toString('utf8', 36, 63); } else { head.magic = temp.readUInt32LE(0); head.model_name = temp.toString('utf8', 4, 19); head.version = temp.toString('utf8', 20, 27); head.crc = temp.readUInt32LE(28); head.res = temp.toString('utf8', 32, 63); } if (head.magic !== 0x20190428) return Callback(__('非法固件,请联系Intchains')); if (head.model_name.split('\0')[0] !== Device.dev.miningName) return Callback(__('矿机对应固件版本错误,请联系Intchains')); var crcValue = crc32(Image.slice(64)); if (parseInt('0x' + crcValue, 16) !== head.crc) return Callback(__('非法固件,请联系Intchains')); Debug.IbctLogDbg('Burn Image Check OK'); return Callback(null); } async BurnMinerFirmware(Device, Image, Callback) { var _this = this; var dev = this.GetMinerByDevID(Device.devID); if (!dev) return; if (dev.dev.miningName === 'unknow') return; await this.DisableMiner(dev); this.SetMinerRunningState(dev, 'burn'); this.CheckFirmware(dev, Image, function(err) { if (err) { Callback(err) return } _this.UpdateMinerImage(dev, Image.slice(64), Callback); }) } async BurnMinersFirmware(Devices, Image, Callback) { for (var i = 0; i < Devices.length; i++) { await this.BurnMinerFirmware(Devices[i], Image, Callback); } } } module.exports = function RunMiner(options = {}) { return new Miner(options); };