Added support for multiple ports with their own diffs and vardiff

This commit is contained in:
Matt 2014-03-03 22:54:20 -07:00
parent 1716d6d738
commit eaf8f99d30
5 changed files with 95 additions and 100 deletions

View File

@ -83,12 +83,29 @@ var pool = Stratum.createPool({
txMessages: false //or true txMessages: false //or true
}, },
pool: { //instanceId: 37, //Recommend not using this because a crypto-random one will be generated
address: "nhfNedMmQ1Rjb62znwaiJgFhL3f4NQztSp",
stratumPort: 3334,
//instanceId: 37, //I recommend not to use this option as a crypto-random one will be generated /* Each pool can have as many ports for your miners to connect to as you wish. Each port can
difficulty: 32, be configured to use its own pool difficulty and variable difficulty settings. varDiff is
blockRefreshInterval: 2000 //milliseconds optional and will only be used for the ports you configure it for. */
"ports": {
"3032": { //a port for your miners to connect to
"diff": 32, //the pool difficulty for this port
/* Variable difficulty is a feature that will automatically adjust difficulty for
individual miners based on their hashrate in order to lower networking overhead */
"varDiff": {
"minDiff": 8, //minimum difficulty
"maxDiff": 512, //network difficulty will be used if it is lower than this
"targetTime": 15, //try to get 1 share per this many seconds
"retargetTime": 90, //check to see if we should retarget every this many seconds
"variancePercent": 30 //allow time to very this % from target without retargeting
}
},
"3256": { //another port for your miners to connect to, this port does not use varDiff
"diff": 256 //the pool difficulty
}
}, },
/* Recommended to have at least two daemon instances running in case one drops out-of-sync /* Recommended to have at least two daemon instances running in case one drops out-of-sync
@ -109,22 +126,6 @@ var pool = Stratum.createPool({
} }
], ],
varDiff: {
enabled: true, //set to false to disable vardiff functionality
minDifficulty: 16, //minimum difficulty. below 16 will cause problems
maxDifficulty: 1000, //network difficulty will be used if it is lower than this
targetTime: 30, //target time per share (i.e. try to get 1 share per this many seconds)
retargetTime: 120, //check to see if we should retarget every this many seconds
variancePercent: 20 //allow average time to very this % from target without retarget
/* By default new difficulties will be sent when a new job is available as stratum
protocol (http://mining.bitcoin.cz/stratum-mining) states that new difficulties
"will be applied to every next job received from the server." Some miner software
will almost immediately apply new difficulties. Set mode to fast for difficulty
to be sent immediately. */
//mode: 'fast'
},
p2p: { p2p: {
enabled: false, enabled: false,
host: "localhost", host: "localhost",

View File

@ -44,7 +44,7 @@ var BlockTemplate = module.exports = function BlockTemplate(jobId, rpcData, publ
bignum.fromBuffer(new Buffer(rpcData.target, 'hex')) : bignum.fromBuffer(new Buffer(rpcData.target, 'hex')) :
util.bignumFromBits(rpcData.bits); util.bignumFromBits(rpcData.bits);
this.difficulty = Math.round(maxDifficulty / this.target.toNumber() * 1e8) / 1e8; this.difficulty = (maxDifficulty / this.target.toNumber()) * 65536;
this.prevHashReversed = util.reverseByteOrder(new Buffer(rpcData.previousblockhash, 'hex')).toString('hex'); this.prevHashReversed = util.reverseByteOrder(new Buffer(rpcData.previousblockhash, 'hex')).toString('hex');
this.transactionData = Buffer.concat(rpcData.transactions.map(function(tx){ this.transactionData = Buffer.concat(rpcData.transactions.map(function(tx){

View File

@ -69,27 +69,23 @@ var pool = module.exports = function pool(options, authorizeFn){
} }
function SetupVarDiff(){ function SetupVarDiff(){
if (!options.varDiff.enabled){ _this.varDiff = new varDiff(options.ports);
emitLog('system', 'VarDiff has been disabled');
return;
}
_this.varDiff = new varDiff(options.varDiff, options.pool.difficulty);
_this.varDiff.on('newDifficulty', function(client, newDiff) { _this.varDiff.on('newDifficulty', function(client, newDiff) {
if (options.varDiff.mode === 'fast'){ /* We request to set the newDiff @ the next difficulty retarget
/* Send new difficulty, then force miner to use new diff by resending the (which should happen when a new job comes in - AKA BLOCK) */
current job parameters but with the "clean jobs" flag set to false client.enqueueNextDifficulty(newDiff);
so the miner doesn't restart work and submit duplicate shares */
/*if (options.varDiff.mode === 'fast'){
//Send new difficulty, then force miner to use new diff by resending the
//current job parameters but with the "clean jobs" flag set to false
//so the miner doesn't restart work and submit duplicate shares
client.sendDifficulty(newDiff); client.sendDifficulty(newDiff);
var job = _this.jobManager.currentJob.getJobParams(); var job = _this.jobManager.currentJob.getJobParams();
job[8] = false; job[8] = false;
client.sendMiningJob(job); client.sendMiningJob(job);
} }*/
else{
/* We request to set the newDiff @ the next difficulty retarget
(which should happen when a new job comes in - AKA BLOCK) */
client.enqueueNextDifficulty(newDiff);
}
}); });
emitLog("system", "VarDiff enabled and setup"); emitLog("system", "VarDiff enabled and setup");
} }
@ -133,8 +129,8 @@ var pool = module.exports = function pool(options, authorizeFn){
function SetupJobManager(){ function SetupJobManager(){
_this.jobManager = new jobManager({ _this.jobManager = new jobManager({
address : options.address,
algorithm : options.coin.algorithm, algorithm : options.coin.algorithm,
address : options.pool.address,
reward : options.coin.reward, reward : options.coin.reward,
txMessages: options.coin.txMessages txMessages: options.coin.txMessages
}); });
@ -186,7 +182,7 @@ var pool = module.exports = function pool(options, authorizeFn){
_this.daemon.once('online', function(){ _this.daemon.once('online', function(){
async.parallel({ async.parallel({
addressInfo: function(callback){ addressInfo: function(callback){
_this.daemon.cmd('validateaddress', [options.pool.address], function(results){ _this.daemon.cmd('validateaddress', [options.address], function(results){
//Make sure address is valid with each daemon //Make sure address is valid with each daemon
var allValid = results.every(function(result){ var allValid = results.every(function(result){
@ -249,12 +245,6 @@ var pool = module.exports = function pool(options, authorizeFn){
return b.response.blocks - a.response.blocks; return b.response.blocks - a.response.blocks;
})[0].response; })[0].response;
if (options.varDiff.enabled)
_this.varDiff.setNetworkDifficulty(largestHeight.difficulty);
emitLog('network', 'Current block height at ' + largestHeight.blocks +
' with difficulty of ' + largestHeight.difficulty);
callback(null, largestHeight); callback(null, largestHeight);
}); });
@ -289,6 +279,7 @@ var pool = module.exports = function pool(options, authorizeFn){
emitLog('system','Connected to daemon via RPC'); emitLog('system','Connected to daemon via RPC');
options.hasSubmitMethod = results.submitMethod; options.hasSubmitMethod = results.submitMethod;
if (options.coin.reward === 'POS' && typeof(results.addressInfo.pubkey) == 'undefined') { if (options.coin.reward === 'POS' && typeof(results.addressInfo.pubkey) == 'undefined') {
@ -301,16 +292,17 @@ var pool = module.exports = function pool(options, authorizeFn){
util.script_to_address(results.addressInfo.address) : util.script_to_address(results.addressInfo.address) :
util.script_to_pubkey(results.addressInfo.pubkey); util.script_to_pubkey(results.addressInfo.pubkey);
if (options.pool.difficulty > results.miningInfo.difficulty && options.pool.difficulty > 16){ var networkDifficulty = Math.round(results.miningInfo.difficulty * 65536);
var newDiff = results.miningInfo.difficulty > 16 ? results.miningInfo.difficulty : 16;
emitWarningLog('system', 'pool difficulty was set higher than network difficulty of ' + results.miningInfo.difficulty);
emitWarningLog('system', 'lowering pool diff from ' + options.pool.difficulty + ' to ' + newDiff);
options.pool.difficulty = newDiff emitLog('network', 'Current block height at ' + results.miningInfo.blocks +
' with difficulty of ' + networkDifficulty);
if (options.varDiff.enabled) Object.keys(options.ports).forEach(function(port){
_this.varDiff.setPoolDifficulty(options.pool.difficulty); var portDiff = options.ports[port].diff;
} if (portDiff > networkDifficulty)
emitWarningLog('system', 'diff of ' + portDiff + ' on port ' + port + ' was set higher' +
'than network difficulty of ' + networkDifficulty);
});
GetBlockTemplate(function(error, result){ GetBlockTemplate(function(error, result){
if (error){ if (error){
@ -337,17 +329,13 @@ var pool = module.exports = function pool(options, authorizeFn){
function StartStratumServer(){ function StartStratumServer(){
_this.stratumServer = new stratum.Server({ _this.stratumServer = new stratum.Server(options.ports, authorizeFn);
port: options.pool.stratumPort,
authorizeFn: authorizeFn
});
_this.stratumServer.on('started', function(){ _this.stratumServer.on('started', function(){
emitLog('system','Stratum server started on port ' + options.pool.stratumPort); emitLog('system','Stratum server started on port(s): ' + Object.keys(options.ports).join(', '));
_this.emit('started'); _this.emit('started');
}).on('client.connected', function(client){ }).on('client.connected', function(client){
if (options.varDiff.enabled) _this.varDiff.manageClient(client);
_this.varDiff.manageClient(client);
client.on('difficultyChanged', function(diff){ client.on('difficultyChanged', function(diff){
_this.emit('difficultyUpdate', client.workerName, diff); _this.emit('difficultyUpdate', client.workerName, diff);
@ -360,7 +348,7 @@ var pool = module.exports = function pool(options, authorizeFn){
extraNonce2Size extraNonce2Size
); );
this.sendDifficulty(options.pool.difficulty); this.sendDifficulty(options.ports[client.socket.localPort].diff);
this.sendMiningJob(_this.jobManager.currentJob.getJobParams()); this.sendMiningJob(_this.jobManager.currentJob.getJobParams());
}).on('submit', function(params, resultCallback){ }).on('submit', function(params, resultCallback){
@ -393,12 +381,12 @@ var pool = module.exports = function pool(options, authorizeFn){
function SetupBlockPolling(){ function SetupBlockPolling(){
if (typeof options.pool.blockRefreshInterval !== "number" || options.pool.blockRefreshInterval <= 0){ if (typeof options.blockRefreshInterval !== "number" || options.blockRefreshInterval <= 0){
emitLog('system', 'Block template polling has been disabled'); emitLog('system', 'Block template polling has been disabled');
return; return;
} }
var pollingInterval = options.pool.blockRefreshInterval; var pollingInterval = options.blockRefreshInterval;
setInterval(function () { setInterval(function () {
GetBlockTemplate(function(error, result){}); GetBlockTemplate(function(error, result){});
@ -417,7 +405,7 @@ var pool = module.exports = function pool(options, authorizeFn){
} else { } else {
var processedNewBlock = _this.jobManager.processTemplate(result.response, publicKeyBuffer); var processedNewBlock = _this.jobManager.processTemplate(result.response, publicKeyBuffer);
if (processedNewBlock && options.varDiff.enabled) if (processedNewBlock)
_this.varDiff.setNetworkDifficulty(_this.jobManager.currentJob.difficulty); _this.varDiff.setNetworkDifficulty(_this.jobManager.currentJob.difficulty);
callback(null, result.response); callback(null, result.response);

View File

@ -254,12 +254,11 @@ StratumClient.prototype.__proto__ = events.EventEmitter.prototype;
* - 'client.disconnected'(StratumClientInstance) - when a miner disconnects. Be aware that the socket cannot be used anymore. * - 'client.disconnected'(StratumClientInstance) - when a miner disconnects. Be aware that the socket cannot be used anymore.
* - 'started' - when the server is up and running * - 'started' - when the server is up and running
**/ **/
var StratumServer = exports.Server = function StratumServer(options){ var StratumServer = exports.Server = function StratumServer(ports, authorizeFn){
//private members //private members
var _this = this; var _this = this;
var socketServer;
var stratumClients = {}; var stratumClients = {};
var subscriptionCounter = SubscriptionCounter(); var subscriptionCounter = SubscriptionCounter();
@ -283,12 +282,15 @@ var StratumServer = exports.Server = function StratumServer(options){
}; };
(function init(){ (function init(){
_socketServer = socketServer = net.createServer({allowHalfOpen: true}, function(socket){
handleNewClient(socket); Object.keys(ports).forEach(function(port){
}); net.createServer({allowHalfOpen: true}, function(socket){
_socketServer.listen(options.port, function(){ handleNewClient(socket);
_this.emit('started'); }).listen(parseInt(port), function(){
_this.emit('started');
});
}); });
})(); })();

View File

@ -41,26 +41,41 @@ function RingBuffer(maxSize){
} }
var varDiff = module.exports = function varDiff(options, poolDifficulty){ var varDiff = module.exports = function varDiff(ports){
var _this = this; var _this = this;
var networkDifficulty; var networkDifficulty;
var bufferSize = options.retargetTime / options.targetTime * 4;
var variance = options.targetTime * (options.variancePercent / 100);
var tMin = options.targetTime - variance;
var tMax = options.targetTime + variance;
var portsCalcInfo = {};
Object.keys(ports).forEach(function(port){
var varDiffOptions = ports[port].varDiff;
if (!varDiffOptions) return;
var variance = varDiffOptions.targetTime * (varDiffOptions.variancePercent / 100);
portsCalcInfo[parseInt(ports)] = {
bufferSize: varDiffOptions.retargetTime / varDiffOptions.targetTime * 4,
tMin: varDiffOptions.targetTime - variance,
tMax: varDiffOptions.targetTime + variance
}
});
this.setNetworkDifficulty = function(diff){ this.setNetworkDifficulty = function(diff){
networkDifficulty = diff; networkDifficulty = diff;
}; };
this.setPoolDifficulty = function(diff){
poolDifficulty = diff;
};
this.manageClient = function(client){ this.manageClient = function(client){
var stratumPort = client.socket.localPort;
if (!(stratumPort in portsCalcInfo))
return;
var calcInfo = portsCalcInfo[stratumPort];
var options = ports[stratumPort];
var lastTs; var lastTs;
var lastRtc; var lastRtc;
var timeBuffer; var timeBuffer;
@ -72,53 +87,42 @@ var varDiff = module.exports = function varDiff(options, poolDifficulty){
if (!lastRtc){ if (!lastRtc){
lastRtc = ts - options.retargetTime / 2; lastRtc = ts - options.retargetTime / 2;
lastTs = ts; lastTs = ts;
timeBuffer = new RingBuffer(bufferSize); timeBuffer = new RingBuffer(calcInfo.bufferSize);
// console.log(bufferSize+ ' first time share vardiff curdiff: '+client.difficulty);
return; return;
} }
var sinceLast = ts - lastTs; var sinceLast = ts - lastTs;
timeBuffer.append(sinceLast); timeBuffer.append(sinceLast);
lastTs = ts; lastTs = ts;
if ((ts - lastRtc) < options.retargetTime && timeBuffer.size() > 0){ if ((ts - lastRtc) < options.retargetTime && timeBuffer.size() > 0)
// console.log('do not retarget');
return; return;
}
lastRtc = ts; lastRtc = ts;
var avg = timeBuffer.avg(); var avg = timeBuffer.avg();
var ddiff; var ddiff;
if (avg > tMax && client.difficulty > options.minDifficulty) { if (avg > calcInfo.tMax && client.difficulty > options.minDiff) {
ddiff = 0.5; ddiff = 0.5;
if (ddiff * client.difficulty < options.minDifficulty) { if (ddiff * client.difficulty < options.minDiff) {
ddiff = options.minDifficulty / client.difficulty; ddiff = options.minDiff / client.difficulty;
} }
} else if (avg < tMin) { } else if (avg < calcInfo.tMin) {
ddiff = 2; ddiff = 2;
var diffMax = networkDifficulty < options.maxDifficulty ? networkDifficulty : options.maxDifficulty; var diffMax = networkDifficulty < options.maxDiff ? networkDifficulty : options.maxDiff;
var diffMax = options.maxDifficulty; var diffMax = options.maxDiff;
// console.log("Max & network", diffMax, networkDifficulty);
if (ddiff * client.difficulty > diffMax) { if (ddiff * client.difficulty > diffMax) {
ddiff = diffMax / client.difficulty; ddiff = diffMax / client.difficulty;
} }
// console.log('increasing difficulty, ddiff: ' + ddiff);
} }
else{ else{
// console.log('hashrate in range ' + JSON.stringify({ddiff: ddiff, avg: avg}) );
return; return;
} }
var newDiff = client.difficulty * ddiff; var newDiff = client.difficulty * ddiff;
timeBuffer.clear(); timeBuffer.clear();
//console.log('sending new difficutly ' + newDiff);
_this.emit('newDifficulty', client, newDiff); _this.emit('newDifficulty', client, newDiff);
}); });
}; };