Add check for if submitblock was successful and refined the data emitted by onShare

This commit is contained in:
Matthew Little 2014-01-15 16:03:30 -07:00
parent cbcaa1cf98
commit cb5d7c7bdc
4 changed files with 108 additions and 46 deletions

View File

@ -78,8 +78,15 @@ function DaemonInterface(options){
data += chunk; data += chunk;
}); });
res.on('end', function(){ res.on('end', function(){
var dataJson = JSON.parse(data); var dataJson;
callback(dataJson.error, dataJson.result); 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);
}); });
}); });

View File

@ -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; var submitTime = Date.now() / 1000 | 0;
if (extraNonce2.length / 2 !== _this.extraNonce2Size) 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; var job = this.currentJob;
if ( job.jobId != jobId ) { if ( job.jobId != jobId ) {
return {error: [21, 'job not found', null]}; return shareError({error: [21, 'job not found']});
} }
if (nTime.length !== 8) { if (nTime.length !== 8) {
return {error: [20, 'incorrect size of ntime']}; return shareError({error: [20, 'incorrect size of ntime']});
} }
var nTimeInt = parseInt(nTime, 16); var nTimeInt = parseInt(nTime, 16);
if (nTimeInt < job.rpcData.curtime || nTime > submitTime + 7200) { 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) { 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)) { 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 headerBigNum = bignum.fromBuffer(headerHash, {endian: 'little', size: 32});
var blockHash; var blockHash;
var blockHex;
if (job.target.ge(headerBigNum)){ 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'); blockHash = util.reverseBuffer(util.doublesha(headerBuffer)).toString('hex');
_this.emit('blockFound', blockHex, blockHash);
} }
else { else {
var targetUser = bignum(diffDividend / difficulty); var targetUser = bignum(diffDividend / difficulty);
if (headerBigNum.gt(targetUser)){ 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}; return {result: true, error: null, solution: blockHash};
}; };
}; };

View File

@ -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(){ function SetupJobManager(){
_this.jobManager = new jobManager({ _this.jobManager = new jobManager({
@ -42,21 +68,27 @@ var pool = module.exports = function pool(options, authorizeFn){
emitLog('system', 'Detected new block'); emitLog('system', 'Detected new block');
_this.stratumServer.broadcastMiningJobs(blockTemplate.getJobParams()); _this.stratumServer.broadcastMiningJobs(blockTemplate.getJobParams());
} }
}).on('blockFound', function(blockHex, blockHash){ }).on('share', function(shareData, blockHex){
if (options.hasSubmitMethod) { var isValidShare = !shareData.error;
_this.daemon.cmd('submitblock', var isValidBlock = !!blockHex;
[blockHex], var emitShare = function(){
function(error, result){ _this.emit('share', isValidShare, isValidBlock, shareData);
emitLog('submitblock', 'Submitted Block using submitblock :'+blockHash); };
}
); /*
} else { If we calculated that the block solution was found,
_this.daemon.cmd('getblocktemplate', before we emit the share, lets submit the block,
[{'mode': 'submit', 'data': blockHex}], then check if it was accepted using RPC getblock
function(error, result){ */
emitLog('submitblock', 'Submitted Block using getblocktemplate: '+blockHash); 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], [options.address],
function(error, result){ function(error, result){
if (error){ if (error){
emitLog('system','validateaddress rpc error'); emitErrorLog('system','validateaddress rpc error');
callback(error); callback(error);
} else if (!result.isvalid) { } else if (!result.isvalid) {
emitLog('system','address is not valid'); emitErrorLog('system','address is not valid');
callback("address-not-valid"); callback("address-not-valid");
} else { } else {
callback(error, result); callback(error, result);
@ -111,6 +143,8 @@ var pool = module.exports = function pool(options, authorizeFn){
}).on('startFailed', function(){ }).on('startFailed', function(){
emitErrorLog('system','Failed to start daemon'); 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, client.extraNonce1,
params.extraNonce2, params.extraNonce2,
params.nTime, params.nTime,
params.nonce params.nonce,
client.socket.remoteAddress,
params.name
); );
resultCallback(result.error, result.result ? true : null); 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) { }).on('malformedMessage', function (message) {
emitWarningLog('client', client.workerName+" has sent us a malformed message: "+message); emitWarningLog('client', client.workerName+" has sent us a malformed message: "+message);
}).on('socketError', function() { }).on('socketError', function() {
@ -202,7 +224,6 @@ var pool = module.exports = function pool(options, authorizeFn){
emitLog('system', 'Block polling setup for every ' + pollingInterval + ' milliseconds'); emitLog('system', 'Block polling setup for every ' + pollingInterval + ' milliseconds');
} }
function GetBlockTemplate(callback){ function GetBlockTemplate(callback){
_this.daemon.cmd('getblocktemplate', _this.daemon.cmd('getblocktemplate',
[{"capabilities": [ "coinbasetxn", "workid", "coinbase/append" ]}], [{"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 * This method is being called from the blockNotify so that when a new block is discovered by the daemon

View File

@ -239,7 +239,8 @@ var StratumServer = exports.Server = function StratumServer(options){
var subscriptionCounter = SubscriptionCounter(); var subscriptionCounter = SubscriptionCounter();
(function init(){ (function init(){
_socketServer = socketServer = net.createServer(function(c){ _socketServer = socketServer = net.createServer({allowHalfOpen: true}, function(c){
c.setKeepAlive(true);
var subscriptionId = subscriptionCounter.next(); var subscriptionId = subscriptionCounter.next();
var client = new StratumClient( var client = new StratumClient(
{ {