intchains_ibctminer
Version:
```js const IntMiner = require('./src'); const Debug = require('./src/log')(); const fs = require('fs'); const COMP = '[SIPC]';
802 lines (768 loc) • 23.7 kB
JavaScript
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 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;
};
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.RunningMiner = [];
_this.crypto = cryptocurrency({
name: _this.cryptoname
});
if (!_this.crypto) {
this.emit('error', 'Get crypto Err');
}
_this.on('error', function (error, Device) {
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);
});
}
async ExitMiner() {
}
proxyConnect(Device) {
var _this = this;
var user = _this.pool.user;
if (_this.pool.user.includes('.')) {
user += ('_' + Device.devID);
} else {
user += ('.' + Device.devID);
}
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) {
_this.DisableMiner(Device);
setTimeout(function () {
_this.EnableMiner(Device);
}, 2000);
return _this.emit("error", data + ' About Miner ' + 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();
}
findMiner(Device, callback) {
var _this = this;
Device.dev.miner = Miner_1({
name: _this.minername,
devPath: Device.port,
algo: Device.dev.algorithm,
varity: 0,
crypto: _this.crypto
});
Device.dev.miner.on("error", function (data) {
return _this.emit("error", data, Device);
})
Device.dev.miner.on("warning", function (data) {
return _this.emit("warning", data);
})
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) {
return Device.dev.miner ? await Device.dev.miner.detect(this.minername) : null;
}
StopMiner(Device) {
return Device.dev.miner ? Device.dev.miner.stop() : 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) {
Device.dev.pool.handleMessage({
type: 'submit',
params: this.crypto.getSubmitParams(Device, nonce.toString('hex'))
});
}
PutNonceToPoolQueue(Device) {
var work = Device.dev.work;
Device.dev.hwPoolQueue.push({
job_id: work.job_id,
difficulty: work.difficulty
});
}
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 = this.crypto.calHash(Device, nonce);
if (this.crypto.checkHash(hwTarget, hash)) {
if (this.crypto.checkHash(target, hash)) {
// Device.dev.minerstatus.txtotal++;
this.PutNonceToPoolQueue(Device);
this.PoolSubmit(Device, nonce);
}
} else {
Device.dev.minerstatus.hardwareErr++;
Debug.IbctLogDbg(COMP, 'HardwareErr: nonce', nonce.toString('hex'), '; hwTarget', hwTarget.toString('hex'), '; calTarget', hash.toString('hex'));
}
}
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 = this.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) {
var dev = Device.dev;
var minerstatus = dev.minerstatus;
var curtime = this.GetTime();
if (!minerstatus.difficulty)
return;
dev.hwCal += minerstatus.difficulty;
minerstatus.avHashrate = this.ConvertUnion(dev.hwCal / (curtime - dev.stime));
}
updateMinerPoolHashrate(Device, submit) {
var dev = Device.dev;
var curtime = this.GetTime();
var minerstatus = Device.dev.minerstatus;
var _this = this;
dev.hwPoolQueue = dev.hwPoolQueue.filter(function (data) {
if (data.job_id === submit.job_id) {
dev.hwPool += data.difficulty;
minerstatus.plHashrate = _this.ConvertUnion(dev.hwPool / (curtime - dev.stime));
return false;
} else {
return true;
}
});
}
updateMinerInstantHashrate(Device) {
var hwInstant = Device.dev.hwInstant;
var minerstatus = Device.dev.minerstatus;
var work = Device.dev.work;
var newHash = {
difficulty: work.hwdifficulty,
time: this.GetTime()
};
var allTime = 0;
var allhash = 0;
var times = 0;
hwInstant.push(newHash);
hwInstant = hwInstant.filter(function (dev) {
times = newHash.time - dev.time;
if (times > 40) {
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);
this.updateMinerInstantHashrate(Device);
}
updateMinerTemperature(Device) {
var minerstatus = Device.dev.minerstatus;
var minerstate = this.GetMinerState(Device);
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);
}
MinerGetJob(Device) {
return Device.dev.jobQueue.pop();
}
MinerThread(Device) {
var _this = this;
var job = {};
var Work = {
job_id: null,
snonce: 0,
enonce: 0xffffffff,
difficulty: 0,
target: null,
hwTarget: null,
hwdifficulty: null,
data: null,
ntime: null,
clean: false
};
if (!_this.GetMinerRunningStatus(Device)) {
return;
}
job = _this.MinerGetJob(Device);
if (job === undefined) {
setTimeout(function () {
_this.MinerThread(Device);
}, 100);
return;
}
// Debug.IbctLogInfo(COMP, 'MinerThread', job);
if (!_this.crypto.JobtoWork(job, Work)) {
this.emit('error', 'Job to Work Error', Device);
return;
}
if (!_this.GetMinerRunningStatus(Device)) {
return;
}
// set current work
Device.dev.work = Work;
_this.MinerScanWork(Device, Work, function (err, data) {
if (err) {
Debug.IbctLogErr(COMP, 'ScanWork Err: ', err);
return;
}
if (data !== null) {
Device.dev.minerstatus.total++;
if (!Work.hwdifficulty) {
_this.setMinerTargetByWork(Device, Work);
}
_this.updateMinerRate(Device);
_this.CheckNonce(Device, data);
return;
}
setTimeout(function () {
_this.MinerThread(Device);
}, 50);
});
}
async StartMinerThread(Device, done) {
var _this = this;
Debug.IbctLogDbg(COMP, 'StartMinerThread');
if (_this.GetMinerRunningStatus(Device)) {
_this.StopMinerThread(Device);
}
_this.DumpMinerStatus(Device, 5000);
Device.dev.stime = _this.GetTime();
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("job:" + Device.dev.id, function (data) {
var packet = null;
if (_this.protocolname === 'stratum') {
packet = _this.crypto.stratum_notify(data);
} else {
packet = data;
}
// Debug.IbctLogInfo(COMP, 'job poolQueue: ', packet);
if (packet.clean) {
_this.stopScanWork(Device)
}
_this.MinerPutJob(Device, packet);
})
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));
Device.dev.minerstatus.accepted++;
_this.updateMinerPoolHashrate(Device, data.nonce)
})
Device.dev.poolQueue.on("rejected:" + Device.dev.id, function (data) {
Debug.IbctLogDbg(COMP, 'rejected poolQueue: ', data);
Device.dev.minerstatus.rejected++;
})
Device.dev.poolQueue.on("error:" + Device.dev.id, function (data) {
Debug.IbctLogDbg(COMP, 'error poolQueue: ', data);
})
var ret = await _this.DetectMiner(Device);
if (ret) {
_this.emit("error", 'Detect Miner ' + Device.devID + ' Error');
return;
}
ret = await _this.InitMiner(Device);
if (ret) {
_this.emit("error", 'Init Miner ' + Device.devID + ' Error');
return;
}
_this.MinerThread(Device);
_this.PoolLogin(Device);
}
StopMinerThread(Device) {
var _this = this;
Debug.IbctLogDbg(COMP, 'StopMinerThread');
if (!_this.GetMinerRunningStatus(Device)) {
return;
}
_this.proxyKill(Device);
Device.dev.poolQueue.stop();
_this.StopMiner(Device);
Device.dev.miner.removeAllListeners("error");
Device.dev.miner.removeAllListeners("warning");
_this.DisableDumpMinerStatus(Device);
_this.SetMinerRunningState(Device, 'standy');
Device.dev.poolQueue.removeAllListeners("job:" + 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,
state: 'off',
version: '1.0.0',
hashrate: '0 KH/s',
avHashrate: '0 KH/s',
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.devID = dev.devID;
minerInfo = this.GetMinerInfo(dev);
if (minerInfo) {
status.miningName = minerInfo.modelName;
status.version = minerInfo.firmwareVer;
}
if (this.GetMinerRunningStatus(Device)) {
minerstatus = dev.dev.minerstatus;
status.state = minerstatus.state;
status.hashrate = minerstatus.hashrate;
status.avHashrate = minerstatus.avHashrate;
status.plHashrate = minerstatus.plHashrate;
status.accepted = minerstatus.accepted;
status.rejected = minerstatus.rejected;
status.hardwareErr = minerstatus.hardwareErr;
status.nonces = minerstatus.total;
status.temperatue = minerstatus.temperatue;
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;
dev = _this.GetMinerByDevID(Device.devID);
if (dev && !_this.GetMinerRunningStatus(dev)) {
// mining or burn status, need not start to mine
if (_this.GetMinerRunningState(dev) === 'burn' || _this.GetMinerRunningState(dev) === 'miner')
return;
await _this.StartMinerThread(dev);
_this.SetMinerRunningStatus(dev, true);
}
}
async EnableMiners(Devices) {
if (!this.pool) {
this.emit("error", 'Set Pool First');
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", 'Disable Some Miners First');
return;
} else {
this.pool = settings;
}
}
}
async DisableMiner(Device) {
var _this = this;
var dev = null;
dev = _this.GetMinerByDevID(Device.devID);
if (dev && _this.GetMinerRunningStatus(dev)) {
await _this.StopMinerThread(dev);
_this.SetMinerRunningStatus(dev, false);
}
}
async DisableMiners(Devices) {
for (var i = 0; i < Devices.length; i++) {
await this.DisableMiner(Devices[i]);
}
}
AddMiner(Device) {
var _this = this;
if (!_this.HasExistMiner(Device)) {
var Dev = __assign({}, Device, {
enable: false,
dev: {
// miner id
id: uuid.v4(),
// miner status: standby; miner; burn
status: 'standy',
// 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: [],
// miner device
miner: null,
// miner algorithm
algorithm: Algo_1({
name: _this.algoname
}),
// current work
work: null,
// setInterval
dump: null,
// start run time
stime: 0,
// Chip calulate number for avhashrate
hwCal: 0,
// 20s hashrate
hwInstant: [],
// pool hw hashrate
hwPool: 0,
// pool nonce id&difficult queue
hwPoolQueue: [],
// miner status
minerstatus: {
state: 'off',
target: 0,
difficulty: 0,
hashrate: '0 KH/s',
avHashrate: '0 KH/s',
plHashrate: '0 KH/s',
accepted: 0,
rejected: 0,
hardwareErr: 0,
total: 0,
temperatue: '0 ℃'
}
}
})
_this.findMiner(Dev, function (data, err) {
if (err) {
_this.emit("error", 'Find Miner ' + data.devID + ' Error');
return;
}
_this.RunningMiner.push(Dev);
})
}
}
AddMiners(Devices) {
var _this = this;
Devices.forEach(function (Device, index) {
_this.AddMiner(Device);
})
}
RemoveMiner(Device) {
var _this = this;
if (_this.HasExistMiner(Device)) {
_this.RunningMiner = _this.RunningMiner.filter(function (dev) {
if (dev.devID !== Device.devID) {
return true;
} else {
if (_this.GetMinerRunningStatus(dev)) {
_this.DisableMiner(Device);
}
return false;
}
});
}
}
RemoveMiners(Devices) {
var _this = this;
Devices.forEach(function (Device, index) {
_this.DisableMiner(Device);
})
}
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(Image) {
var temp = Buffer.from(Image, 0, 64);
var head = {
magic: 0,
model_name: null,
version: null,
crc: 0,
res: null
}
if (!Image)
return false;
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);
Debug.IbctLogDbg(COMP, JSON.stringify(head));
if (head.magic !== 0x20190428)
return false;
if (head.model_name.slice(0, this.minername.length) !== this.minername)
return false;
var crcValue = crc32(Image.slice(64));
if (parseInt('0x' + crcValue, 16) !== head.crc)
return false;
Debug.IbctLogDbg('Burn Image Check OK');
return true;
}
BurnMinerFirmware(Device, Image, Callback) {
var dev = this.GetMinerByDevID(Device.devID);
if (!dev)
return;
if (this.GetMinerRunningStatus(dev)) {
this.DisableMiner(dev);
}
this.SetMinerRunningState(dev, 'burn');
if (!this.CheckFirmware(Image)) {
Callback('Check Firmware Error');
return;
}
this.UpdateMinerImage(dev, Image.slice(64), Callback);
}
BurnMinersFirmware(Devices, Image, Callback) {
var _this = this;
Devices.forEach(function (Device, index) {
_this.BurnMinerFirmware(Device, Image, Callback);
})
}
}
module.exports = function RunMiner(options = {}) {
return new Miner(options);
};