node-stratum-pool/jobManager.js
Matthew Little 7a21abacc4 updated
2014-01-07 03:23:56 -05:00

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;