From a50beb1932013849c336c6ec92cffd2d0b266762 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 1 Apr 2014 10:10:50 -0600 Subject: [PATCH] Added jobRebroadcastTimeout configuration --- README.md | 5 +++++ lib/algoProperties.js | 7 ++++--- lib/jobManager.js | 11 ++++++----- lib/merkleTree.js | 4 ++-- lib/peer.js | 4 ++-- lib/pool.js | 2 +- lib/stratum.js | 19 ++++++++++--------- lib/util.js | 14 ++++++-------- 8 files changed, 36 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 7fc925d..7922eeb 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,11 @@ var pool = Stratum.createPool({ job broadcast. */ "txRefreshInterval": 20000, + /* Some miner software is bugged and will consider the pool offline if it doesn't receive + anything for around a minute, so every time we broadcast jobs, set a timeout to rebroadcast + in this many seconds unless we find a new job. Set to zero or remove to disable this. */ + "jobRebroadcastTimeout": 55, + //instanceId: 37, //Recommend not using this because a crypto-random one will be generated /* Some attackers will create thousands of workers that use up all available socket connections, diff --git a/lib/algoProperties.js b/lib/algoProperties.js index 88a08f3..e32cdf1 100644 --- a/lib/algoProperties.js +++ b/lib/algoProperties.js @@ -10,7 +10,7 @@ var algos = module.exports = global.algos = { multiplier: Math.pow(2, 32), hash: function(){ return function(){ - return util.doublesha.apply(this, arguments); + return util.sha256d.apply(this, arguments); } } }, @@ -99,8 +99,9 @@ var algos = module.exports = global.algos = { shift: 24, multiplier: Math.pow(2, 8), hash: function(){ - return function(){ - return multiHashing.keccak.apply(this, arguments); + return function(data, nTime){ + var f = Buffer.concat([data, new Buffer(nTime.toString(16), 'hex')]); + return multiHashing.keccak(f); } } }, diff --git a/lib/jobManager.js b/lib/jobManager.js index d426615..12cca3a 100644 --- a/lib/jobManager.js +++ b/lib/jobManager.js @@ -65,6 +65,8 @@ var JobManager = module.exports = function JobManager(maxDifficulty, hashDigest, var lastTransactionUpdateCheck = Date.now(); + var coinbaseHasher = options.coin.algorithm === 'keccak' ? util.sha256 : util.sha256d; + //returns true if processed a new block this.processTemplate = function(rpcData, publicKey){ @@ -168,12 +170,12 @@ var JobManager = module.exports = function JobManager(maxDifficulty, hashDigest, var extraNonce2Buffer = new Buffer(extraNonce2, 'hex'); var coinbaseBuffer = job.serializeCoinbase(extraNonce1Buffer, extraNonce2Buffer); - var coinbaseHash = util.doublesha(coinbaseBuffer); + var coinbaseHash = coinbaseHasher(coinbaseBuffer); var merkleRoot = util.reverseBuffer(job.merkleTree.withFirst(coinbaseHash)).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 blockHash; @@ -181,14 +183,13 @@ var JobManager = module.exports = function JobManager(maxDifficulty, hashDigest, if (job.target.ge(headerBigNum)){ - blockHex = job.serializeBlock(headerBuffer, coinbaseBuffer).toString('hex'); - blockHash = util.reverseBuffer(util.doublesha(headerBuffer)).toString('hex'); + blockHash = util.reverseBuffer(util.sha256d(headerBuffer)).toString('hex'); } else { var targetUser = maxDifficulty.div(difficulty); if (headerBigNum.gt(targetUser)){ - var lowPercent = targetUser.div(headerBigNum).mul(100).toNumber().toFixed(10); + var lowPercent = headerBigNum.sub(targetUser).div(headerBigNum).mul(100).toNumber().toFixed(10); return shareError([23, 'low difficulty share, need to be ' + lowPercent + '% higher']); } } diff --git a/lib/merkleTree.js b/lib/merkleTree.js index 6090517..097b480 100644 --- a/lib/merkleTree.js +++ b/lib/merkleTree.js @@ -10,7 +10,7 @@ var MerkleTree = module.exports = function MerkleTree(data){ function merkleJoin(h1, h2){ var joined = Buffer.concat([h1, h2]); - var dhashed = util.doublesha(joined); + var dhashed = util.sha256d(joined); return dhashed; } @@ -51,7 +51,7 @@ var MerkleTree = module.exports = function MerkleTree(data){ MerkleTree.prototype = { withFirst: function(f){ this.steps.forEach(function(s){ - f = util.doublesha(Buffer.concat([f, s])); + f = util.sha256d(Buffer.concat([f, s])); }); return f; } diff --git a/lib/peer.js b/lib/peer.js index 4186cc5..eab8da7 100644 --- a/lib/peer.js +++ b/lib/peer.js @@ -102,7 +102,7 @@ var Peer = module.exports = function(options){ var msgLength = header.readUInt32LE(16); var msgChecksum = header.readUInt32LE(20); readFlowingBytes(client, msgLength, lopped, function(payload, lopped){ - if (util.doublesha(payload).readUInt32LE(0) !== msgChecksum){ + if (util.sha256d(payload).readUInt32LE(0) !== msgChecksum){ _this.emit('error', 'bad payload - failed checksum'); beginReadingMessage(null); return; @@ -154,7 +154,7 @@ var Peer = module.exports = function(options){ magic, command, util.packUInt32LE(payload.length), - util.doublesha(payload).slice(0, 4), + util.sha256d(payload).slice(0, 4), payload ]); client.write(message); diff --git a/lib/pool.js b/lib/pool.js index 2cd173b..58d5c43 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -517,7 +517,7 @@ var pool = module.exports = function pool(options, authorizeFn){ function StartStratumServer(finishedCallback){ - _this.stratumServer = new stratum.Server(options.ports, options.connectionTimeout, options.banning, authorizeFn); + _this.stratumServer = new stratum.Server(options.ports, options.connectionTimeout, options.jobRebroadcastTimeout, options.banning, authorizeFn); _this.stratumServer.on('started', function(){ options.initStats.stratumPorts = Object.keys(options.ports); diff --git a/lib/stratum.js b/lib/stratum.js index 530d895..47f039e 100644 --- a/lib/stratum.js +++ b/lib/stratum.js @@ -287,18 +287,18 @@ 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(ports, connectionTimeout, banning, authorizeFn){ +var StratumServer = exports.Server = function StratumServer(ports, connectionTimeout, jobRebroadcastTimeout, banning, authorizeFn){ //private members - var socketTimeout = connectionTimeout * 1000; - var bannedMS = banning ? banning.time * 1000 : null; + var socketTimeout = connectionTimeout * 1000; + var bannedMS = banning ? banning.time * 1000 : null; - var _this = this; - var stratumClients = {}; + var _this = this; + var stratumClients = {}; var subscriptionCounter = SubscriptionCounter(); var rebroadcastTimeout; - var bannedIPs = {}; + var bannedIPs = {}; //Interval to look through bannedIPs for old bans and remove them in order to prevent a memory leak var purgeOldBans = (!banning || !banning.enabled) ? null : setInterval(function(){ @@ -376,14 +376,15 @@ var StratumServer = exports.Server = function StratumServer(ports, connectionTim } } - /* Some miners will consider the pool dead if it doesn't receive a job at least every 30 seconds. - So every time broadcast jobs, we set a timeout to rebroadcast in 30 seconds unless cleared. */ + /* Some miners will consider the pool dead if it doesn't receive a job for around a minute. + So every time we broadcast jobs, set a timeout to rebroadcast in X seconds unless cleared. */ + if (isNaN(jobRebroadcastTimeout) || jobRebroadcastTimeout <= 0) return; clearTimeout(rebroadcastTimeout); rebroadcastTimeout = setTimeout(function(){ var resendParams = jobParams; resendParams[8] = false; _this.broadcastMiningJobs(resendParams); - }, 30000); + }, jobRebroadcastTimeout * 1000); }; this.getStratumClients = function () { diff --git a/lib/util.js b/lib/util.js index 987168a..2fd3a37 100644 --- a/lib/util.js +++ b/lib/util.js @@ -22,16 +22,14 @@ exports.bignumFromBits = function(bitsString){ return target; }; -exports.doublesha = function(buffer){ +exports.sha256 = function(buffer){ var hash1 = crypto.createHash('sha256'); hash1.update(buffer); - hash1 = hash1.digest(); + return hash1.digest(); +}; - var hash2 = crypto.createHash('sha256'); - hash2.update(hash1); - hash2 = hash2.digest(); - - return hash2; +exports.sha256d = function(buffer){ + return exports.sha256(exports.sha256(buffer)); }; exports.reverseBuffer = function(buff){ @@ -257,7 +255,7 @@ exports.addressToPubkey = function(addr){ /* We already do rpc.validateaddress so we don't need this var ver = decoded[0]; var cksumA = decoded.slice(-4); - var cksumB = exports.doublesha(decoded.slice(0, -4)).slice(0, 4); + var cksumB = exports.sha256d(decoded.slice(0, -4)).slice(0, 4); if (cksumA.toString('hex') != cksumB.toString('hex')){ console.error('checksum did not match for ' + addr)