diff --git a/README.md b/README.md index 79e2779..f99cee0 100644 --- a/README.md +++ b/README.md @@ -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", diff --git a/lib/blockTemplate.js b/lib/blockTemplate.js index dd22867..8a586eb 100644 --- a/lib/blockTemplate.js +++ b/lib/blockTemplate.js @@ -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){ diff --git a/lib/pool.js b/lib/pool.js index 3d25b67..e7cecf1 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -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); diff --git a/lib/stratum.js b/lib/stratum.js index eb1d3fe..d183a53 100644 --- a/lib/stratum.js +++ b/lib/stratum.js @@ -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'); + }); }); + })(); diff --git a/lib/varDiff.js b/lib/varDiff.js index 07ba455..f233584 100644 --- a/lib/varDiff.js +++ b/lib/varDiff.js @@ -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); }); };