mirror of https://github.com/BTCPrivate/z-nomp.git
merge latest fixes and fee withdrawal functionality
This commit is contained in:
commit
d3699b3d08
|
@ -20,7 +20,7 @@
|
|||
"enabled": true,
|
||||
"siteTitle": "Cryppit",
|
||||
"port": 80,
|
||||
"statUpdateInterval": 5,
|
||||
"statUpdateInterval": 1.5,
|
||||
"hashrateWindow": 600
|
||||
},
|
||||
"proxy": {
|
||||
|
|
3
init.js
3
init.js
|
@ -2,6 +2,7 @@ var fs = require('fs');
|
|||
var os = require('os');
|
||||
var cluster = require('cluster');
|
||||
|
||||
require('./libs/algoProperties.js');
|
||||
|
||||
var async = require('async');
|
||||
var posix = require('posix');
|
||||
|
@ -183,7 +184,7 @@ var startPaymentProcessor = function(poolConfigs){
|
|||
var enabledForAny = false;
|
||||
for (var pool in poolConfigs){
|
||||
var p = poolConfigs[pool];
|
||||
var enabled = p.shareProcessing && p.shareProcessing.internal && p.shareProcessing.internal.enabled;
|
||||
var enabled = !p.disabled && p.shareProcessing && p.shareProcessing.internal && p.shareProcessing.internal.enabled;
|
||||
if (enabled){
|
||||
enabledForAny = true;
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
global.algos = {
|
||||
|
||||
};
|
||||
//lets put all algo related properties in here as a global object. also put this in stratum module then borrow it for the portal.
|
|
@ -4,6 +4,8 @@ var colors = require('colors');
|
|||
|
||||
var severityToColor = function(severity, text) {
|
||||
switch(severity) {
|
||||
case 'special':
|
||||
return text.cyan.underline;
|
||||
case 'debug':
|
||||
return text.green;
|
||||
case 'warning':
|
||||
|
@ -19,7 +21,8 @@ var severityToColor = function(severity, text) {
|
|||
var severityValues = {
|
||||
'debug': 1,
|
||||
'warning': 2,
|
||||
'error': 3
|
||||
'error': 3,
|
||||
'special': 4
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -40,11 +40,13 @@ function SetupForPool(logger, poolOptions){
|
|||
|
||||
daemon.cmd('validateaddress', [poolOptions.address], function(result){
|
||||
if (!result[0].response || !result[0].response.ismine){
|
||||
logger.error(logSystem, logComponent, 'Daemon does not own pool address - payment processing can not be done with this daemon');
|
||||
logger.error(logSystem, logComponent,
|
||||
'Daemon does not own pool address - payment processing can not be done with this daemon');
|
||||
}
|
||||
});
|
||||
}).once('connectionFailed', function(error){
|
||||
logger.error(logSystem, logComponent, 'Failed to connect to daemon for payment processing: ' + JSON.stringify(error));
|
||||
logger.error(logSystem, logComponent, 'Failed to connect to daemon for payment processing: ' +
|
||||
JSON.stringify(error));
|
||||
}).on('error', function(error){
|
||||
logger.error(logSystem, logComponent);
|
||||
}).init();
|
||||
|
@ -73,6 +75,15 @@ function SetupForPool(logger, poolOptions){
|
|||
connectToRedis();
|
||||
|
||||
|
||||
/* Number.toFixed gives us the decimal places we want, but as a string. parseFloat turns it back into number
|
||||
we don't care about trailing zeros in this case. */
|
||||
var toPrecision = function(value, precision){
|
||||
return parseFloat(value.toFixed(precision));
|
||||
};
|
||||
|
||||
|
||||
/* Deal with numbers in smallest possible units (satoshis) as much as possible. This greatly helps with accuracy
|
||||
when rounding and whatnot. When we are storing numbers for only humans to see, store in whole coin units. */
|
||||
|
||||
var processPayments = function(){
|
||||
async.waterfall([
|
||||
|
@ -120,13 +131,15 @@ function SetupForPool(logger, poolOptions){
|
|||
daemon.batchCmd(batchRPCcommand, function(error, txDetails){
|
||||
|
||||
if (error || !txDetails){
|
||||
callback('Check finished - daemon rpc error with batch gettransactions ' + JSON.stringify(error));
|
||||
callback('Check finished - daemon rpc error with batch gettransactions ' +
|
||||
JSON.stringify(error));
|
||||
return;
|
||||
}
|
||||
|
||||
txDetails = txDetails.filter(function(tx){
|
||||
if (tx.error || !tx.result){
|
||||
logger.error(logSystem, logComponent, 'error with requesting transaction from block daemon: ' + JSON.stringify(tx));
|
||||
logger.error(logSystem, logComponent,
|
||||
'error with requesting transaction from block daemon: ' + JSON.stringify(tx));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -135,11 +148,13 @@ function SetupForPool(logger, poolOptions){
|
|||
|
||||
var magnitude;
|
||||
|
||||
//Filter out all rounds that are immature (not confirmed or orphaned yet)
|
||||
rounds = rounds.filter(function(r){
|
||||
var tx = txDetails.filter(function(tx){return tx.result.txid === r.txHash})[0];
|
||||
|
||||
if (!tx){
|
||||
logger.error(logSystem, logComponent, 'daemon did not give us back a transaction that we asked for: ' + r.txHash);
|
||||
logger.error(logSystem, logComponent,
|
||||
'daemon did not give us back a transaction that we asked for: ' + r.txHash);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -148,15 +163,25 @@ function SetupForPool(logger, poolOptions){
|
|||
if (r.category === 'generate'){
|
||||
r.amount = tx.result.amount;
|
||||
|
||||
/* Here we calculate the smallest unit in this coin's currency; the 'satoshi'.
|
||||
The rpc.getblocktemplate.amount tells us how much we get in satoshis, while the
|
||||
rpc.gettransaction.amount tells us how much we get in whole coin units. Therefore,
|
||||
we simply divide the two to get the magnitude. I don't know math, there is probably
|
||||
a better term than 'magnitude'. Sue me or do a pull request to fix it. */
|
||||
var roundMagnitude = r.reward / r.amount;
|
||||
|
||||
if (!magnitude){
|
||||
magnitude = roundMagnitude;
|
||||
|
||||
if (roundMagnitude % 10 !== 0)
|
||||
logger.error(logSystem, logComponent, 'Satosihis in coin is not divisible by 10 which is very odd');
|
||||
logger.error(logSystem, logComponent,
|
||||
'Satosihis in coin is not divisible by 10 which is very odd');
|
||||
}
|
||||
else if (magnitude != roundMagnitude){
|
||||
logger.error(logSystem, logComponent, 'Magnitude in a round was different than in another round. HUGE PROBLEM.');
|
||||
/* Magnitude for a coin should ALWAYS be the same. For BTC and most coins there are
|
||||
100,000,000 satoshis in one coin unit. */
|
||||
logger.error(logSystem, logComponent,
|
||||
'Magnitude in a round was different than in another round. HUGE PROBLEM.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -200,12 +225,18 @@ function SetupForPool(logger, poolOptions){
|
|||
var workerShares = allWorkerShares[i];
|
||||
|
||||
if (round.category === 'orphan'){
|
||||
/* Each block that gets orphaned, all the shares go into the current round so that miners
|
||||
still get a reward for their work. This seems unfair to those that just started mining
|
||||
during this current round, but over time it balances out and rewards loyal miners. */
|
||||
Object.keys(workerShares).forEach(function(worker){
|
||||
orphanMergeCommands.push(['hincrby', coin + '_shares:roundCurrent', worker, workerShares[worker]]);
|
||||
orphanMergeCommands.push(['hincrby', coin + '_shares:roundCurrent',
|
||||
worker, workerShares[worker]]);
|
||||
});
|
||||
}
|
||||
else if (round.category === 'generate'){
|
||||
|
||||
/* We found a confirmed block! Now get the reward for it and calculate how much
|
||||
we owe each miner based on the shares they submitted during that block round. */
|
||||
var reward = round.reward * (1 - processingConfig.feePercent);
|
||||
|
||||
var totalShares = Object.keys(workerShares).reduce(function(p, c){
|
||||
|
@ -272,6 +303,8 @@ function SetupForPool(logger, poolOptions){
|
|||
var balanceUpdateCommands = [];
|
||||
var workerPayoutsCommand = [];
|
||||
|
||||
/* Here we add up all workers' previous unpaid balances plus their current rewards as we are
|
||||
about to check if they reach the payout threshold. */
|
||||
for (var worker in workerRewards){
|
||||
workerPayments[worker] = ((workerPayments[worker] || 0) + workerRewards[worker]);
|
||||
}
|
||||
|
@ -279,16 +312,32 @@ function SetupForPool(logger, poolOptions){
|
|||
workerPayments[worker] = ((workerPayments[worker] || 0) + workerBalances[worker]);
|
||||
}
|
||||
|
||||
/* Here we check if any of the workers reached their payout threshold, or delete them from the
|
||||
pending payment ledger (the workerPayments object). */
|
||||
if (Object.keys(workerPayments).length > 0){
|
||||
var coinPrecision = magnitude.toString().length - 1;
|
||||
for (var worker in workerPayments){
|
||||
if (workerPayments[worker] < processingConfig.minimumPayment * magnitude){
|
||||
balanceUpdateCommands.push(['hincrby', coin + '_balances', worker, workerRewards[worker]]);
|
||||
/* The workers total earnings (balance + current reward) was not enough to warrant
|
||||
a transaction, so we will store their balance in the database. Next time they
|
||||
are rewarded it might reach the payout threshold. */
|
||||
balanceUpdateCommands.push([
|
||||
'hincrby',
|
||||
coin + '_balances',
|
||||
worker,
|
||||
workerRewards[worker]
|
||||
]);
|
||||
delete workerPayments[worker];
|
||||
}
|
||||
else{
|
||||
//If worker had a balance that is about to be paid out, subtract it from the database
|
||||
if (workerBalances[worker] !== 0){
|
||||
balanceUpdateCommands.push(['hincrby', coin + '_balances', worker, -1 * workerBalances[worker]]);
|
||||
balanceUpdateCommands.push([
|
||||
'hincrby',
|
||||
coin + '_balances',
|
||||
worker,
|
||||
-1 * workerBalances[worker]
|
||||
]);
|
||||
}
|
||||
var rewardInPrecision = (workerRewards[worker] / magnitude).toFixed(coinPrecision);
|
||||
workerPayoutsCommand.push(['hincrbyfloat', coin + '_payouts', worker, rewardInPrecision]);
|
||||
|
@ -299,19 +348,23 @@ function SetupForPool(logger, poolOptions){
|
|||
}
|
||||
|
||||
// txfee included in feeAmountToBeCollected
|
||||
var feeAmountToBeCollected = parseFloat((toBePaid / (1 - processingConfig.feePercent) * processingConfig.feePercent).toFixed(coinPrecision));
|
||||
var leftOver = toBePaid / (1 - processingConfig.feePercent);
|
||||
var feeAmountToBeCollected = toPrecision(leftOver * processingConfig.feePercent, coinPrecision);
|
||||
var balanceLeftOver = totalBalance - toBePaid - feeAmountToBeCollected;
|
||||
var minReserveSatoshis = processingConfig.minimumReserve * magnitude;
|
||||
if (balanceLeftOver < minReserveSatoshis){
|
||||
|
||||
callback('Check finished - payments would wipe out minimum reserve, tried to pay out ' + toBePaid +
|
||||
' and collect ' + feeAmountToBeCollected + ' as fees' +
|
||||
/* TODO: Need to convert all these variables into whole coin units before displaying because
|
||||
humans aren't good at reading satoshi units. */
|
||||
callback('Check finished - payments would wipe out minimum reserve, tried to pay out ' +
|
||||
toBePaid + ' and collect ' + feeAmountToBeCollected + ' as fees' +
|
||||
' but only have ' + totalBalance + '. Left over balance would be ' + balanceLeftOver +
|
||||
', needs to be at least ' + minReserveSatoshis);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Move pending blocks into either orphan for confirmed sets, and delete their no longer
|
||||
required round/shares data. */
|
||||
var movePendingCommands = [];
|
||||
var roundsToDelete = [];
|
||||
rounds.forEach(function(r){
|
||||
|
@ -357,7 +410,7 @@ function SetupForPool(logger, poolOptions){
|
|||
var finalizeRedisTx = function(){
|
||||
redisClient.multi(finalRedisCommands).exec(function(error, results){
|
||||
if (error){
|
||||
callback('Check finished - error with final redis commands for cleaning up ' + JSON.stringify(error));
|
||||
callback('Error with final redis commands for cleaning up ' + JSON.stringify(error));
|
||||
return;
|
||||
}
|
||||
logger.debug(logSystem, logComponent, 'Payments processing performed an interval');
|
||||
|
@ -369,22 +422,27 @@ function SetupForPool(logger, poolOptions){
|
|||
}
|
||||
else{
|
||||
|
||||
//This is how many decimal places to round a coin down to
|
||||
var coinPrecision = magnitude.toString().length - 1;
|
||||
var addressAmounts = {};
|
||||
var totalAmountUnits = 0;
|
||||
for (var address in workerPayments){
|
||||
var coinUnits = parseFloat((workerPayments[address] / magnitude).toFixed(coinPrecision));
|
||||
var coinUnits = toPrecision(workerPayments[address] / magnitude, coinPrecision);
|
||||
addressAmounts[address] = coinUnits;
|
||||
totalAmountUnits += coinUnits;
|
||||
}
|
||||
|
||||
logger.debug(logSystem, logComponent, 'Payments about to be sent to: ' + JSON.stringify(addressAmounts));
|
||||
logger.debug(logSystem, logComponent, 'Payments to be sent to: ' + JSON.stringify(addressAmounts));
|
||||
|
||||
daemon.cmd('sendmany', ['', addressAmounts], function(results){
|
||||
|
||||
if (results[0].error){
|
||||
callback('Check finished - error with sendmany ' + JSON.stringify(results[0].error));
|
||||
return;
|
||||
}
|
||||
|
||||
finalizeRedisTx();
|
||||
|
||||
var totalWorkers = Object.keys(workerPayments).length;
|
||||
logger.debug(logSystem, logComponent, 'Payments sent, a total of ' + totalAmountUnits + ' ' + poolOptions.coin.symbol +
|
||||
' was sent to ' + totalWorkers + ' miners');
|
||||
|
|
Loading…
Reference in New Issue