diff --git a/lib/pool.js b/lib/pool.js index 2d0ca08..988118f 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -1,5 +1,7 @@ var events = require('events'); var async = require('async'); + +var varDiff = require('./varDifff.js'); var daemon = require('./daemon.js'); var stratum = require('./stratum.js'); var jobManager = require('./jobManager.js'); @@ -22,6 +24,7 @@ var util = require('./util.js'); var pool = module.exports = function pool(options, authorizeFn){ this.options = options; + var _this = this; var publicKeyBuffer; @@ -32,10 +35,34 @@ var pool = module.exports = function pool(options, authorizeFn){ (function Init(){ SetupJobManager(); + SetupVarDiff(); SetupDaemonInterface(); })(); + function SetupVarDiff(){ + _this.varDiff = new varDifff(options.varDiff, options.difficulty); + _this.varDiff.on('difficultyRequest', function(){ + emitLog('varDiff', 'Difficulty requested for vardiff'); + if (_this.stratumServer) + RequestDifficulty(function(){}); + }); + } + + function RequestDifficulty(callback){ + _this.daemon.cmd('getdifficulty', + [], + function(error, result){ + if (error) { + emitErrorLog('getdifficulty', 'Error requesting difficulty from daemon for vardiff'); + } else { + _this.varDiff.setNetworkDifficulty(result); + callback(error, result); + } + } + ); + } + /* Coin daemons either use submitblock or getblocktemplate for submitting new blocks */ @@ -108,6 +135,7 @@ var pool = module.exports = function pool(options, authorizeFn){ _this.daemon = new daemon.interface(options.daemon); _this.daemon.on('online', function(){ async.parallel({ + difficulty: RequestDifficulty, addressInfo: function(callback){ _this.daemon.cmd('validateaddress', [options.address], @@ -178,6 +206,9 @@ var pool = module.exports = function pool(options, authorizeFn){ emitLog('system','Stratum server started on port ' + options.stratumPort); _this.emit('started'); }).on('client.connected', function(client){ + + _this.varDiff.manageClient(client); + client.on('subscription', function(params, resultCallback){ var extraNonce = _this.jobManager.extraNonceCounter.next(); diff --git a/lib/varDifff.js b/lib/varDifff.js new file mode 100644 index 0000000..ba90c02 --- /dev/null +++ b/lib/varDifff.js @@ -0,0 +1,128 @@ +var events = require('events'); + +/* + +Vardiff ported from stratum-mining share-limiter + https://github.com/ahmedbodi/stratum-mining/blob/master/mining/basic_share_limiter.py + + */ + + +function RingBuffer(maxSize){ + var data = []; + var cursor = 0; + var isFull = false; + this.append = function(x){ + if (isFull){ + data[cursor] = x; + cursor = (cursor + 1) % maxSize; + } + else{ + data.push(x); + cursor++; + if (data.length === maxSize){ + cursor = 0; + isFull = true; + } + } + }; + this.avg = function(){ + var total = data.reduce(function(a, b){ + return a + b; + }); + return total / (isFull ? maxSize : cursor); + }; + this.size = function(){ + return isFull ? maxSize : cursor; + }; + this.clear = function(){ + data = []; + cursor = 0; + isFull = false; + }; +} + + +var varDiff = module.exports = function varDiff(options, poolDifficulty){ + + var _this = this; + + if (!options.enabled){ + return; + } + + 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; + + this.setNetworkDifficulty = function(diff){ + networkDifficulty = diff; + }; + + setInterval(function(){ + _this.emit('difficultyRequest'); + }, options.daemonDiffUpdateFrequency * 1000); + + this.manageClient = function(client){ + var lastTs; + var lastRtc; + var timeBuffer; + + client.on('submit', function(){ + + var ts = Date.now() / 1000; + + if (!lastRtc){ + lastRtc = ts - options.retargetTime / 2; + lastTs = ts; + timeBuffer = new RingBuffer(bufferSize); + console.log('first time share vardiff'); + return; + } + + timeBuffer.append(ts - lastTs); + lastTs = ts; + + if ((ts - lastRtc) < options.retargetTime && timeBuffer.size() > 0){ + console.log('do not retarget'); + return; + } + + lastRtc = ts; + var avg = timeBuffer.avg(); + + if (avg < 1) + avg = 1; + + var ddiff = (client.difficulty * (options.targetTime / avg)) - client.difficulty; + + if (avg > tMax && client.difficulty > options.minDifficulty){ + if (ddiff > -1) + ddiff = -1; + if (ddiff + client.difficulty < poolDifficulty) + ddiff = options.minDifficulty - client.difficulty; + } + else if (avg < tMin){ + if (ddiff < 1) + ddiff = 1; + var diffMax = networkDifficulty < options.maxDifficulty ? networkDifficulty : options.maxDifficulty; + if (ddiff + client.difficulty > diffMax) + ddiff = diffMax - client.difficulty; + } + 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); + + client.sendDifficulty(newDiff); + }); + }; +}; +varDiff.prototype.__proto__ = events.EventEmitter.prototype; \ No newline at end of file diff --git a/lib/vardiff.js b/lib/vardiff.js deleted file mode 100644 index e69de29..0000000