From cb5d7c7bdc42b871a3f2bbc323e3d37e339433eb Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Wed, 15 Jan 2014 16:03:30 -0700 Subject: [PATCH] Add check for if submitblock was successful and refined the data emitted by onShare --- lib/daemon.js | 11 ++++- lib/jobManager.js | 40 ++++++++++++++----- lib/pool.js | 100 +++++++++++++++++++++++++++++++--------------- lib/stratum.js | 3 +- 4 files changed, 108 insertions(+), 46 deletions(-) diff --git a/lib/daemon.js b/lib/daemon.js index d652c04..67de845 100644 --- a/lib/daemon.js +++ b/lib/daemon.js @@ -78,8 +78,15 @@ function DaemonInterface(options){ data += chunk; }); res.on('end', function(){ - var dataJson = JSON.parse(data); - callback(dataJson.error, dataJson.result); + var dataJson; + try{ + dataJson = JSON.parse(data); + } + catch(e){ + _this.emit('error', 'daemon interface could not parse rpc data from method: ' + method + ' ' + options.hostname); + } + if (typeof(dataJson) !== 'undefined') + callback(dataJson.error, dataJson.result); }); }); diff --git a/lib/jobManager.js b/lib/jobManager.js index 17c4b90..726ad01 100644 --- a/lib/jobManager.js +++ b/lib/jobManager.js @@ -126,33 +126,45 @@ var JobManager = module.exports = function JobManager(options){ } }; - this.processShare = function(jobId, difficulty, extraNonce1, extraNonce2, nTime, nonce){ + this.processShare = function(jobId, difficulty, extraNonce1, extraNonce2, nTime, nonce, ipAddress, workerName){ + + + var shareError = function(error){ + _this.emit('share', { + job: jobId, + ip: ipAddress, + worker: workerName, + difficulty: difficulty, + error: error.error[1] + }); + return error; + }; var submitTime = Date.now() / 1000 | 0; if (extraNonce2.length / 2 !== _this.extraNonce2Size) - return {error: [20, 'incorrect size of extranonce2', null]}; + return shareError({error: [20, 'incorrect size of extranonce2']}); var job = this.currentJob; if ( job.jobId != jobId ) { - return {error: [21, 'job not found', null]}; + return shareError({error: [21, 'job not found']}); } if (nTime.length !== 8) { - return {error: [20, 'incorrect size of ntime']}; + return shareError({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]}; + return shareError({error: [20, 'ntime out of range']}); } if (nonce.length !== 8) { - return {error: [20, 'incorrect size of nonce']}; + return shareError({error: [20, 'incorrect size of nonce']}); } if (!job.registerSubmit(extraNonce1, extraNonce2, nTime, nonce)) { - return {error: [22, 'duplicate share', null]}; + return shareError({error: [22, 'duplicate share']}); } @@ -169,19 +181,27 @@ var JobManager = module.exports = function JobManager(options){ var headerBigNum = bignum.fromBuffer(headerHash, {endian: 'little', size: 32}); var blockHash; + var blockHex; if (job.target.ge(headerBigNum)){ - var blockHex = job.serializeBlock(headerBuffer, coinbaseBuffer).toString('hex'); + blockHex = job.serializeBlock(headerBuffer, coinbaseBuffer).toString('hex'); blockHash = util.reverseBuffer(util.doublesha(headerBuffer)).toString('hex'); - _this.emit('blockFound', blockHex, blockHash); } else { var targetUser = bignum(diffDividend / difficulty); if (headerBigNum.gt(targetUser)){ - return {error: [23, 'low difficulty share', null]}; + return shareError({error: [23, 'low difficulty share']}); } } + _this.emit('share', { + job: jobId, + ip: ipAddress, + worker: workerName, + difficulty: difficulty, + solution: blockHash + }, blockHex); + return {result: true, error: null, solution: blockHash}; }; }; diff --git a/lib/pool.js b/lib/pool.js index 553c53e..ee95b44 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -29,6 +29,32 @@ var pool = module.exports = function pool(options, authorizeFn){ })(); + function SubmitBlock(blockHex, callback){ + if (options.hasSubmitMethod) { + _this.daemon.cmd('submitblock', + [blockHex], + function(error, result){ + if (error) + emitErrorLog('submitblock', 'rpc error when submitting block with submitblock') + else + emitLog('submitblock', 'Submitted Block using submitblock'); + callback(); + } + ); + } else { + _this.daemon.cmd('getblocktemplate', + [{'mode': 'submit', 'data': blockHex}], + function(error, result){ + if (error) + emitErrorLog('submitblock', 'rpc error when submitting block with getblocktemplate') + else + emitLog('submitblock', 'Submitted Block using getblocktemplate'); + callback() + } + ); + } + } + function SetupJobManager(){ _this.jobManager = new jobManager({ @@ -42,21 +68,27 @@ var pool = module.exports = function pool(options, authorizeFn){ emitLog('system', 'Detected new block'); _this.stratumServer.broadcastMiningJobs(blockTemplate.getJobParams()); } - }).on('blockFound', function(blockHex, blockHash){ - if (options.hasSubmitMethod) { - _this.daemon.cmd('submitblock', - [blockHex], - function(error, result){ - emitLog('submitblock', 'Submitted Block using submitblock :'+blockHash); - } - ); - } else { - _this.daemon.cmd('getblocktemplate', - [{'mode': 'submit', 'data': blockHex}], - function(error, result){ - emitLog('submitblock', 'Submitted Block using getblocktemplate: '+blockHash); - } - ); + }).on('share', function(shareData, blockHex){ + var isValidShare = !shareData.error; + var isValidBlock = !!blockHex; + var emitShare = function(){ + _this.emit('share', isValidShare, isValidBlock, shareData); + }; + + /* + If we calculated that the block solution was found, + before we emit the share, lets submit the block, + then check if it was accepted using RPC getblock + */ + if (!isValidBlock) + emitShare(); + else{ + SubmitBlock(blockHex, function(){ + CheckBlockAccepted(shareData.solution, function(isAccepted){ + isValidBlock = isAccepted; + emitShare(); + }); + }); } }); } @@ -72,10 +104,10 @@ var pool = module.exports = function pool(options, authorizeFn){ [options.address], function(error, result){ if (error){ - emitLog('system','validateaddress rpc error'); + emitErrorLog('system','validateaddress rpc error'); callback(error); } else if (!result.isvalid) { - emitLog('system','address is not valid'); + emitErrorLog('system','address is not valid'); callback("address-not-valid"); } else { callback(error, result); @@ -111,6 +143,8 @@ var pool = module.exports = function pool(options, authorizeFn){ }).on('startFailed', function(){ emitErrorLog('system','Failed to start daemon'); + }).on('error', function(message){ + emitErrorLog('system', message); }); } @@ -148,25 +182,13 @@ var pool = module.exports = function pool(options, authorizeFn){ client.extraNonce1, params.extraNonce2, params.nTime, - params.nonce + params.nonce, + client.socket.remoteAddress, + params.name ); resultCallback(result.error, result.result ? true : null); - _this.emit('share', !result.error, { - job: params.jobId, - ip: client.socket.remoteAddress, - worker: params.name, - solution: result.solution, - error: result.error ? result.error[1] : undefined, - difficulty: client.difficulty, - timestamp: Date.now() / 1000 | 0, - accepted: !!result.result, - extraNonce2: params.extraNonce2, - nTime: params.nTime, - nonce: params.nonce - }); - }).on('malformedMessage', function (message) { emitWarningLog('client', client.workerName+" has sent us a malformed message: "+message); }).on('socketError', function() { @@ -202,7 +224,6 @@ var pool = module.exports = function pool(options, authorizeFn){ emitLog('system', 'Block polling setup for every ' + pollingInterval + ' milliseconds'); } - function GetBlockTemplate(callback){ _this.daemon.cmd('getblocktemplate', [{"capabilities": [ "coinbasetxn", "workid", "coinbase/append" ]}], @@ -217,6 +238,19 @@ var pool = module.exports = function pool(options, authorizeFn){ ); } + function CheckBlockAccepted(blockHash, callback){ + _this.daemon.cmd('getblock', + [blockHash], + function(error, result){ + if (error) + callback(false); + else if (result.hash === blockHash) + callback(true); + else + callback(false); + } + ); + } /** * This method is being called from the blockNotify so that when a new block is discovered by the daemon diff --git a/lib/stratum.js b/lib/stratum.js index d5d993a..3b12ec5 100644 --- a/lib/stratum.js +++ b/lib/stratum.js @@ -239,7 +239,8 @@ var StratumServer = exports.Server = function StratumServer(options){ var subscriptionCounter = SubscriptionCounter(); (function init(){ - _socketServer = socketServer = net.createServer(function(c){ + _socketServer = socketServer = net.createServer({allowHalfOpen: true}, function(c){ + c.setKeepAlive(true); var subscriptionId = subscriptionCounter.next(); var client = new StratumClient( {