mirror of
https://github.com/jwinterm/nodejs-pool.git
synced 2026-10-17 05:21:27 +02:00
+2
-2
@@ -271,12 +271,12 @@ function calculatePPLNSPayments(blockHeader) {
|
||||
paymentData[userIdentifier].amount = paymentData[userIdentifier].amount + amountToPay;
|
||||
let donations = 0;
|
||||
if(global.config.payout.devDonation > 0){
|
||||
let devDonation = (feesToPay * (global.config.payout.devDonation / 100));
|
||||
let devDonation = Math.floor(feesToPay * (global.config.payout.devDonation / 100));
|
||||
donations += devDonation;
|
||||
paymentData[global.coinFuncs.coinDevAddress].amount = paymentData[global.coinFuncs.coinDevAddress].amount + devDonation ;
|
||||
}
|
||||
if(global.config.payout.poolDevDonation > 0){
|
||||
let poolDevDonation = (feesToPay * (global.config.payout.poolDevDonation / 100));
|
||||
let poolDevDonation = Math.floor(feesToPay * (global.config.payout.poolDevDonation / 100));
|
||||
donations += poolDevDonation;
|
||||
paymentData[global.coinFuncs.poolDevAddress].amount = paymentData[global.coinFuncs.poolDevAddress].amount + poolDevDonation;
|
||||
}
|
||||
|
||||
+38
-2
@@ -69,7 +69,7 @@ function Coin(data){
|
||||
|
||||
this.getBlockTemplate = function(walletAddress, callback){
|
||||
global.support.rpcDaemon('getblocktemplate', {
|
||||
reserve_size: 8,
|
||||
reserve_size: 17,
|
||||
wallet_address: walletAddress
|
||||
}, function(body){
|
||||
return callback(body);
|
||||
@@ -102,22 +102,58 @@ 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:
|
||||
Assuming that the extraNonce starts at byte 130:
|
||||
|130-133|134-137|138-141|142-145|
|
||||
|minerNonce/extraNonce - 4 bytes|instanceId - 4 bytes|clientPoolNonce - 4 bytes|clientNonce - 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 + 12;
|
||||
// The clientPoolLocation is for multi-thread/multi-server pools to handle the nonce for each of their tiers.
|
||||
this.clientPoolLocation = this.reserveOffset + 8;
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
+121
-59
@@ -23,7 +23,7 @@ let threadName;
|
||||
let minerCount = [];
|
||||
let BlockTemplate = global.coinFuncs.BlockTemplate;
|
||||
let hexMatch = new RegExp("^[0-9a-f]+$");
|
||||
let totalShares=0, trustedShares=0, normalShares=0, invalidShares=0;
|
||||
let totalShares = 0, trustedShares = 0, normalShares = 0, invalidShares = 0;
|
||||
|
||||
|
||||
Buffer.prototype.toByteArray = function () {
|
||||
@@ -33,12 +33,12 @@ Buffer.prototype.toByteArray = function () {
|
||||
|
||||
if (cluster.isMaster) {
|
||||
threadName = "(Master) ";
|
||||
setInterval(function(){
|
||||
setInterval(function () {
|
||||
console.log(`Processed ${trustedShares}/${normalShares}/${invalidShares}/${totalShares} Trusted/Validated/Invalid/Total shares in the last 30 seconds`);
|
||||
totalShares=0;
|
||||
trustedShares=0;
|
||||
normalShares=0;
|
||||
invalidShares=0;
|
||||
totalShares = 0;
|
||||
trustedShares = 0;
|
||||
normalShares = 0;
|
||||
invalidShares = 0;
|
||||
}, 30000);
|
||||
} else {
|
||||
threadName = "(Worker " + cluster.worker.id + " - " + process.pid + ") ";
|
||||
@@ -59,7 +59,7 @@ function registerPool() {
|
||||
[global.config.pool_id, global.config.bind_ip, true, global.config.hostname, true]);
|
||||
global.mysql.query("DELETE FROM ports WHERE pool_id = ?", [global.config.pool_id]).then(function () {
|
||||
global.config.ports.forEach(function (port) {
|
||||
if ('ssl' in port && port['ssl'] === true) {
|
||||
if ('ssl' in port && port.ssl === true) {
|
||||
global.mysql.query("INSERT INTO ports (pool_id, network_port, starting_diff, port_type, description, hidden, ip_address, ssl_port) values (?, ?, ?, ?, ?, ?, ?, 1)",
|
||||
[global.config.pool_id, port.port, port.difficulty, port.portType, port.desc, port.hidden, global.config.bind_ip]);
|
||||
} else {
|
||||
@@ -171,7 +171,7 @@ function templateUpdate(repeating) {
|
||||
newBlockTemplate(rpcResponse);
|
||||
}
|
||||
}
|
||||
if (repeating !== false){
|
||||
if (repeating !== false) {
|
||||
setTimeout(templateUpdate, 300);
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
if (agent && agent.includes('MinerGate')){
|
||||
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;
|
||||
@@ -244,7 +248,7 @@ function Miner(id, login, pass, ipAddress, startingDiff, messageSender, protoVer
|
||||
this.fixed_diff = false;
|
||||
this.difficulty = startingDiff;
|
||||
this.connectTime = Date.now();
|
||||
if (agent && agent.includes('NiceHash')){
|
||||
if (agent && agent.includes('NiceHash')) {
|
||||
this.fixed_diff = true;
|
||||
this.difficulty = global.coinFuncs.niceHashDiff;
|
||||
}
|
||||
@@ -312,7 +316,7 @@ function Miner(id, login, pass, ipAddress, startingDiff, messageSender, protoVer
|
||||
this.error = "Exchange addresses need payment IDs";
|
||||
this.valid_miner = false;
|
||||
}
|
||||
if (!activeBlockTemplate){
|
||||
if (!activeBlockTemplate) {
|
||||
this.error = "No active block template";
|
||||
this.valid_miner = false;
|
||||
}
|
||||
@@ -343,7 +347,7 @@ function Miner(id, login, pass, ipAddress, startingDiff, messageSender, protoVer
|
||||
};
|
||||
}
|
||||
|
||||
this.validJobs = global.support.circularBuffer(10);
|
||||
this.validJobs = global.support.circularBuffer(4);
|
||||
|
||||
this.cachedJob = null;
|
||||
|
||||
@@ -388,9 +392,15 @@ function Miner(id, login, pass, ipAddress, startingDiff, messageSender, protoVer
|
||||
}
|
||||
};
|
||||
|
||||
this.updateDifficulty = function(){
|
||||
this.updateDifficulty = function () {
|
||||
if (this.hashes > 0) {
|
||||
this.setNewDiff(Math.floor(this.hashes / (Math.floor((Date.now() - this.connectTime) / 1000))) * global.config.pool.targetTime);
|
||||
let newDiff = 0;
|
||||
if (this.proxy) {
|
||||
newDiff = Math.floor(Math.floor(this.hashes / (Math.floor((Date.now() - this.connectTime) / 1000))));
|
||||
} else {
|
||||
newDiff = Math.floor(this.hashes / (Math.floor((Date.now() - this.connectTime) / 1000))) * global.config.pool.targetTime;
|
||||
}
|
||||
this.setNewDiff(newDiff);
|
||||
} else {
|
||||
this.updateDifficultyOld();
|
||||
}
|
||||
@@ -399,7 +409,7 @@ function Miner(id, login, pass, ipAddress, startingDiff, messageSender, protoVer
|
||||
this.setNewDiff = function (difficulty) {
|
||||
this.newDiff = Math.round(difficulty);
|
||||
debug(threadName + "Difficulty: " + this.newDiff + " For: " + this.logString + " Time Average: " + this.shareTimeBuffer.average(this.lastShareTime) + " Entries: " + this.shareTimeBuffer.size() + " Sum: " + this.shareTimeBuffer.sum());
|
||||
if (this.newDiff > global.config.pool.maxDifficulty) {
|
||||
if (this.newDiff > global.config.pool.maxDifficulty && !this.proxy) {
|
||||
this.newDiff = global.config.pool.maxDifficulty;
|
||||
}
|
||||
if (this.difficulty === this.newDiff) {
|
||||
@@ -409,10 +419,10 @@ function Miner(id, login, pass, ipAddress, startingDiff, messageSender, protoVer
|
||||
this.newDiff = global.config.pool.minDifficulty;
|
||||
}
|
||||
debug(threadName + "Difficulty change to: " + this.newDiff + " For: " + this.logString);
|
||||
if (this.hashes > 0){
|
||||
debug(threadName + "Hashes: " + this.hashes + " in: " + Math.floor((Date.now() - this.connectTime)/1000) + " seconds gives: " +
|
||||
Math.floor(this.hashes/(Math.floor((Date.now() - this.connectTime)/1000))) + " hashes/second or: " +
|
||||
Math.floor(this.hashes/(Math.floor((Date.now() - this.connectTime)/1000))) * global.config.pool.targetTime + " difficulty versus: " + this.newDiff);
|
||||
if (this.hashes > 0) {
|
||||
debug(threadName + "Hashes: " + this.hashes + " in: " + Math.floor((Date.now() - this.connectTime) / 1000) + " seconds gives: " +
|
||||
Math.floor(this.hashes / (Math.floor((Date.now() - this.connectTime) / 1000))) + " hashes/second or: " +
|
||||
Math.floor(this.hashes / (Math.floor((Date.now() - this.connectTime) / 1000))) * global.config.pool.targetTime + " difficulty versus: " + this.newDiff);
|
||||
}
|
||||
this.messageSender('job', this.getJob());
|
||||
};
|
||||
@@ -458,32 +468,65 @@ function Miner(id, login, pass, ipAddress, startingDiff, messageSender, protoVer
|
||||
return buffReversed.toString('hex');
|
||||
};
|
||||
this.getJob = function () {
|
||||
|
||||
|
||||
if (this.lastBlockHeight === activeBlockTemplate.height && !this.newDiff && this.cachedJob !== null) {
|
||||
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();
|
||||
if (this.newDiff) {
|
||||
this.difficulty = this.newDiff;
|
||||
this.newDiff = null;
|
||||
}
|
||||
this.lastBlockHeight = activeBlockTemplate.height;
|
||||
|
||||
let newJob = {
|
||||
id: crypto.pseudoRandomBytes(21).toString('base64'),
|
||||
extraNonce: activeBlockTemplate.extraNonce,
|
||||
height: activeBlockTemplate.height,
|
||||
difficulty: this.difficulty,
|
||||
diffHex: this.diffHex,
|
||||
clientPoolLocation: activeBlockTemplate.clientPoolLocation,
|
||||
clientNonceLocation: activeBlockTemplate.clientNonceLocation,
|
||||
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,
|
||||
target_diff: this.difficulty,
|
||||
target_diff_hex: this.diffHex,
|
||||
job_id: newJob.id,
|
||||
id: this.id
|
||||
};
|
||||
}
|
||||
return this.cachedJob;
|
||||
};
|
||||
}
|
||||
@@ -526,10 +569,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.poolNonce, job.clientPoolLocation);
|
||||
template.writeUInt32BE(params.workerNonce, job.clientNonceLocation);
|
||||
}
|
||||
let shareBuffer = global.coinFuncs.constructNewBlob(template, new Buffer(nonce, 'hex'));
|
||||
|
||||
let convertedBlob;
|
||||
@@ -549,7 +601,7 @@ function processShare(miner, job, blockTemplate, nonce, resultHash) {
|
||||
if (hash.toString('hex') !== resultHash) {
|
||||
console.error(threadName + "Bad share from miner " + miner.logString);
|
||||
process.send({type: 'invalidShare'});
|
||||
if (miner.incremented === false){
|
||||
if (miner.incremented === false) {
|
||||
miner.newDiff = miner.difficulty + 1;
|
||||
miner.incremented = true;
|
||||
} else {
|
||||
@@ -661,24 +713,34 @@ function handleMinerData(method, params, ip, portData, sendReply, pushMessage) {
|
||||
global.database.storeInvalidShare(miner.invalidShareProto);
|
||||
return;
|
||||
}
|
||||
|
||||
if (job.submissions.indexOf(params.nonce) !== -1) {
|
||||
console.warn(threadName + 'Duplicate share: ' + JSON.stringify(params) + ' from ' + miner.logString);
|
||||
miner.checkBan(false);
|
||||
sendReply('Duplicate share');
|
||||
global.database.storeInvalidShare(miner.invalidShareProto);
|
||||
return;
|
||||
if (!miner.proxy) {
|
||||
if (job.submissions.indexOf(params.nonce) !== -1) {
|
||||
console.warn(threadName + 'Duplicate share: ' + JSON.stringify(params) + ' from ' + miner.logString);
|
||||
miner.checkBan(false);
|
||||
sendReply('Duplicate share');
|
||||
global.database.storeInvalidShare(miner.invalidShareProto);
|
||||
return;
|
||||
}
|
||||
job.submissions.push(params.nonce);
|
||||
} else {
|
||||
let nonce_test = `${params.nonce}_${params.poolNonce}_${params.workerNonce}`;
|
||||
if (job.submissions.indexOf(nonce_test) !== -1) {
|
||||
console.warn(threadName + 'Duplicate share: ' + JSON.stringify(params) + ' from ' + miner.logString);
|
||||
miner.checkBan(false);
|
||||
sendReply('Duplicate share');
|
||||
global.database.storeInvalidShare(miner.invalidShareProto);
|
||||
return;
|
||||
}
|
||||
job.submissions.push(nonce_test);
|
||||
}
|
||||
|
||||
job.submissions.push(params.nonce);
|
||||
|
||||
let blockTemplate = activeBlockTemplate.height === job.height ? activeBlockTemplate : pastBlockTemplates.toarray().filter(function (t) {
|
||||
return t.height === job.height;
|
||||
})[0];
|
||||
return t.height === job.height;
|
||||
})[0];
|
||||
|
||||
if (!blockTemplate) {
|
||||
console.warn(threadName + 'Block expired, Height: ' + job.height + ' from ' + miner.logString);
|
||||
if (miner.incremented === false){
|
||||
if (miner.incremented === false) {
|
||||
miner.newDiff = miner.difficulty + 1;
|
||||
miner.incremented = true;
|
||||
} else {
|
||||
@@ -691,7 +753,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) {
|
||||
@@ -768,7 +830,7 @@ if (cluster.isMaster) {
|
||||
workerList.push(worker);
|
||||
});
|
||||
templateUpdate();
|
||||
global.support.sendEmail(global.config.general.adminEmail, "Pool server "+global.config.hostname+" online", "The pool server: "+global.config.hostname+" with IP: "+global.config.bind_ip+" is online");
|
||||
global.support.sendEmail(global.config.general.adminEmail, "Pool server " + global.config.hostname + " online", "The pool server: " + global.config.hostname + " with IP: " + global.config.bind_ip + " is online");
|
||||
} else {
|
||||
setInterval(checkAliveMiners, 30000);
|
||||
setInterval(retargetMiners, global.config.pool.retargetTime * 1000);
|
||||
@@ -778,7 +840,7 @@ if (cluster.isMaster) {
|
||||
templateUpdate(false);
|
||||
}, 60000);
|
||||
async.each(global.config.ports, function (portData) {
|
||||
if (global.config[portData.portType].enable !== true){
|
||||
if (global.config[portData.portType].enable !== true) {
|
||||
return;
|
||||
}
|
||||
let handleMessage = function (socket, jsonData, pushMessage) {
|
||||
@@ -830,7 +892,7 @@ if (cluster.isMaster) {
|
||||
|
||||
socket.on('data', function (d) {
|
||||
dataBuffer += d;
|
||||
if (Buffer.byteLength(dataBuffer, 'utf8') > 10240) { //10KB
|
||||
if (Buffer.byteLength(dataBuffer, 'utf8') > 102400) { //10KB
|
||||
dataBuffer = null;
|
||||
console.warn(threadName + 'Excessive packet size from: ' + socket.remoteAddress);
|
||||
socket.destroy();
|
||||
@@ -879,7 +941,7 @@ if (cluster.isMaster) {
|
||||
});
|
||||
}
|
||||
|
||||
if ('ssl' in portData && portData['ssl'] === true) {
|
||||
if ('ssl' in portData && portData.ssl === true) {
|
||||
tls.createServer({
|
||||
key: fs.readFileSync('cert.key'),
|
||||
cert: fs.readFileSync('cert.pem')
|
||||
|
||||
Reference in New Issue
Block a user