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:
parent
263972fcdc
commit
a42fe87723
11
TODOLIST.md
Normal file
11
TODOLIST.md
Normal 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 ===
|
||||
@ -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 = '';
|
||||
|
||||
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
11
index.js
11
index.js
@ -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
|
||||
|
||||
@ -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)};
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@ -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];
|
||||
|
||||
Loading…
Reference in New Issue
Block a user