diff --git a/lib/coins/xmr.js b/lib/coins/xmr.js index a493a43..977759f 100644 --- a/lib/coins/xmr.js +++ b/lib/coins/xmr.js @@ -69,7 +69,7 @@ function Coin(data){ this.getBlockTemplate = function(walletAddress, callback){ global.support.rpcDaemon('getblocktemplate', { - reserve_size: 8, + reserve_size: 16, wallet_address: walletAddress }, function(body){ return callback(body); @@ -102,22 +102,56 @@ function Coin(data){ }; this.BlockTemplate = function(template) { + /* + Generating a block template is a simple thing. Ask for a boatload of information, and go from there. + Important things to consider. + The reserved space is 13 bytes long now in the following format: + |minerNonce/extraNonce - 4 bytes|instanceId - 4 bytes|clientNonce - 4 bytes|clientPoolNonce - 4 bytes| + This is designed to allow a single block template to be used on up to 4 billion poolSlaves (clientPoolNonce) + Each with 4 billion clients. (clientNonce) + While being unique to this particular pool thread (instanceId) + With up to 4 billion clients (minerNonce/extraNonce) + Overkill? Sure. But that's what we do here. Overkill. + */ + + // Set this.blob equal to the BT blob that we get from upstream. this.blob = template.blocktemplate_blob; + // Set this.diff equal to the known diff for this block. this.difficulty = template.difficulty; + // Set this.height equal to the known height for this block. this.height = template.height; + // Set this.reserveOffset to the byte location of the reserved offset. this.reserveOffset = template.reserved_offset; + // Set this.buffer to the binary decoded version of the BT blob. this.buffer = new Buffer(this.blob, 'hex'); + // Copy the Instance ID to the reserve offset + 4 bytes deeper. Copy in 4 bytes. instanceId.copy(this.buffer, this.reserveOffset + 4, 0, 3); + // Generate a clean, shiny new buffer. this.previous_hash = new Buffer(32); + // Copy in bytes 7 through 39 to this.previous_hash from the current BT. this.buffer.copy(this.previous_hash, 0, 7, 39); + // Reset the Nonce. - This is the per-miner/pool nonce this.extraNonce = 0; + // The clientNonceLocation is the location at which the client pools should set the nonces for each of their clients. + this.clientNonceLocation = this.reserveOffset + 8; + // The clientPoolLocation is for multi-thread/multi-server pools to handle the nonce for each of their tiers. + this.clientPoolLocation = this.reserveOffset + 12; this.nextBlob = function () { + // Write a 32 bit integer, big-endian style to the 0 byte of the reserve offset. this.buffer.writeUInt32BE(++this.extraNonce, this.reserveOffset); + // Convert the blob into something hashable. return global.coinFuncs.convertBlob(this.buffer).toString('hex'); }; + // Make it so you can get the raw block blob out. + this.nextBlobWithChildNonce = function () { + // Write a 32 bit integer, big-endian style to the 0 byte of the reserve offset. + this.buffer.writeUInt32BE(++this.extraNonce, this.reserveOffset); + // Don't convert the blob to something hashable. You bad. + return this.buffer.toString('hex'); + }; }; - this.cryptoNight = multiHashing['cryptonight']; + this.cryptoNight = multiHashing.cryptoNight; } diff --git a/lib/pool.js b/lib/pool.js index 4715633..d0e556c 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -215,9 +215,13 @@ function Miner(id, login, pass, ipAddress, startingDiff, messageSender, protoVer let pass_split = pass.split(":"); this.error = ""; this.identifier = pass_split[0]; + this.proxy = false; if (agent && agent.includes('MinerGate')){ this.identifier = "MinerGate"; } + if (agent && agent.includes('xmr-node-proxy')){ + this.proxy = true; + } this.paymentID = null; this.valid_miner = true; this.port = port; @@ -463,27 +467,52 @@ function Miner(id, login, pass, ipAddress, startingDiff, messageSender, protoVer return this.cachedJob; } - let blob = activeBlockTemplate.nextBlob(); - let target = this.getTargetHex(); - this.lastBlockHeight = activeBlockTemplate.height; + if (!this.proxy){ + let blob = activeBlockTemplate.nextBlob(); + let target = this.getTargetHex(); + this.lastBlockHeight = activeBlockTemplate.height; - let newJob = { - id: crypto.pseudoRandomBytes(21).toString('base64'), - extraNonce: activeBlockTemplate.extraNonce, - height: activeBlockTemplate.height, - difficulty: this.difficulty, - diffHex: this.diffHex, - submissions: [] - }; + let newJob = { + id: crypto.pseudoRandomBytes(21).toString('base64'), + extraNonce: activeBlockTemplate.extraNonce, + height: activeBlockTemplate.height, + difficulty: this.difficulty, + diffHex: this.diffHex, + submissions: [] + }; - this.validJobs.enq(newJob); - this.cachedJob = { - blob: blob, - job_id: newJob.id, - target: target, - id: this.id - }; + this.validJobs.enq(newJob); + this.cachedJob = { + blob: blob, + job_id: newJob.id, + target: target, + id: this.id + }; + } else { + let blob = activeBlockTemplate.nextBlobWithChildNonce(); + this.lastBlockHeight = activeBlockTemplate.height; + + let newJob = { + id: crypto.pseudoRandomBytes(21).toString('base64'), + extraNonce: activeBlockTemplate.extraNonce, + height: activeBlockTemplate.height, + difficulty: this.difficulty, + diffHex: this.diffHex, + submissions: [] + }; + this.validJobs.enq(newJob); + this.cachedJob = { + blocktemplate_blob: blob, + difficulty: activeBlockTemplate.difficulty, + height: activeBlockTemplate.height, + reserved_offset: activeBlockTemplate.reserveOffset, + client_nonce_offset: activeBlockTemplate.clientNonceLocation, + client_pool_offset: activeBlockTemplate.clientPoolLocation, + job_id: newJob.id, + id: this.id + }; + } return this.cachedJob; }; } @@ -526,10 +555,19 @@ function recordShareData(miner, job, shareDiff, blockCandidate, hashHex, shareTy } -function processShare(miner, job, blockTemplate, nonce, resultHash) { +function processShare(miner, job, blockTemplate, params) { + let nonce = params.nonce; + let resultHash = params.result; let template = new Buffer(blockTemplate.buffer.length); - blockTemplate.buffer.copy(template); - template.writeUInt32BE(job.extraNonce, blockTemplate.reserveOffset); + if (!miner.proxy){ + blockTemplate.buffer.copy(template); + template.writeUInt32BE(job.extraNonce, blockTemplate.reserveOffset); + } else { + blockTemplate.buffer.copy(template); + template.writeUInt32BE(job.extraNonce, blockTemplate.reserveOffset); + template.writeUInt32BE(params.workerNonce, blockTemplate.clientNonceLocation); + template.writeUInt32BE(params.poolNonce, blockTemplate.clientPoolLocation); + } let shareBuffer = global.coinFuncs.constructNewBlob(template, new Buffer(nonce, 'hex')); let convertedBlob; @@ -691,7 +729,7 @@ function handleMinerData(method, params, ip, portData, sendReply, pushMessage) { return; } - let shareAccepted = processShare(miner, job, blockTemplate, params.nonce, params.result); + let shareAccepted = processShare(miner, job, blockTemplate, params); miner.checkBan(shareAccepted); if (global.config.pool.trustedMiners) {