135 lines
4.1 KiB
JavaScript
135 lines
4.1 KiB
JavaScript
var events = require('events');
|
|
|
|
var binpack = require('binpack');
|
|
var bignum = require('bignum');
|
|
|
|
var util = require('./util.js');
|
|
var blockTemplate = require('./blockTemplate.js');
|
|
|
|
|
|
|
|
//Unique extranonce per subscriber
|
|
var ExtraNonceCounter = function(){
|
|
var instanceId = 31;
|
|
var counter = instanceId << 27;
|
|
var size = binpack.packUInt32(counter, 'big').length;
|
|
|
|
this.next = function(){
|
|
var extraNonce = binpack.packUInt32(counter++, 'big');
|
|
return extraNonce.toString('hex');
|
|
};
|
|
this.size = function(){
|
|
return size;
|
|
};
|
|
};
|
|
|
|
//Unique job per new block template
|
|
var JobCounter = function(){
|
|
var counter = 0;
|
|
|
|
this.next = function(){
|
|
counter++;
|
|
if (counter % 0xffff == 0)
|
|
counter = 1;
|
|
return counter.toString(16);
|
|
};
|
|
};
|
|
|
|
|
|
var JobManager = module.exports = function JobManager(options){
|
|
|
|
//private members
|
|
|
|
var _this = this;
|
|
var jobCounter = new JobCounter();
|
|
var jobs = {};
|
|
|
|
function CheckNewIfNewBlock(blockTemplate){
|
|
var newBlock = true;
|
|
for(var job in jobs){
|
|
if (jobs[job].rpcData.previousblockhash == blockTemplate.rpcData.previousblockhash)
|
|
newBlock = false;
|
|
}
|
|
if (newBlock)
|
|
_this.emit('newBlock', blockTemplate);
|
|
}
|
|
|
|
|
|
//public members
|
|
|
|
this.extraNonceCounter = new ExtraNonceCounter();
|
|
this.extraNoncePlaceholder = new Buffer('f000000ff111111f', 'hex');
|
|
this.extraNonce2Size = this.extraNoncePlaceholder.length - this.extraNonceCounter.size();
|
|
|
|
this.currentJob;
|
|
this.newTemplate = function(rpcData, publicKey){
|
|
this.currentJob = new blockTemplate(jobCounter.next(), rpcData, publicKey, _this.extraNoncePlaceholder);
|
|
jobs[this.currentJob.jobId] = this.currentJob;
|
|
CheckNewIfNewBlock(this.currentJob);
|
|
};
|
|
this.processShare = function(jobId, difficulty, extraNonce1Buffer, extraNonce2, nTime, nonce){
|
|
|
|
var submitTime = Date.now() / 1000 | 0;
|
|
|
|
if (extraNonce2.length / 2 !== _this.extraNonce2Size)
|
|
return {error: [20, 'incorrect size of extranonce2', null]};
|
|
|
|
|
|
var job = jobs[jobId];
|
|
if (!job)
|
|
return {error: [21, 'job not found', null]};
|
|
|
|
|
|
if (nTime.length !== 8)
|
|
return {error: [20, 'incorrect size of ntime']};
|
|
|
|
|
|
var nTimeInt = parseInt(nTime, 16);
|
|
if (nTimeInt < job.rpcData.curtime || nTime > submitTime + 7200)
|
|
return {error: [20, 'ntime out of range', null]};
|
|
|
|
|
|
if (nonce.length !== 8)
|
|
return {error: [20, 'incorrect size of nonce']};
|
|
|
|
|
|
if (!job.registerSubmit(extraNonce1Buffer, extraNonce2, nTime, nonce))
|
|
return {error: [22, 'duplicate share', null]};
|
|
|
|
|
|
var extraNonce2Buffer = new Buffer(extraNonce2, 'hex');
|
|
var nTimeBuffer = new Buffer(nTime, 'hex');
|
|
var nonceBuffer = new Buffer(nonce, 'hex');
|
|
|
|
|
|
var coinbaseBuffer = job.serializeCoinbase(extraNonce1Buffer, extraNonce2Buffer);
|
|
var coinbaseHash = util.doublesha(coinbaseBuffer);
|
|
|
|
|
|
var merkleRootBuffer = job.merkleTree.withFirst(coinbaseHash);
|
|
for (var i = 0; i < 8; i++)
|
|
merkleRootBuffer.writeUInt32LE(merkleRootBuffer.readUInt32BE(i * 4), i * 4);
|
|
|
|
|
|
var headerBuffer = job.serializeHeader(merkleRootBuffer, nTimeBuffer, nonceBuffer);
|
|
for (var i = 0; i < 20; i++) headerBuffer.writeUInt32LE(headerBuffer.readUInt32BE(i * 4), i * 4);
|
|
var headerHash = util.doublesha(headerBuffer);
|
|
var headerBigNum = bignum.fromBuffer(headerHash);
|
|
|
|
|
|
var targetUser = bignum.fromBuffer(
|
|
new Buffer('00000000ffff0000000000000000000000000000000000000000000000000000', 'hex')
|
|
).div(difficulty);
|
|
if (headerBigNum.gt(targetUser))
|
|
return {error: [23, 'low difficulty share', null]};
|
|
|
|
|
|
if (headerBigNum.gt(job.target)){
|
|
_this.emit('blockFound', job.serializeBlock(headerBuffer, coinbaseBuffer));
|
|
}
|
|
|
|
|
|
return true;
|
|
};
|
|
};
|
|
JobManager.prototype.__proto__ = events.EventEmitter.prototype; |