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
},
pool: {
address: "nhfNedMmQ1Rjb62znwaiJgFhL3f4NQztSp",
stratumPort: 3334,
//instanceId: 37, //I recommend not to use this option as a crypto-random one will be generated
difficulty: 32,
blockRefreshInterval: 2000 //milliseconds
//instanceId: 37, //Recommend not using this because 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
be configured to use its own pool difficulty and variable difficulty settings. varDiff is
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
@ -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: {
enabled: false,
host: "localhost",

View File

@ -44,7 +44,7 @@ var BlockTemplate = module.exports = function BlockTemplate(jobId, rpcData, publ
bignum.fromBuffer(new Buffer(rpcData.target, 'hex')) :
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.transactionData = Buffer.concat(rpcData.transactions.map(function(tx){

View File

@ -69,27 +69,23 @@ var pool = module.exports = function pool(options, authorizeFn){
}
function SetupVarDiff(){
if (!options.varDiff.enabled){
emitLog('system', 'VarDiff has been disabled');
return;
}
_this.varDiff = new varDiff(options.varDiff, options.pool.difficulty);
_this.varDiff = new varDiff(options.ports);
_this.varDiff.on('newDifficulty', function(client, newDiff) {
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 */
/* We request to set the newDiff @ the next difficulty retarget
(which should happen when a new job comes in - AKA BLOCK) */
client.enqueueNextDifficulty(newDiff);
/*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);
var job = _this.jobManager.currentJob.getJobParams();
job[8] = false;
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");
}
@ -133,8 +129,8 @@ var pool = module.exports = function pool(options, authorizeFn){
function SetupJobManager(){
_this.jobManager = new jobManager({
address : options.address,
algorithm : options.coin.algorithm,
address : options.pool.address,
reward : options.coin.reward,
txMessages: options.coin.txMessages
});
@ -186,7 +182,7 @@ var pool = module.exports = function pool(options, authorizeFn){
_this.daemon.once('online', function(){
async.parallel({
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
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;
})[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);
});
@ -289,6 +279,7 @@ var pool = module.exports = function pool(options, authorizeFn){
emitLog('system','Connected to daemon via RPC');
options.hasSubmitMethod = results.submitMethod;
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_pubkey(results.addressInfo.pubkey);
if (options.pool.difficulty > results.miningInfo.difficulty && options.pool.difficulty > 16){
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);
var networkDifficulty = Math.round(results.miningInfo.difficulty * 65536);
options.pool.difficulty = newDiff
emitLog('network', 'Current block height at ' + results.miningInfo.blocks +
' with difficulty of ' + networkDifficulty);
if (options.varDiff.enabled)
_this.varDiff.setPoolDifficulty(options.pool.difficulty);
}
Object.keys(options.ports).forEach(function(port){
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){
if (error){
@ -337,17 +329,13 @@ var pool = module.exports = function pool(options, authorizeFn){
function StartStratumServer(){
_this.stratumServer = new stratum.Server({
port: options.pool.stratumPort,
authorizeFn: authorizeFn
});
_this.stratumServer = new stratum.Server(options.ports, authorizeFn);
_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');
}).on('client.connected', function(client){
if (options.varDiff.enabled)
_this.varDiff.manageClient(client);
_this.varDiff.manageClient(client);
client.on('difficultyChanged', function(diff){
_this.emit('difficultyUpdate', client.workerName, diff);
@ -360,7 +348,7 @@ var pool = module.exports = function pool(options, authorizeFn){
extraNonce2Size
);
this.sendDifficulty(options.pool.difficulty);
this.sendDifficulty(options.ports[client.socket.localPort].diff);
this.sendMiningJob(_this.jobManager.currentJob.getJobParams());
}).on('submit', function(params, resultCallback){
@ -393,12 +381,12 @@ var pool = module.exports = function pool(options, authorizeFn){
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');
return;
}
var pollingInterval = options.pool.blockRefreshInterval;
var pollingInterval = options.blockRefreshInterval;
setInterval(function () {
GetBlockTemplate(function(error, result){});
@ -417,7 +405,7 @@ var pool = module.exports = function pool(options, authorizeFn){
} else {
var processedNewBlock = _this.jobManager.processTemplate(result.response, publicKeyBuffer);
if (processedNewBlock && options.varDiff.enabled)
if (processedNewBlock)
_this.varDiff.setNetworkDifficulty(_this.jobManager.currentJob.difficulty);
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.
* - '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
var _this = this;
var socketServer;
var stratumClients = {};
var subscriptionCounter = SubscriptionCounter();
@ -283,12 +282,15 @@ var StratumServer = exports.Server = function StratumServer(options){
};
(function init(){
_socketServer = socketServer = net.createServer({allowHalfOpen: true}, function(socket){
handleNewClient(socket);
});
_socketServer.listen(options.port, function(){
_this.emit('started');
Object.keys(ports).forEach(function(port){
net.createServer({allowHalfOpen: true}, function(socket){
handleNewClient(socket);
}).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 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){
networkDifficulty = diff;
};
this.setPoolDifficulty = function(diff){
poolDifficulty = diff;
};
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 lastRtc;
var timeBuffer;
@ -72,53 +87,42 @@ var varDiff = module.exports = function varDiff(options, poolDifficulty){
if (!lastRtc){
lastRtc = ts - options.retargetTime / 2;
lastTs = ts;
timeBuffer = new RingBuffer(bufferSize);
// console.log(bufferSize+ ' first time share vardiff curdiff: '+client.difficulty);
timeBuffer = new RingBuffer(calcInfo.bufferSize);
return;
}
var sinceLast = ts - lastTs;
timeBuffer.append(sinceLast);
lastTs = ts;
if ((ts - lastRtc) < options.retargetTime && timeBuffer.size() > 0){
// console.log('do not retarget');
if ((ts - lastRtc) < options.retargetTime && timeBuffer.size() > 0)
return;
}
lastRtc = ts;
var avg = timeBuffer.avg();
var ddiff;
if (avg > tMax && client.difficulty > options.minDifficulty) {
if (avg > calcInfo.tMax && client.difficulty > options.minDiff) {
ddiff = 0.5;
if (ddiff * client.difficulty < options.minDifficulty) {
ddiff = options.minDifficulty / client.difficulty;
if (ddiff * client.difficulty < options.minDiff) {
ddiff = options.minDiff / client.difficulty;
}
} else if (avg < tMin) {
} else if (avg < calcInfo.tMin) {
ddiff = 2;
var diffMax = networkDifficulty < options.maxDifficulty ? networkDifficulty : options.maxDifficulty;
var diffMax = options.maxDifficulty;
// console.log("Max & network", diffMax, networkDifficulty);
var diffMax = networkDifficulty < options.maxDiff ? networkDifficulty : options.maxDiff;
var diffMax = options.maxDiff;
if (ddiff * client.difficulty > diffMax) {
ddiff = diffMax / client.difficulty;
}
// console.log('increasing difficulty, ddiff: ' + ddiff);
}
else{
// console.log('hashrate in range ' + JSON.stringify({ddiff: ddiff, avg: avg}) );
return;
}
var newDiff = client.difficulty * ddiff;
timeBuffer.clear();
//console.log('sending new difficutly ' + newDiff);
_this.emit('newDifficulty', client, newDiff);
});
};