Added todolist

code cleanup
Added jobManager.blockHashHex which should generate the expected block hash but it seems to not work properly
Moved check for the share difficulty after the block check since it's useless to make the check there.
Changed emit event from client to client.connected and added client.disconnected
Changed share emit data. Now we pass the client instance to everyone listening to the share event.
Added mining.get_transaction to the handled events of stratumclient. It looks like bfgminer uses this.
changed StratumClient.sendDifficulty to StratumCLient.sendAndSetDifficulty which will also perform a check on the given difficulty and broadcasts the sendDifficulty message to the client Only if the new difficulty is different from the previous one.
Code cleanup and comments
This commit is contained in:
Andrea Baccega 2014-01-13 16:17:38 +01:00
parent 263972fcdc
commit a42fe87723
7 changed files with 131 additions and 67 deletions

11
TODOLIST.md Normal file
View File

@ -0,0 +1,11 @@
=== TODO ===
* vekexasia: handle socket error from client so that we could clean up stratumServers Client
* vekexasia: fix check of difficulty of the shares. Not sure why a lot of shares are marked as "low-submit-share"
=== TOFIX ===
* vekexasia: jobManager.js has implement the expected block hash after submitting it to the daemon. This will let us check if the block was accepted by the daemon.
=== DONE ===

View File

@ -1,8 +1,7 @@
var net = require('net');
var fs = require('fs');
var path = require('path');
var pool = require('../index.js');
var net = require('net');
var fs = require('fs');
var path = require('path');
var pool = require('../index.js');
var ShareManager = require('./shareManager.js').ShareManager;
var logRef = console.log;
@ -48,7 +47,7 @@ fs.readdir(confFolder, function(err, files){
console.log('Starting pool for ' + coin.options.name);
coin.pool = new pool(coin, authorizeFN );
coin.shareManager = new ShareManager(coin.pool);
var shareManager = new ShareManager(coin.pool);
coins.push(coin);
@ -75,7 +74,7 @@ fs.readdir(confFolder, function(err, files){
if (config.blockNotifyListener.enabled){
console.log("ENABLED");
console.log("blockNotifyListener ENABLED");
var blockNotifyServer = net.createServer(function(c) {
console.log('server connected');
var data = '';

View File

@ -11,28 +11,27 @@ var ShareManager = exports.ShareManager = function(pool) {
pool.on('share', function(isValid, data) {
if (isValid) {
handleValidShare(
data.workerName,
data.client,
data.blockHeaderHex,
data.jobId,
data.clientDifficulty,
data.extraNonce1,
data.extraNonce2,
data.nTime,
data.nonce);
} else {
handleInvalidShare(
data.workerName,
data.client,
data.error[0],
data.error[1]);
}
});
function handleValidShare(workerName, headerHex, jobId, clientDifficulty, extraNonce1, extraNonce2, nTime, nonce) {
console.log("A new Valid share from "+workerName+" has arrived! - "+headerHex);
function handleValidShare(client, headerHex, jobId, extraNonce1, extraNonce2, nTime, nonce) {
console.log("A new Valid share from "+client.workerName+" has arrived! - "+headerHex);
}
function handleInvalidShare(workerName, errorCode, errorDescription) {
console.log("Invalid share form "+workerName+" ErrorCode: "+errorCode+ " ErrorDescription: "+errorDescription);
function handleInvalidShare(client, errorCode, errorDescription) {
console.log("Invalid share form "+client.workerName+" ErrorCode: "+errorCode+ " ErrorDescription: "+errorDescription);
}
};

View File

@ -17,7 +17,6 @@ var pool = module.exports = function pool(coin, authFn){
var _this = this;
var publicKeyBuffer;
this.shareManager = undefined; // just for us to know that the variable should be this one.
this.jobManager = new jobManager({
algorithm: coin.options.algorithm,
address: coin.options.address
@ -34,9 +33,6 @@ var pool = module.exports = function pool(coin, authFn){
process.exit("ERROR: an authorize Function is needed.")
}
this.jobManager.on('newBlock', function(blockTemplate){
if ( typeof(_this.stratumServer ) === 'undefined') {
console.warn("Stratum server still not started! cannot broadcast block!");
@ -128,7 +124,7 @@ var pool = module.exports = function pool(coin, authFn){
_this.stratumServer.on('started', function(){
_this.emit('started');
console.log('Stratum server started on port ' + coin.options.stratumPort + ' for ' + coin.options.name);
}).on('client', function(client){
}).on('client.connected', function(client){
client.on('subscription', function(params, resultCallback){
var extraNonce = _this.jobManager.extraNonceCounter.next();
@ -155,17 +151,16 @@ var pool = module.exports = function pool(coin, authFn){
if (result.error){
resultCallback(result.error);
_this.emit('share', false, {
workerName : params.name,
client : client,
error : result.error
});
} else {
resultCallback(null, true);
_this.emit('share', true, {
client : client,
blockHeaderHex : result.headerHEX,
workerName : params.name,
jobId : params.jobId,
clientDifficulty : client.difficulty,
extraNonce1 : client.extraNonce1,
extraNonce2 : params.extraNonce2,
nTime : params.nTime,
nonce : params.nonce

View File

@ -106,6 +106,25 @@ var JobManager = module.exports = function JobManager(options){
}
})();
/**
* Tries to estimate the resulting block hash
* This is only valid for scrypt apparently.
* @author vekexasia
**/
function blockHashHex(headerBuffer) {
var result = new Buffer(80);
for (var i=0; i<20; i++) {
for (var j=0; j<4; j++) {
result[i*4+j] = headerBuffer[i*4+3-j];
}
}
var shaed = util.reverseBuffer(util.doublesha(result));
return shaed.toString('hex'); // return the expected block hash
}
//public members
this.extraNonceCounter = new ExtraNonceCounter();
@ -131,45 +150,55 @@ var JobManager = module.exports = function JobManager(options){
return {error: [20, 'incorrect size of extranonce2', null]};
var job = jobs[jobId];
if (!job)
if (!job) {
return {error: [21, 'job not found', null]};
}
if (nTime.length !== 8)
if (nTime.length !== 8) {
return {error: [20, 'incorrect size of ntime']};
}
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]};
}
if (nonce.length !== 8)
if (nonce.length !== 8) {
return {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]};
}
var extraNonce1Buffer = new Buffer(extraNonce1, 'hex');
var extraNonce2Buffer = new Buffer(extraNonce2, 'hex');
var coinbaseBuffer = job.serializeCoinbase(extraNonce1Buffer, extraNonce2Buffer);
var coinbaseHash = util.doublesha(coinbaseBuffer);
var coinbaseHash = util.doublesha(coinbaseBuffer);
var merkleRoot = job.merkleTree.withFirst(coinbaseHash);
merkleRoot = util.reverseBuffer(merkleRoot).toString('hex');
merkleRoot = util.reverseBuffer(merkleRoot).toString('hex');
var headerBuffer = job.serializeHeader(merkleRoot, nTime, nonce);
var headerHash = hashDigest(headerBuffer, nTimeInt);
var headerHash = hashDigest(headerBuffer, nTimeInt);
var headerBigNum = bignum.fromBuffer(headerHash, {endian: 'little', size: 32});
var targetUser = bignum(diffDividend / difficulty);
if (headerBigNum.gt(targetUser)){
return {error: [23, 'low difficulty share', null]};
}
if (job.target.ge(headerBigNum)){
var blockBuf = job.serializeBlock(headerBuffer, coinbaseBuffer);
console.log("EXPECTED BLOCK HASH: "+blockHashHex(headerBuffer)); // NOT WORKING :(?
_this.emit('blockFound', blockBuf.toString('hex'));
} else {
// If block is not found we want also to check the difficulty of the share.
// TODO: this seems to not be working properly
var targetUser = bignum(diffDividend / difficulty);
if (headerBigNum.gt(targetUser)){
return {error: [23, 'low difficulty share', null]};
}
}
return {result: true, headerHEX: headerBigNum.toString(16)};

View File

@ -46,8 +46,15 @@ var StratumClient = function(options){
case 'mining.submit':
handleSubmit(message);
break;
case 'mining.get_transactions':
sendJson({
id : null,
result : [],
error : true
});
break;
default:
console.dir('unknown stratum client message: ' + message);
console.dir('unknown stratum client message: ' + JSON.stringify(message));
break;
}
}
@ -82,10 +89,10 @@ var StratumClient = function(options){
}
function handleAuthorize(message){
this.workerIP = options.socket.address().address;
this.workerName = message.params[0];
this.workerPass = message.params[1];
options.authorizeFn(this.workerIP, this.workerName, this.workerPass, function(err, authorized, shouldCloseSocket, difficulty) {
_this.workerIP = options.socket.address().address;
_this.workerName = message.params[0];
_this.workerPass = message.params[1];
options.authorizeFn(_this.workerIP, _this.workerName, _this.workerPass, function(err, authorized, shouldCloseSocket, difficulty) {
_this.authorized = ( ! err && authorized );
sendJson({
id : message.id,
@ -105,12 +112,12 @@ var StratumClient = function(options){
console.error("Cannot set difficulty for "+_this.workernName+" error: "+JSON.stringify(err));
options.socket.end();
} else {
_this.sendDifficulty(diff);
_this.sendAndSetDifficultyIfNew(diff);
}
});
} else if (typeof(difficulty) === 'number') {
_this.sendDifficulty(difficulty);
_this.sendAndSetDifficultyIfNew(difficulty);
} else {
process.exit("Difficulty from authorizeFn callback is neither a function or a number");
}
@ -140,7 +147,6 @@ var StratumClient = function(options){
});
return;
}
console.log("SUBMIT "+JSON.stringify(message));
_this.emit('submit',
{
name : message.params[0],
@ -164,7 +170,6 @@ var StratumClient = function(options){
for (var i = 0; i < arguments.length; i++){
response += JSON.stringify(arguments[i]) + '\n';
}
console.log('response: ' + response);
options.socket.write(response);
}
@ -186,8 +191,9 @@ var StratumClient = function(options){
catch(e){
console.log('could not parse stratum client socket message: ' + message);
}
if (messageJson)
if (messageJson) {
handleMessage(messageJson);
}
});
dataBuffer = '';
}
@ -205,14 +211,28 @@ var StratumClient = function(options){
//public members
this.sendDifficulty = function(difficulty){
_this.difficulty = difficulty;
console.log("SENDING DIFFICULTY "+difficulty);
sendJson({
id : null,
method: "mining.set_difficulty",
params: [difficulty]//[512],
});
/**
* IF the given difficulty is valid and new it'll send it to the client.
* returns boolean
**/
this.sendAndSetDifficultyIfNew = function(difficulty){
if (typeof(difficulty) != 'number') {
console.error('[StratumClient.sendAndSetDifficultyIfNew] given difficulty parameter is not a number: ['+difficulty+']');
return false;
}
if (difficulty !== this.difficulty) {
this.difficulty = difficulty;
sendJson({
id : null,
method: "mining.set_difficulty",
params: [difficulty]//[512],
});
return true;
} else {
return false;
}
};
this.sendMiningJob = function(jobParams){
@ -227,6 +247,13 @@ StratumClient.prototype.__proto__ = events.EventEmitter.prototype;
/**
* The actual stratum server.
* It emits the following Events:
* - 'client.connected'(StratumClientInstance) - when a new miner connects
* - 'client.disconnected'(StratumClientInstance) - when a miner disconnects. Be aware that the socket cannot be used anymore.
* - 'started' - when the server is up and running
**/
var StratumServer = exports.Server = function StratumServer(options){
//private members
@ -247,8 +274,8 @@ var StratumServer = exports.Server = function StratumServer(options){
}
);
stratumClients[subscriptionId] = client;
_this.emit('client', client);
c.on('disconnect', function() {
_this.emit('client.connected', client);
client.on('socketDisconnect', function() {
delete stratumClients[subscriptionId];
_this.emit('client.disconnected', client);
});
@ -261,9 +288,13 @@ var StratumServer = exports.Server = function StratumServer(options){
//public members
this.broadcastMiningJobs = function(jobParams){
this.broadcastMiningJobs = function(jobParams) {
for (var clientId in stratumClients) {
stratumClients[clientId].sendMiningJob(jobParams)
// if a client gets disconnected WHILE doing this loop a crash might happen.
// 'm not sure if that can ever happn but an if here doesn't hurt!
if (typeof(stratumClients[clientId]) !== 'undefined') {
stratumClients[clientId].sendMiningJob(jobParams);
}
}
};
};

View File

@ -86,23 +86,23 @@ var Generation = exports.Generation = function Generation(rpcData, publicKey, ex
var tx = new Transaction({
inputs: [new TransactionInput({
prevOutIndex: Math.pow(2, 32) - 1,
sigScript: new ScriptSig({
height: rpcData.height,
flags: rpcData.coinbaseaux.flags,
extraNoncePlaceholder: extraNoncePlaceholder
prevOutIndex : Math.pow(2, 32) - 1,
sigScript : new ScriptSig({
height : rpcData.height,
flags : rpcData.coinbaseaux.flags,
extraNoncePlaceholder : extraNoncePlaceholder
})
})],
outputs: [new TransactionOutput({
value: rpcData.coinbasevalue,
pkScriptBuffer: publicKey
value : rpcData.coinbasevalue,
pkScriptBuffer : publicKey
})]
});
var txBuffer = tx.toBuffer();
var epIndex = buffertools.indexOf(txBuffer, extraNoncePlaceholder);
var p1 = txBuffer.slice(0, epIndex);
var p2 = txBuffer.slice(epIndex + extraNoncePlaceholder.length);
var epIndex = buffertools.indexOf(txBuffer, extraNoncePlaceholder);
var p1 = txBuffer.slice(0, epIndex);
var p2 = txBuffer.slice(epIndex + extraNoncePlaceholder.length);
this.transaction = tx;
this.coinbase = [p1, p2];