merged-pooler
Advanced tools
Comparing version 0.1.15 to 0.1.16
105
example.js
var Stratum = require('./lib/index.js'); | ||
var myCoin = { | ||
"name": "Litecoin", | ||
"symbol": "LTC", | ||
"name": "Catcoin", | ||
"symbol": "CTC", | ||
"algorithm": "scrypt" | ||
@@ -10,4 +10,4 @@ }; | ||
var myAuxCoins = [{ | ||
"name": "Dogecoin", | ||
"symbol": "DOGE", | ||
"name": "LottoShares", | ||
"symbol": "LTS", | ||
"algorithm": "scrypt", | ||
@@ -19,7 +19,21 @@ | ||
"host": "127.0.0.1", | ||
"port": 22555, // auxcoin daemon RPC port | ||
"user": "rpcuser", | ||
"password": "supersecret" | ||
"port": 23327, // **NOT ACTUAL PORT** | ||
"user": "lottosharesrpc", | ||
"password": "By66dCmyX44uUbA7P3qqXJQeT3Ywd8dZ4dJdfgxCAxbg" | ||
} | ||
], | ||
], | ||
}, { | ||
"name": "Syscoin", | ||
"symbol": "SYS", | ||
"algorithm": "scrypt", | ||
/* */ | ||
"daemons": [ | ||
{ //Main daemon instance | ||
"host": "127.0.0.1", | ||
"port": 23327, // **NOT ACTUAL PORT** | ||
"user": "syscoinrpc", | ||
"password": "By66dCmyX44uUbA7P3qqXJQeT3Ywd8dZ4dJdfgxCAxbg" | ||
} | ||
], | ||
}]; | ||
@@ -34,3 +48,3 @@ | ||
// Payout address - for primary node only | ||
"address": "This is a LTC address", //Address to where block rewards are given; | ||
"address": "9iKR9FwwEbWCiU3ZhCAs5dQRVkYoC288Go", //Address to where block rewards are given; | ||
// shared between all coins, so copy your private key over with dumpprivkey and importprivkey | ||
@@ -43,6 +57,5 @@ | ||
"rewardRecipients": { | ||
/* You will want a universal address here to cover all your coins. Possibly hack on | ||
https://github.com/chiguireitor/nomp-pubkey-generator to get a good idea. | ||
Feel free to donate using the BTC address 1BRUcdAdjdQoAgUwcN7nbNDzqhL92ub3xE */ | ||
"The address for stratum fees": 0.1 | ||
/* 0.1% donation to NOMP. This pubkey can accept any type of coin, please leave this in | ||
your config to help support NOMP development. */ | ||
"22851477d63a085dbc2398c8430af1c09e7343f6": 0.1 | ||
}, | ||
@@ -89,3 +102,3 @@ | ||
"ports": { | ||
"3333": { //A port for your miners to connect to | ||
"3032": { //A port for your miners to connect to | ||
"diff": 32, //the pool difficulty for this port | ||
@@ -97,10 +110,10 @@ | ||
"minDiff": 8, //Minimum difficulty | ||
"maxDiff": 8192, //Network difficulty will be used if it is lower than this | ||
"maxDiff": 512, //Network difficulty will be used if it is lower than this | ||
"targetTime": 15, //Try to get 1 share per this many seconds | ||
"retargetTime": 90, //Check to see if we should retarget every this many seconds | ||
"variancePercent": 30 //Allow time to vary this % from target without retargeting | ||
"variancePercent": 30 //Allow time to very this % from target without retargeting | ||
} | ||
}, | ||
"3334": { //Another port for your miners to connect to, this port does not use varDiff | ||
"diff": 2048 //The pool difficulty | ||
"3256": { //Another port for your miners to connect to, this port does not use varDiff | ||
"diff": 256 //The pool difficulty | ||
} | ||
@@ -121,4 +134,4 @@ }, | ||
"port": 9932, | ||
"user": "litecoinrpc", | ||
"password": "supersecretpassword" | ||
"user": "catcoinrpc", | ||
"password": "74QL5rZ9h8xZbLmwdNzW3cBRjJNQ6fy3b8pB5bJ6oURF" | ||
} | ||
@@ -186,12 +199,50 @@ ], | ||
pool.on('share', function(isValidShare, isValidBlock, data){ | ||
if (isValidShare) | ||
console.log('Valid share submitted'); | ||
else if (data.blockHash) | ||
console.log('Nu uh uh, not today! We thought a block was found but it was rejected by the daemon'); | ||
else | ||
console.log('Try again! Invalid share submitted'); | ||
console.log('share data: ' + JSON.stringify(data)); | ||
//maincoin | ||
var coin = myCoin.name; | ||
storeShare(isValidShare, isValidBlock, data, coin); | ||
//loop through auxcoins | ||
for(var i = 0; i < myAuxCoins.length; i++) { | ||
coin = myAuxCoins[i].name; | ||
storeShare(isValidShare, isValidBlock, data, coin); | ||
} | ||
}); | ||
//store each share/block submission for a given coin | ||
function storeShare(isValidShare, isValidBlock, data, coin) { | ||
var redisCommands = []; | ||
if (isValidShare){ | ||
redisCommands.push(['hincrbyfloat', coin + ':shares:roundCurrent', data.worker, data.difficulty]); | ||
redisCommands.push(['hincrby', coin + ':stats', 'validShares', 1]); | ||
} | ||
else{ | ||
redisCommands.push(['hincrby', coin + ':stats', 'invalidShares', 1]); | ||
} | ||
/* Stores share diff, worker, and unique value with a score that is the timestamp. Unique value ensures it | ||
doesn't overwrite an existing entry, and timestamp as score lets us query shares from last X minutes to | ||
generate hashrate for each worker and pool. */ | ||
var dateNow = Date.now(); | ||
var hashrateData = [ isValidShare ? data.difficulty : -data.difficulty, data.worker, dateNow]; | ||
redisCommands.push(['zadd', coin + ':hashrate', dateNow / 1000 | 0, hashrateData.join(':')]); | ||
if (isValidBlock){ | ||
redisCommands.push(['rename', coin + ':shares:roundCurrent', coin + ':shares:round' + data.height]); | ||
redisCommands.push(['sadd', coin + ':blocksPending', [data.blockHash, data.txHash, data.height].join(':')]); | ||
redisCommands.push(['hincrby', coin + ':stats', 'validBlocks', 1]); | ||
} | ||
else if (shareData.blockHash){ | ||
redisCommands.push(['hincrby', coin + ':stats', 'invalidBlocks', 1]); | ||
} | ||
connection.multi(redisCommands).exec(function(err, replies){ | ||
if (err) | ||
logger.error(logSystem, logComponent, logSubCat, 'Error with share processor multi ' + JSON.stringify(err)); | ||
}); | ||
console.log('share data: ' + JSON.stringify(data)); | ||
} | ||
/* | ||
@@ -198,0 +249,0 @@ Called when a block, auxillery or primary, is found |
@@ -29,2 +29,15 @@ var bignum = require('bignum'); | ||
}, | ||
'scrypt-og': { | ||
//Aiden settings | ||
//Uncomment diff if you want to use hardcoded truncated diff | ||
//diff: '0000ffff00000000000000000000000000000000000000000000000000000000', | ||
multiplier: Math.pow(2, 16), | ||
hash: function(coinConfig){ | ||
var nValue = coinConfig.nValue || 64; | ||
var rValue = coinConfig.rValue || 1; | ||
return function(data){ | ||
return multiHashing.scrypt(data,nValue,rValue); | ||
} | ||
} | ||
}, | ||
'scrypt-jane': { | ||
@@ -64,2 +77,9 @@ multiplier: Math.pow(2, 16), | ||
}, | ||
sha1: { | ||
hash: function(){ | ||
return function(){ | ||
return multiHashing.sha1.apply(this, arguments); | ||
} | ||
} | ||
}, | ||
x11: { | ||
@@ -166,3 +186,25 @@ hash: function(){ | ||
} | ||
} | ||
}, | ||
'yescrypt': { | ||
multiplier: Math.pow(2, 16), | ||
hash: function(){ | ||
return function(){ | ||
return multiHashing.yescrypt.apply(this, arguments); | ||
} | ||
} | ||
}, | ||
s3: { | ||
hash: function(){ | ||
return function(){ | ||
return multiHashing.s3.apply(this, arguments); | ||
} | ||
} | ||
}, | ||
dcrypt: { | ||
hash: function(){ | ||
return function(){ | ||
return multiHashing.dcrypt.apply(this, arguments); | ||
} | ||
} | ||
} | ||
}; | ||
@@ -169,0 +211,0 @@ |
@@ -100,2 +100,3 @@ var events = require('events'); | ||
case 'scrypt-n': | ||
case 'sha1': | ||
return function (d) { | ||
@@ -102,0 +103,0 @@ return util.reverseBuffer(util.sha256d(d)); |
@@ -25,6 +25,6 @@ var bignum = require('bignum'); | ||
var emitLog = function(text) { _this.emit('log', 'debug' , 'POOL', text); }; | ||
var emitWarningLog = function(text) { _this.emit('log', 'warning', 'POOL', text); }; | ||
var emitErrorLog = function(text) { _this.emit('log', 'error' , 'POOL', text); }; | ||
var emitSpecialLog = function(text) { _this.emit('log', 'special', 'POOL', text); }; | ||
var emitLog = function(text) { _this.emit('log', 'debug' , text); }; | ||
var emitWarningLog = function(text) { _this.emit('log', 'warning', text); }; | ||
var emitErrorLog = function(text) { _this.emit('log', 'error' , text); }; | ||
var emitSpecialLog = function(text) { _this.emit('log', 'special', text); }; | ||
@@ -65,9 +65,11 @@ | ||
var indexes = []; | ||
for(var i = 0;i < options.auxes.length;i++) indexes.push(i); | ||
if(options.auxes) { | ||
for(var i = 0;i < options.auxes.length;i++) indexes.push(i); | ||
} // or make sure options.auxes always exists, even if empty. This seems cleaner. | ||
_this.updateNeeded = false; | ||
async.each(indexes, GetAuxWork, function(err) { | ||
if(err) emitErrorLog('Could not update auxillery chains: ' + err); | ||
if(err) emitErrorLog('could not update auxillary chains: ' + err); | ||
if(_this.updateNeeded) { | ||
GetBlockTemplate(function() { | ||
emitLog('Updated work for auxillery chains'); | ||
emitLog('updating work for auxillary chains '); | ||
}, true); | ||
@@ -80,3 +82,3 @@ } | ||
function GetFirstJob(finishedCallback){ | ||
// Get work from auxillery chains every 5 seconds, perpetually | ||
// Get work from auxillary chains every 5 seconds, perpetually | ||
UpdateAuxes(function() { | ||
@@ -134,3 +136,3 @@ GetBlockTemplate(function(error, result){ | ||
emitSpecialLog(infoLines.join('\n\t\t\t\t\t\t')); | ||
emitSpecialLog(infoLines.join('\n\t\t\t\t\t\t')); | ||
} | ||
@@ -245,3 +247,3 @@ | ||
*/ | ||
function SubmitBlock(blockHex, callback){ | ||
function SubmitBlock(blockHex, finishedCallback){ | ||
@@ -277,3 +279,3 @@ var rpcCommand, rpcArgs; | ||
emitLog('Submitted Block using ' + rpcCommand + ' successfully to daemon instance(s)'); | ||
callback(); | ||
finishedCallback(); | ||
} | ||
@@ -308,7 +310,7 @@ ); | ||
if(result[0].error) { | ||
emitErrorLog('Failed to submit potential auxillery block: ' + result.error); | ||
emitErrorLog('Failed to submit potential auxiliary block: ' + JSON.stringify(result[0].error)); | ||
} | ||
else { | ||
if(!result[0].response) { | ||
emitWarningLog('Submitted block was rejected by the ' + aux.name + ' network! Check its log for more information'); | ||
emitWarningLog('Submitted block was rejected by the ' + _this.auxes[aux].name + ' network! Check its log for more information'); | ||
} | ||
@@ -369,7 +371,14 @@ } | ||
CheckBlockAccepted(hash, _this.auxes[aux].daemon, function(accepted, tx, height, value) { | ||
if(!accepted) emitErrorLog('Block was not detect to have been accepted by' + aux.name + ' network: ' + hash); | ||
// Push a message to alert that an auxillery block was found | ||
if(!accepted) emitErrorLog('Block was not detected to have been accepted by ' + _this.auxes[aux].name + ' network: ' + hash); | ||
// Push a message to alert that an auxillary block was found | ||
// First get transaction ID of our coinbase transaction | ||
_this.auxes[aux].daemon.cmd('gettransaction', [tx], function(err, res) { | ||
_this.emit('block', options.auxes[aux].symbol, height, hash, tx, res[0].details[0].amount, shareData.difficulty, shareData.worker); | ||
_this.auxes[aux].daemon.cmd('gettransaction', [tx], function(res) { | ||
var cmdResponse = res[0].response; | ||
var cmdError = res[0].error; | ||
if(cmdError) { | ||
emitErrorLog('Some error occured: ' + JSON.stringify(cmdError)); | ||
} | ||
else { | ||
_this.emit('block', options.auxes[aux].symbol, height, hash, tx, 0, cmdResponse.details[0].amount, shareData.difficulty, shareData.worker); | ||
} | ||
}); | ||
@@ -410,3 +419,3 @@ UpdateAuxes(); | ||
SubmitBlock(blockHex, function(){ | ||
CheckBlockAccepted(shareData.blockHash, _this.daemon, function(isAccepted, tx){ | ||
CheckBlockAccepted(shareData.blockHash, _this.daemon, function(isAccepted, tx, height, value){ | ||
isValidBlock = isAccepted; | ||
@@ -416,3 +425,3 @@ shareData.txHash = tx; | ||
// Also emit the new block callback | ||
_this.emit('block', options.coin.symbol, shareData.blockHash, tx, shareData.blockReward * 0.00000001, shareData.difficulty, shareData.worker); | ||
_this.emit('block', options.coin.symbol, height, shareData.blockHash, tx, shareData.blockReward * 0.00000001, shareData.difficulty, shareData.worker); | ||
GetBlockTemplate(function(error, result, foundNewBlock) { | ||
@@ -461,13 +470,16 @@ if (foundNewBlock) | ||
// Setup auxillery daemons | ||
// Setup auxillary daemons | ||
_this.auxes = []; | ||
for(var i = 0;i < options.auxes.length;i++) { | ||
if(!Array.isArray(options.auxes[i].daemons) || options.auxes[i].daemons.length < 1) { | ||
emitErrorLog('No daemons have been configured for the auxillery coin: ' + options.auxes[i].name + '. Please specify before the pool starts.'); | ||
_this.daemon = undefined; // Should force program to close naturally | ||
return; | ||
} | ||
var a = {}; | ||
a.daemon = setupDaemon(options.auxes[i].daemons); | ||
_this.auxes.push(a); | ||
if (options.auxes) { | ||
for(var i = 0;i < options.auxes.length;i++) { | ||
if(!Array.isArray(options.auxes[i].daemons) || options.auxes[i].daemons.length < 1) { | ||
emitErrorLog('No daemons have been configured for the auxillary coin: ' + options.auxes[i].name + '. Please specify before the pool starts.'); | ||
_this.daemon = undefined; // Should force program to close naturally | ||
return; | ||
} | ||
var a = {}; | ||
a.name = options.auxes[i].name; // I want at least this... | ||
a.daemon = setupDaemon(options.auxes[i].daemons); | ||
_this.auxes.push(a); | ||
} | ||
} | ||
@@ -673,3 +685,3 @@ | ||
if (foundNewBlock) | ||
emitLog('Block notification via RPC polling'); | ||
emitLog('getting block notification via RPC polling'); | ||
}); | ||
@@ -676,0 +688,0 @@ }, pollingInterval); |
@@ -21,3 +21,3 @@ var net = require('net'); | ||
/** | ||
* Defining each client that connects to the stratum server. | ||
* Defining each client that connects to the stratum server. | ||
* Emits: | ||
@@ -71,2 +71,18 @@ * - subscription(obj, cback(error, extraNonce1, extraNonce2Size)) | ||
break; | ||
case 'mining.get_multiplier': | ||
_this.emit('log', algos[options.coin.algorithm].multiplier); | ||
sendJson({ | ||
id : null, | ||
result : [algos[options.coin.algorithm].multiplier], | ||
method : "mining.get_multiplier" | ||
}); | ||
break; | ||
case 'ping': | ||
_this.lastActivity = Date.now(); | ||
sendJson({ | ||
id : null, | ||
result : [], | ||
method : "pong" | ||
}); | ||
break; | ||
case 'mining.submit': | ||
@@ -126,3 +142,3 @@ _this.lastActivity = Date.now(); | ||
_this.authorized = (!result.error && result.authorized); | ||
if (replyToSocket) { | ||
@@ -180,3 +196,3 @@ sendJson({ | ||
); | ||
} | ||
@@ -364,2 +380,3 @@ | ||
{ | ||
coin: options.coin, | ||
subscriptionId: subscriptionId, | ||
@@ -366,0 +383,0 @@ authorizeFn: authorizeFn, |
@@ -135,5 +135,11 @@ var util = require('./util.js'); | ||
if (rpcData.payee) { | ||
var payeeReward = Math.ceil(reward / 5); | ||
if (rpcData.payee) { | ||
var payeeReward = 0; | ||
if (rpcData.payee_amount) { | ||
payeeReward = rpcData.payee_amount; | ||
} else { | ||
payeeReward = Math.ceil(reward / 5); | ||
} | ||
reward -= payeeReward; | ||
@@ -140,0 +146,0 @@ rewardToPool -= payeeReward; |
{ | ||
"name": "merged-pooler", | ||
"version": "0.1.15", | ||
"version": "0.1.16", | ||
"description": "High performance Stratum poolserver in Node.js for merged mining", | ||
@@ -38,3 +38,4 @@ "keywords": [ | ||
"base58-native": "*", | ||
"async": "*" | ||
"async": "*", | ||
"redis": "*" | ||
}, | ||
@@ -41,0 +42,0 @@ "engines": { |
282
README.md
@@ -9,7 +9,7 @@ [![Build Status](https://travis-ci.org/sigwo/node-merged-pool.png?branch=master)](https://travis-ci.org/sigwo/node-merged-pool) | ||
* Multiple PoWAUX coins on each main chain | ||
* Multiple PoWAUX coins on each main chain ([ahmedbodi](//github.com/ahmedbodi)) | ||
* Profit Switching | ||
* Built-in stratum redundancy | ||
Frontend and payment enhancements will be a separate repo. (Insert repo URL when created) | ||
Frontend and payment enhancements will be a separate repo. [Merged Pooler](//github.com/sigwo/node-merged-portal) | ||
@@ -55,2 +55,3 @@ #### Why | ||
* ? *SHAvite-3* (INKcoin [INK]) | ||
* ? *Sha1* (Sha1coin [SHA], Yaycoin [YAY]) | ||
@@ -66,3 +67,3 @@ Not working currently: | ||
* Node v0.10+ | ||
* Coin daemon for primay and auxillery coins (preferably one with a relatively updated API and not some crapcoin :p) | ||
* Coin daemon for primary and auxillary coins | ||
* Patience :) | ||
@@ -78,17 +79,274 @@ | ||
cd node-merged-pool | ||
npm update | ||
npm install | ||
``` | ||
or | ||
```bash | ||
npm install merged-pooler | ||
#### Module usage | ||
======= | ||
Create the configuration for your coin: | ||
Possible options for `algorithm`: *sha256, scrypt, scrypt-jane, scrypt-n, quark, x11, keccak, blake, | ||
skein, groestl, fugue, shavite3, hefty1, qubit, or sha1*. | ||
```javascript | ||
var myCoin = { | ||
"name": "Dogecoin", | ||
"symbol": "DOGE", | ||
"algorithm": "scrypt", | ||
"nValue": 1024, //optional - defaults to 1024 | ||
"rValue": 1, //optional - defaults to 1 | ||
"txMessages": false, //optional - defaults to false, | ||
/* Magic value only required for setting up p2p block notifications. It is found in the daemon | ||
source code as the pchMessageStart variable. | ||
For example, litecoin mainnet magic: http://git.io/Bi8YFw | ||
And for litecoin testnet magic: http://git.io/NXBYJA */ | ||
"peerMagic": "fbc0b6db" //optional | ||
"peerMagicTestnet": "fcc1b7dc" //optional | ||
}; | ||
``` | ||
Note to self: Add actual instructions here. | ||
If you are using the `scrypt-jane` algorithm there are additional configurations: | ||
#### Module usage | ||
```javascript | ||
var myCoin = { | ||
"name": "Freecoin", | ||
"symbol": "FEC", | ||
"algorithm": "scrypt-jane", | ||
"chainStartTime": 1375801200, //defaults to 1367991200 (YACoin) if not used | ||
"nMin": 6, //defaults to 4 if not used | ||
"nMax": 32 //defaults to 30 if not used | ||
}; | ||
``` | ||
Please see the included example.js file for more information. This section will be | ||
expanded soon. | ||
If you are using the `scrypt-n` algorithm there is an additional configuration: | ||
```javascript | ||
var myCoin = { | ||
"name": "Execoin", | ||
"symbol": "EXE", | ||
"algorithm": "scrypt-n", | ||
/* This defaults to Vertcoin's timetable if not used. It is required for scrypt-n coins that | ||
have modified their N-factor timetable to be different than Vertcoin's. */ | ||
"timeTable": { | ||
"2048": 1390959880, | ||
"4096": 1438295269, | ||
"8192": 1485630658, | ||
"16384": 1532966047, | ||
"32768": 1580301436, | ||
"65536": 1627636825, | ||
"131072": 1674972214, | ||
"262144": 1722307603 | ||
} | ||
}; | ||
``` | ||
If you are using the `keccak` algorithm there are additional configurations *(The rare `normalHashing` keccak coins | ||
such as Copperlark and eCoin don't appear to work yet - only the popular ones like Maxcoin are)*: | ||
```javascript | ||
var myCoin = { | ||
"name": "eCoin", | ||
"symbol": "ECN", | ||
"algorithm": "keccak", | ||
/* This is not required and set to false by default. Some coins such as Copperlark and eCoin | ||
require it to be set to true. Maxcoin and most others are false. */ | ||
"normalHashing": true | ||
}; | ||
``` | ||
Create and start new pool with configuration options and authentication function | ||
```javascript | ||
var Stratum = require('stratum-pool'); | ||
var pool = Stratum.createPool({ | ||
"coin": myCoin, | ||
"address": "mi4iBXbBsydtcc5yFmsff2zCFVX4XG7qJc", //Address to where block rewards are given | ||
/* Block rewards go to the configured pool wallet address to later be paid out to miners, | ||
except for a percentage that can go to, for examples, pool operator(s) as pool fees or | ||
or to donations address. Addresses or hashed public keys can be used. Here is an example | ||
of rewards going to the main pool op, a pool co-owner, and NOMP donation. */ | ||
"rewardRecipients": { | ||
"n37vuNFkXfk15uFnGoVyHZ6PYQxppD3QqK": 1.5, //1.5% goes to pool op | ||
"mirj3LtZxbSTharhtXvotqtJXUY7ki5qfx": 0.5, //0.5% goes to a pool co-owner | ||
/* 0.1% donation to NOMP. This pubkey can accept any type of coin, please leave this in | ||
your config to help support NOMP development. */ | ||
"22851477d63a085dbc2398c8430af1c09e7343f6": 0.1 | ||
}, | ||
"blockRefreshInterval": 1000, //How often to poll RPC daemons for new blocks, in milliseconds | ||
/* Some miner apps will consider the pool dead/offline if it doesn't receive anything new jobs | ||
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, | ||
usually the workers are zombies and don't submit shares after connecting. This features | ||
detects those and disconnects them. */ | ||
"connectionTimeout": 600, //Remove workers that haven't been in contact for this many seconds | ||
/* Sometimes you want the block hashes even for shares that aren't block candidates. */ | ||
"emitInvalidBlockHashes": false, | ||
/* Enable for client IP addresses to be detected when using a load balancer with TCP proxy | ||
protocol enabled, such as HAProxy with 'send-proxy' param: | ||
http://haproxy.1wt.eu/download/1.5/doc/configuration.txt */ | ||
"tcpProxyProtocol": false, | ||
/* If a worker is submitting a high threshold of invalid shares we can temporarily ban their IP | ||
to reduce system/network load. Also useful to fight against flooding attacks. If running | ||
behind something like HAProxy be sure to enable 'tcpProxyProtocol', otherwise you'll end up | ||
banning your own IP address (and therefore all workers). */ | ||
"banning": { | ||
"enabled": true, | ||
"time": 600, //How many seconds to ban worker for | ||
"invalidPercent": 50, //What percent of invalid shares triggers ban | ||
"checkThreshold": 500, //Check invalid percent when this many shares have been submitted | ||
"purgeInterval": 300 //Every this many seconds clear out the list of old bans | ||
}, | ||
/* Each pool can have as many ports for your miners to connect to as you wish. Each port can | ||
be configured to use its own pool difficulty and variable difficulty settings. varDiff is | ||
optional and will only be used for the ports you configure it for. */ | ||
"ports": { | ||
"3032": { //A port for your miners to connect to | ||
"diff": 32, //the pool difficulty for this port | ||
/* Variable difficulty is a feature that will automatically adjust difficulty for | ||
individual miners based on their hashrate in order to lower networking overhead */ | ||
"varDiff": { | ||
"minDiff": 8, //Minimum difficulty | ||
"maxDiff": 512, //Network difficulty will be used if it is lower than this | ||
"targetTime": 15, //Try to get 1 share per this many seconds | ||
"retargetTime": 90, //Check to see if we should retarget every this many seconds | ||
"variancePercent": 30 //Allow time to very this % from target without retargeting | ||
} | ||
}, | ||
"3256": { //Another port for your miners to connect to, this port does not use varDiff | ||
"diff": 256 //The pool difficulty | ||
} | ||
}, | ||
/* Recommended to have at least two daemon instances running in case one drops out-of-sync | ||
or offline. For redundancy, all instances will be polled for block/transaction updates | ||
and be used for submitting blocks. Creating a backup daemon involves spawning a daemon | ||
using the "-datadir=/backup" argument which creates a new daemon instance with it's own | ||
RPC config. For more info on this see: | ||
- https://en.bitcoin.it/wiki/Data_directory | ||
- https://en.bitcoin.it/wiki/Running_bitcoind */ | ||
"daemons": [ | ||
{ //Main daemon instance | ||
"host": "127.0.0.1", | ||
"port": 19332, | ||
"user": "litecoinrpc", | ||
"password": "testnet" | ||
}, | ||
{ //Backup daemon instance | ||
"host": "127.0.0.1", | ||
"port": 19344, | ||
"user": "litecoinrpc", | ||
"password": "testnet" | ||
} | ||
], | ||
/* This allows the pool to connect to the daemon as a node peer to receive block updates. | ||
It may be the most efficient way to get block updates (faster than polling, less | ||
intensive than blocknotify script). It requires the additional field "peerMagic" in | ||
the coin config. */ | ||
"p2p": { | ||
"enabled": false, | ||
/* Host for daemon */ | ||
"host": "127.0.0.1", | ||
/* Port configured for daemon (this is the actual peer port not RPC port) */ | ||
"port": 19333, | ||
/* If your coin daemon is new enough (i.e. not a shitcoin) then it will support a p2p | ||
feature that prevents the daemon from spamming our peer node with unnecessary | ||
transaction data. Assume its supported but if you have problems try disabling it. */ | ||
"disableTransactions": true | ||
} | ||
}, function(ip, port , workerName, password, callback){ //stratum authorization function | ||
console.log("Authorize " + workerName + ":" + password + "@" + ip); | ||
callback({ | ||
error: null, | ||
authorized: true, | ||
disconnect: false | ||
}); | ||
}); | ||
``` | ||
Listen to pool events | ||
```javascript | ||
/* | ||
'data' object contains: | ||
job: 4, //stratum work job ID | ||
ip: '71.33.19.37', //ip address of client | ||
port: 3333, //port of the client | ||
worker: 'matt.worker1', //stratum worker name | ||
height: 443795, //block height | ||
blockReward: 5000000000, //the number of satoshis received as payment for solving this block | ||
difficulty: 64, //stratum worker difficulty | ||
shareDiff: 78, //actual difficulty of the share | ||
blockDiff: 3349, //block difficulty adjusted for share padding | ||
blockDiffActual: 3349 //actual difficulty for this block | ||
//AKA the block solution - set if block was found | ||
blockHash: '110c0447171ad819dd181216d5d80f41e9218e25d833a2789cb8ba289a52eee4', | ||
//Exists if "emitInvalidBlockHashes" is set to true | ||
blockHashInvalid: '110c0447171ad819dd181216d5d80f41e9218e25d833a2789cb8ba289a52eee4' | ||
//txHash is the coinbase transaction hash from the block | ||
txHash: '41bb22d6cc409f9c0bae2c39cecd2b3e3e1be213754f23d12c5d6d2003d59b1d, | ||
error: 'low share difficulty' //set if share is rejected for some reason | ||
*/ | ||
pool.on('share', function(isValidShare, isValidBlock, data){ | ||
if (isValidBlock) | ||
console.log('Block found'); | ||
else if (isValidShare) | ||
console.log('Valid share submitted'); | ||
else if (data.blockHash) | ||
console.log('We thought a block was found but it was rejected by the daemon'); | ||
else | ||
console.log('Invalid share submitted') | ||
console.log('share data: ' + JSON.stringify(data)); | ||
}); | ||
/* | ||
'severity': can be 'debug', 'warning', 'error' | ||
'logKey': can be 'system' or 'client' indicating if the error | ||
was caused by our system or a stratum client | ||
*/ | ||
pool.on('log', function(severity, logKey, logText){ | ||
console.log(severity + ': ' + '[' + logKey + '] ' + logText); | ||
}); | ||
``` | ||
Start pool | ||
```javascript | ||
pool.start(); | ||
``` | ||
Credits | ||
@@ -103,3 +361,3 @@ ------- | ||
* [viperaus](//github.com/viperaus/stratum-mining) - scrypt adaptions to python code | ||
* [ahmedbodi](//github.com/ahmedbodi/stratum-mining) - more algo adaptions to python code | ||
* [ahmedbodi](//github.com/ahmedbodi/stratum-mining) - more algo adaptions to python code and unomp dev | ||
* [steveshit](//github.com/steveshit) - ported X11 hashing algo from python to node module | ||
@@ -106,0 +364,0 @@ * [KillerByte](//github.com/KillerByte) - for beginning this creation |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Wildcard dependency
QualityPackage has a dependency with a floating version range. This can cause issues if the dependency publishes a new major version.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
173707
20
3361
386
5
5
+ Addedredis@*
+ Added@redis/bloom@1.2.0(transitive)
+ Added@redis/client@1.6.0(transitive)
+ Added@redis/graph@1.1.1(transitive)
+ Added@redis/json@1.0.7(transitive)
+ Added@redis/search@1.2.0(transitive)
+ Added@redis/time-series@1.1.0(transitive)
+ Addedcluster-key-slot@1.1.2(transitive)
+ Addedgeneric-pool@3.9.0(transitive)
+ Addedredis@4.7.0(transitive)
+ Addedyallist@4.0.0(transitive)