Working on stats and payment processing
This commit is contained in:
parent
912e3682be
commit
ed9e61b8ee
12
README.md
12
README.md
@ -313,11 +313,11 @@ For more information on these configuration options see the [pool module documen
|
|||||||
1. In `config.json` set the port and password for `blockNotifyListener`
|
1. In `config.json` set the port and password for `blockNotifyListener`
|
||||||
2. In your daemon conf file set the `blocknotify` command to use:
|
2. In your daemon conf file set the `blocknotify` command to use:
|
||||||
```
|
```
|
||||||
[path to scripts/blockNotify.js] [listener host]:[listener port] [listener password] [coin name in config] %s
|
node [path to scripts/blockNotify.js] [listener host]:[listener port] [listener password] [coin name in config] %s
|
||||||
```
|
```
|
||||||
Example: inside `dogecoin.conf` add the line
|
Example: inside `dogecoin.conf` add the line
|
||||||
```
|
```
|
||||||
blocknotify="scripts/blockNotify.js localhost:8117 mySuperSecurePassword dogecoin %s"
|
blocknotify="node scripts/blockNotify.js localhost:8117 mySuperSecurePassword dogecoin %s"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -344,6 +344,14 @@ To support development of this project feel free to donate :)
|
|||||||
|
|
||||||
BTC: 1KRotMnQpxu3sePQnsVLRy3EraRFYfJQFR
|
BTC: 1KRotMnQpxu3sePQnsVLRy3EraRFYfJQFR
|
||||||
|
|
||||||
|
|
||||||
|
Credits
|
||||||
|
-------
|
||||||
|
* [vekexasia](https://github.com/vekexasia) - co-developer & great tester
|
||||||
|
* [TheSeven](https://github.com/TheSeven) - answering an absurd amount of my questions and being a very helpful and king gentleman
|
||||||
|
* Those that contributed to [node-stratum](/zone117x/node-stratum)
|
||||||
|
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
Released under the GNU General Public License v2
|
Released under the GNU General Public License v2
|
||||||
|
|||||||
14
config.json
14
config.json
@ -2,7 +2,7 @@
|
|||||||
"logLevel": "debug",
|
"logLevel": "debug",
|
||||||
"clustering": {
|
"clustering": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"forks": "1"
|
"forks": "auto"
|
||||||
},
|
},
|
||||||
"blockNotifyListener": {
|
"blockNotifyListener": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
@ -16,8 +16,13 @@
|
|||||||
"redisHost" : "hostname",
|
"redisHost" : "hostname",
|
||||||
"psubscribeKey" : "newblocks:*"
|
"psubscribeKey" : "newblocks:*"
|
||||||
},
|
},
|
||||||
|
"website": {
|
||||||
|
"enabled": false,
|
||||||
|
"port": 80,
|
||||||
|
"statUpdateInterval": 3
|
||||||
|
},
|
||||||
"proxy": {
|
"proxy": {
|
||||||
"enabled": true,
|
"enabled": false,
|
||||||
"ports": {
|
"ports": {
|
||||||
"80": {
|
"80": {
|
||||||
"diff": 32,
|
"diff": 32,
|
||||||
@ -50,10 +55,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"website": {
|
|
||||||
"enabled": false,
|
|
||||||
"port": 80,
|
|
||||||
"liveStats": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
5
init.js
5
init.js
@ -150,6 +150,9 @@ var startBlockListener = function(portalConfig){
|
|||||||
var startRedisBlockListener = function(portalConfig){
|
var startRedisBlockListener = function(portalConfig){
|
||||||
//block notify options
|
//block notify options
|
||||||
//setup block notify here and use IPC to tell appropriate pools
|
//setup block notify here and use IPC to tell appropriate pools
|
||||||
|
|
||||||
|
if (!portalConfig.redisBlockNotifyListener.enabled) return;
|
||||||
|
|
||||||
var listener = new RedisBlocknotifyListener(portalConfig.redisBlockNotifyListener);
|
var listener = new RedisBlocknotifyListener(portalConfig.redisBlockNotifyListener);
|
||||||
listener.on('log', function(text){
|
listener.on('log', function(text){
|
||||||
logDebug('blocknotify', 'system', text);
|
logDebug('blocknotify', 'system', text);
|
||||||
@ -178,7 +181,6 @@ var startPaymentProcessor = function(poolConfigs){
|
|||||||
|
|
||||||
|
|
||||||
var startWebsite = function(portalConfig, poolConfigs){
|
var startWebsite = function(portalConfig, poolConfigs){
|
||||||
console.log(portalConfig.website);
|
|
||||||
|
|
||||||
if (!portalConfig.website.enabled) return;
|
if (!portalConfig.website.enabled) return;
|
||||||
|
|
||||||
@ -209,7 +211,6 @@ var startWebsite = function(portalConfig, poolConfigs){
|
|||||||
startRedisBlockListener(portalConfig);
|
startRedisBlockListener(portalConfig);
|
||||||
|
|
||||||
startWorkerListener(poolConfigs);
|
startWorkerListener(poolConfigs);
|
||||||
;
|
|
||||||
|
|
||||||
startWebsite(portalConfig, poolConfigs);
|
startWebsite(portalConfig, poolConfigs);
|
||||||
|
|
||||||
|
|||||||
12
libs/api.js
12
libs/api.js
@ -40,8 +40,20 @@ module.exports = function(logger, poolConfigs){
|
|||||||
setInterval(clearExpiredHashrates, 10 * 60 * 1000);
|
setInterval(clearExpiredHashrates, 10 * 60 * 1000);
|
||||||
clearExpiredHashrates();
|
clearExpiredHashrates();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.getStats = function(callback){
|
this.getStats = function(callback){
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
{ global: {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
//get stats like hashrate and in/valid shares/blocks and workers in current round
|
//get stats like hashrate and in/valid shares/blocks and workers in current round
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -53,11 +53,11 @@ var PoolLogger = function (configuration) {
|
|||||||
|
|
||||||
var desc = poolName ? '[' + poolName + '] ' : '';
|
var desc = poolName ? '[' + poolName + '] ' : '';
|
||||||
console.log(
|
console.log(
|
||||||
'\u001b['+getSeverityColor(severity)+'m' +
|
'\u001b[' + getSeverityColor(severity) + 'm' +
|
||||||
dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss') +
|
dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss') +
|
||||||
" ["+key+"]" + '\u001b[39m: ' + "\t" +
|
" [" + key + "]" + '\u001b[39m: ' + "\t" +
|
||||||
desc +
|
desc + text
|
||||||
text);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// public
|
// public
|
||||||
|
|||||||
@ -79,6 +79,48 @@ function SetupForPool(logger, poolOptions){
|
|||||||
connectToRedis();
|
connectToRedis();
|
||||||
|
|
||||||
|
|
||||||
|
/* When blocks or orphaned, all shares contributed to that round would receive no reward. That doesn't seem fair
|
||||||
|
so we still all the shares from an orphaned rounds into the current round.
|
||||||
|
*/
|
||||||
|
var adoptOrphanRounds = function(rounds){
|
||||||
|
|
||||||
|
var shareLookups = rounds.map(function(r){
|
||||||
|
return ['hgetall', coin + '_shares:round' + r.height]
|
||||||
|
});
|
||||||
|
|
||||||
|
var shareIncries = [];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
redisClient.multi(shareLookups).exec(function(error, allWorkerShares){
|
||||||
|
if (error){
|
||||||
|
paymentLogger.error('redis', 'Error with multi get rounds share for adopting orphan');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var workerRewards = {};
|
||||||
|
|
||||||
|
|
||||||
|
for (var i = 0; i < rounds.length; i++){
|
||||||
|
var round = rounds[i];
|
||||||
|
var workerShares = allWorkerShares[i];
|
||||||
|
|
||||||
|
var reward = round.reward * (1 - processingConfig.feePercent);
|
||||||
|
|
||||||
|
var totalShares = Object.keys(workerShares).reduce(function(p, c){
|
||||||
|
return p + parseInt(workerShares[c])
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
|
||||||
|
for (var worker in workerShares){
|
||||||
|
var percent = parseInt(workerShares[worker]) / totalShares;
|
||||||
|
var workerRewardTotal = Math.floor(reward * percent);
|
||||||
|
if (!(worker in workerRewards)) workerRewards[worker] = 0;
|
||||||
|
workerRewards[worker] += workerRewardTotal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
var processPayments = function(){
|
var processPayments = function(){
|
||||||
@ -125,29 +167,61 @@ function SetupForPool(logger, poolOptions){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (var i = txDetails.length; i > 0; --i){
|
||||||
|
var tx = txDetails[i];
|
||||||
|
if (tx.error || !tx.result){
|
||||||
|
console.log('error with requesting transaction from block daemon: ' + JSON.stringify(t));
|
||||||
|
txDetails.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var orphanedRounds = [];
|
||||||
|
var confirmedRounds = [];
|
||||||
//Rounds that are not confirmed yet are removed from the round array
|
//Rounds that are not confirmed yet are removed from the round array
|
||||||
//We also get reward amount for each block from daemon reply
|
//We also get reward amount for each block from daemon reply
|
||||||
rounds = rounds.filter(function(r){
|
rounds.forEach(function(r){
|
||||||
var tx = txDetails.filter(function(t){ return t.result.txid === r.txHash; })[0];
|
|
||||||
if (tx.result.details[0].category !== 'generate') return false;
|
var tx = txDetails.filter(function(tx){return tx.result.txid === r.txHash})[0];
|
||||||
r.amount = tx.result.amount;
|
|
||||||
r.magnitude = r.reward / r.amount;
|
if (!tx){
|
||||||
return true;
|
console.log('daemon did not give us back a transaction that we asked for: ' + r.txHash);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
r.category = tx.result.details[0].category;
|
||||||
|
|
||||||
|
if (r.category === 'orphan'){
|
||||||
|
orphanedRounds.push(r);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (r.category === 'generate'){
|
||||||
|
r.amount = tx.result.amount;
|
||||||
|
r.magnitude = r.reward / r.amount;
|
||||||
|
confirmedRounds.push(r);
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (rounds.length === 0){
|
if (orphanedRounds.length === 0 && confirmedRounds.length === 0){
|
||||||
callback('done - no confirmed transactions yet');
|
callback('done - no confirmed or orhpaned rounds');
|
||||||
return;
|
}
|
||||||
|
else{
|
||||||
|
callback(null, confirmedRounds, orphanedRounds);
|
||||||
}
|
}
|
||||||
callback(null, rounds);
|
|
||||||
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/* Does a batch redis call to get shares contributed to each round. Then calculates the reward
|
/* Does a batch redis call to get shares contributed to each round. Then calculates the reward
|
||||||
amount owned to each miner for each round. */
|
amount owned to each miner for each round. */
|
||||||
function(rounds, callback){
|
function(confirmedRounds, orphanedRounds, callback){
|
||||||
|
|
||||||
|
var rounds = [];
|
||||||
|
for (var i = 0; i < orphanedRounds; i++) rounds.push(orphanedRounds[i]);
|
||||||
|
for (var i = 0; i < confirmedRounds; i++) rounds.push(confirmedRounds[i]);
|
||||||
|
|
||||||
|
|
||||||
var shareLookups = rounds.map(function(r){
|
var shareLookups = rounds.map(function(r){
|
||||||
@ -160,6 +234,16 @@ function SetupForPool(logger, poolOptions){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var orphanMergeCommands = []
|
||||||
|
for (var i = 0; i < orphanedRounds.length; i++){
|
||||||
|
var workerShares = allWorkerShares[i];
|
||||||
|
Object.keys(workerShares).forEach(function(worker){
|
||||||
|
orphanMergeCommands.push(['hincrby', coin + '_shares:roundCurrent', worker, workerShares[worker]]);
|
||||||
|
});
|
||||||
|
orphanMergeCommands.push([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var workerRewards = {};
|
var workerRewards = {};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -94,10 +94,9 @@ module.exports = function(logger){
|
|||||||
var shareProcessor = new ShareProcessor(poolLogger, poolOptions)
|
var shareProcessor = new ShareProcessor(poolLogger, poolOptions)
|
||||||
|
|
||||||
handlers.auth = function(workerName, password, authCallback){
|
handlers.auth = function(workerName, password, authCallback){
|
||||||
authCallback({
|
pool.daemon.cmd('validateaddress', [workerName], function(results){
|
||||||
error: null,
|
var isValid = results.filter(function(r){return r.response.isvalid}).length > 0;
|
||||||
authorized: true,
|
authCallback(isValid);
|
||||||
disconnect: false
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -74,10 +74,7 @@ module.exports = function(logger, poolConfig){
|
|||||||
|
|
||||||
connection.multi(redisCommands).exec(function(err, replies){
|
connection.multi(redisCommands).exec(function(err, replies){
|
||||||
if (err)
|
if (err)
|
||||||
console.log('error with share processor multi ' + JSON.stringify(err));
|
logger.error('redis', 'error with share processor multi ' + JSON.stringify(err));
|
||||||
else{
|
|
||||||
console.log(JSON.stringify(replies));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"disabled": true,
|
"disabled": false,
|
||||||
"coin": "litecoin.json",
|
"coin": "litecoin.json",
|
||||||
|
|
||||||
"shareProcessing": {
|
"shareProcessing": {
|
||||||
@ -48,8 +48,8 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"ports": {
|
"ports": {
|
||||||
"3032": {
|
"3008":{
|
||||||
"diff": 32,
|
"diff": 8,
|
||||||
"varDiff": {
|
"varDiff": {
|
||||||
"minDiff": 8,
|
"minDiff": 8,
|
||||||
"maxDiff": 512,
|
"maxDiff": 512,
|
||||||
@ -58,6 +58,9 @@
|
|||||||
"variancePercent": 30
|
"variancePercent": 30
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"3032": {
|
||||||
|
"diff": 32
|
||||||
|
},
|
||||||
"3256": {
|
"3256": {
|
||||||
"diff": 256
|
"diff": 256
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,10 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
/**
|
/*
|
||||||
* This script should be hooked to the coin daemon as follow:
|
This script should be hooked to the coin daemon as follow:
|
||||||
*
|
litecoind -blocknotify="node /path/to/this/script/blockNotify.js localhost:8117 password litecoin %s"
|
||||||
* litecoind -blocknotify="/path/to/this/script/blockNotify.js localhost:8117 password litecoin %s"
|
The above will send tell litecoin to launch this script with those parameters every time a block is found.
|
||||||
*
|
This script will then send the blockhash along with other information to a listening tcp socket
|
||||||
* The above will send tell litecoin to launch this script with those parameters every time
|
*/
|
||||||
* a block is found.
|
|
||||||
* This script will then send the blockhash along with other informations to a listening tcp socket
|
|
||||||
**/
|
|
||||||
|
|
||||||
var net = require('net');
|
var net = require('net');
|
||||||
var config = process.argv[1];
|
var config = process.argv[1];
|
||||||
|
|||||||
33
statsExampleJson.js
Normal file
33
statsExampleJson.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
var stats = {
|
||||||
|
|
||||||
|
global:{
|
||||||
|
hashrate: 1000, //in KH/s
|
||||||
|
validShares: 1,
|
||||||
|
invalidShares: 1,
|
||||||
|
validBlocks: 1,
|
||||||
|
invalidBlocks: 1,
|
||||||
|
blocksPending: 1,
|
||||||
|
blocksConfirmed: 1,
|
||||||
|
blocksOrphaned: 1,
|
||||||
|
connectedMiners: 344
|
||||||
|
},
|
||||||
|
pools:[
|
||||||
|
{
|
||||||
|
coin: "Dogecoin",
|
||||||
|
sybmol: 'doge',
|
||||||
|
stats:{
|
||||||
|
hashrate: 1000, //in KH/s
|
||||||
|
validShares: 1,
|
||||||
|
invalidShares: 1,
|
||||||
|
validBlocks: 1,
|
||||||
|
invalidBlocks: 1,
|
||||||
|
blocksPending: 1,
|
||||||
|
blocksConfirmed: 1,
|
||||||
|
blocksOrphaned: 1,
|
||||||
|
connectedMiners: 34545,
|
||||||
|
totalPayedOut: 3343.789797
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user