Browse Source

Disablecb (#19)

* add disablcb mode (#18)

* Make a bit easier

* change to my stratum fork

* try first go with reward recipents active, debug.

* revert wtf

* try again

* forgot print of sendmany

* wtf

* fix

* fixed pool payouts. Now works for disablecb option as normal. Template now enables payment processor by default. gencfg.sh script can take a coin as $1, run wihtout it will use assetchains.json.

* remoce debug prints

* fix

* update with new changes for disablecb

* change to use jl777 FSM branch KMD

* Whitespace

* Revert dependency and template change

* Fix readme and coins template
pull/1/head
Web Worker 5 years ago
committed by GitHub
parent
commit
a1af0da4d2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 28
      README.md
  2. 3
      coins.template
  3. 44
      gencfg.sh
  4. 146
      libs/paymentProcessor.js

28
README.md

@ -60,22 +60,38 @@ We need node and npm installed
cd ~
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
```
Now, let's build our stratum and run it. This will install the pool and configure it for all the assetchains on your system automatically. It must be run from the same user as the coin deamons were launched, as it pulls the rpcuser/pass from the conf file in the home directory.
```shell
git clone https://github.com/webworker01/knomp
cd knomp
npm install
cp config_example.json config.json (and configure it)
nano gencfg.sh
```
Edit line 3 in so that it has your own KMD based address, CTRL-X then Y to save and exit
We need to generate the coins files (coin daemon must be running!): `gencfg.sh <coin name>`
You can run just gencfg.sh with no coin name to use the assetchains.json in komodo/src directory for all coins. Make sure you edit the template with the correct values you want before running the config generator.
Finally we are ready to start the pool software
```shell
./gencfg.sh
npm install
cp config_example.json config.json (and configure it)
npm start
```
If all went well the program should start without error and you should be able to browse to your pool website on your server via port 8080.
Disable Coinbase Mode
-------------
This mode uses -pubkey to tell the daemon where the coinbase should be sent, and uses the daemons coinbase transaction rather then having the pool create the coinabse transaction. This enables special coinbase transactions, such as ac_founders and ac_script or new modes with CC vouts in the coinbase not yet created, it will work with all coins, except Full Z support described below.
To enable it, change the value in the `./coins/*.json` to `"disablecb" : true`
The pool fee is taken in the payment processor using this mode, and might not be 100% accurate down to the single satoshi, so the pool address may end up with some small amount of coins over time.
Payment Processing
-------------
Please note that the default configs generated are for solo mining. If you wish to create a public pool please modify the configs like in this [example config](https://github.com/z-classic/z-nomp/blob/master/pool_configs/komodo_example.json)
@ -122,9 +138,9 @@ In pool_config:
"walletInterval": 2,
"validateWorkerUsername": true,
"paymentProcessing": {
"minConf": 5,
"paymentInterval": 180,
"maxBlocksPerPayment": 20,
"minConf": 5,
"paymentInterval": 180,
"maxBlocksPerPayment": 20,
```
More Resources

3
coins.template

@ -3,5 +3,6 @@
"symbol": "COINNAMEVAR",
"algorithm": "equihash",
"txfee": 0.0001,
"peerMagic": "MAGICREVVAR"
"peerMagic": "MAGICREVVAR",
"disablecb": "false"
}

44
gencfg.sh

@ -1,6 +1,6 @@
#!/bin/bash
# Put the address to mine to here
walletaddress=RWEBo1Yp4uGkeXPi1ZGQARfLPkGmoW1MwY
walletaddress=
#Change to path of komodo-cli here
komodoexec=~/komodo/src/komodo-cli
@ -11,6 +11,7 @@ declare -a skip=("BEER" "PIZZA")
# Stratum port to start with
stratumport=3030
cli="komodo-cli"
coinsdir=./coins
poolconfigdir=./pool_configs
coinstpl=coins.template
@ -32,18 +33,39 @@ if [ -f $ufwdisablefile ]; then
rm $ufwdisablefile
fi
~/komodo/src/listassetchains | while read chain; do
conffile="~/.komodo/$chain/$chain.conf"
if [[ -z $1 ]]; then
specificchain=0
else
specificchain=$1
fi
listassetchains () {
if [[ $specificchain = "0" ]]; then
~/komodo/src/listassetchains
else
echo $specificchain
fi
}
listassetchains | while read chain; do
if [[ " ${skip[@]} " =~ " ${chain} " ]]; then
pointless=0
else
echo "[$chain] Generating config files"
getinfo=$(${cli} -ac_name=$chain getinfo 2>/dev/null)
outcome=$(echo $?)
if [[ $outcome != 0 ]]; then
echo "[$chain] Daemon is not running skipped."
continue
fi
if [[ " ${skip[@]} " =~ " ${chain} " ]] || [ ! -f $conffile ]; then
pointless=0
else
thisconf=$(<$conffile)
string=$(printf '%08x\n' $($komodoexec -ac_name=$chain getinfo | jq '.magic'))
magic=${string: -8}
magicrev=$(echo ${magic:6:2}${magic:4:2}${magic:2:2}${magic:0:2})
string=$(printf '%08x\n' $(echo $getinfo | jq '.magic'))
magic=${string: -8}
magicrev=$(echo ${magic:6:2}${magic:4:2}${magic:2:2}${magic:0:2})
p2pport=$($komodoexec -ac_name=$chain getinfo | jq '.p2pport')
p2pport=$(echo $getinfo | jq '.p2pport')
thisconf=$(<~/.komodo/$chain/$chain.conf)
rpcuser=$(echo $thisconf | grep -Po "rpcuser=(\S*)" | sed 's/rpcuser=//')
rpcpass=$(echo $thisconf | grep -Po "rpcpassword=(\S*)" | sed 's/rpcpassword=//')

146
libs/paymentProcessor.js

@ -120,6 +120,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
}
}, true);
}
function validateTAddress (callback) {
daemon.cmd('validateaddress', [poolOptions.tAddress], function(result) {
if (result.error){
@ -136,8 +137,9 @@ function SetupForPool(logger, poolOptions, setupFinished) {
callback()
}
}, true);
}
function validateZAddress (callback) {
}
function validateZAddress (callback) {
daemon.cmd('z_validateaddress', [poolOptions.zAddress], function(result) {
if (result.error){
logger.error(logSystem, logComponent, 'Error with payment processing daemon ' + JSON.stringify(result.error));
@ -154,6 +156,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
}
}, true);
}
function getBalance(callback){
daemon.cmd('getbalance', [], function(result){
if (result.error){
@ -201,11 +204,11 @@ function SetupForPool(logger, poolOptions, setupFinished) {
/**
* Specify with first param if using normal listunspent or listunspentZ
* @param {String} type (T | Z)
* @param {*} addr
* @param {*} notAddr
* @param {*} minConf
* @param {*} displayBool
* @param {*} callback
* @param {*} addr
* @param {*} notAddr
* @param {*} minConf
* @param {*} displayBool
* @param {*} callback
*/
function listUnspentType(type, zaddr, notAddr, minConf, displayBool, callback) {
if (type == 'Z') {
@ -296,7 +299,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
callback(true);
}
else {
var opid = (result.response || result[0].response);
var opid = (result.response || result[0].response);
opidCount++;
opids.push(opid);
logger.special(logSystem, logComponent, 'Shield balance ' + amount + ' ' + opid);
@ -342,7 +345,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
callback(true);
}
else {
var opid = (result.response || result[0].response);
var opid = (result.response || result[0].response);
opidCount++;
opids.push(opid);
logger.special(logSystem, logComponent, 'Unshield funds for payout ' + amount + ' ' + opid);
@ -408,7 +411,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
if (privateChain && op.status != "executing") {
//Check to see if the opid exists in the payments datastore and a payment needs the transaction updated or we need to mark the payment as failed
redisClient.multi([ ['zrange', coin + ':payments', 0, -1] ]).exec(function(error, allpayments) {
redisClient.multi([ ['zrange', coin + ':payments', 0, -1] ]).exec(function(error, allpayments) {
if (!error && allpayments) {
// console.log(allpayments[0]);
paymentstoparse = allpayments[0];
@ -451,9 +454,9 @@ function SetupForPool(logger, poolOptions, setupFinished) {
// log status to console
if (op.status == "failed") {
if (op.error) {
logger.error(logSystem, logComponent, "Shielded operation failed " + op.id + " " + op.error.code +", " + op.error.message);
logger.error(logSystem, logComponent, "Shielded operation failed " + op.id + " " + op.error.code +", " + op.error.message);
} else {
logger.error(logSystem, logComponent, "Shielded operation failed " + op.id);
logger.error(logSystem, logComponent, "Shielded operation failed " + op.id);
}
} else {
logger.special(logSystem, logComponent, 'Shielded operation success ' + op.id + ' txid: ' + op.result.txid);
@ -551,7 +554,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
}
return count > 1;
}
/* 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. */
@ -559,6 +562,8 @@ function SetupForPool(logger, poolOptions, setupFinished) {
var startPaymentProcess = Date.now();
var poolperc = 0;
var timeSpentRPC = 0;
var timeSpentRedis = 0;
@ -650,7 +655,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
// not unique duplicate block
logger.warning(logSystem, logComponent, 'Remove non-unique duplicate block ' + block.result.height + ' > ' + block.result.hash);
// move from blocksPending to blocksDuplicate...
invalidBlocks.push(['smove', coin + ':blocksPending', coin + ':blocksDuplicate', dups[i].serialized]);
invalidBlocks.push(['smove', coin + ':blocksPending', coin + ':blocksDuplicate', dups[i].serialized]);
} else {
// keep unique valid block
validBlocks[dups[i].blockHash] = dups[i].serialized;
@ -677,7 +682,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
// notify pool owner that we are unable to find the invalid duplicate blocks, manual intervention required...
logger.error(logSystem, logComponent, 'Unable to detect invalid duplicate blocks, duplicate block payments on hold.');
// continue payments normally
callback(null, workers, rounds);
callback(null, workers, rounds);
}
});
} else {
@ -688,7 +693,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
},
/*
/*
Step 2 - check if mined block coinbase tx are ready for payment
* adds block reward to rounds object
* adds block confirmations count to rounds object
@ -710,7 +715,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
callback(true);
return;
}
var addressAccount = "";
// check for transaction errors and generated coins
@ -759,8 +764,16 @@ function SetupForPool(logger, poolOptions, setupFinished) {
// get reward for newly generated blocks
if (round.category === 'generate' || round.category === 'immature') {
round.reward = coinsRound(parseFloat(generationTx.amount || generationTx.value));
var minerperc = 1;
if (poolOptions.coin.disablecb && poolOptions.rewardRecipients.length !== 0) {
for (var r in poolOptions.rewardRecipients) {
minerperc = minerperc - (poolOptions.rewardRecipients[r]/100);
}
}
poolperc = roundTo(1 - minerperc,4);
round.reward = coinsRound(parseFloat(generationTx.amount*minerperc || generationTx.value*minerperc));
}
//console.log(round.reward);
});
var canDeleteShares = function(r){
@ -810,7 +823,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
},
/*
/*
Step 3 - lookup shares and calculate rewards
* pull pplnt times from redis
* pull shares from redis
@ -895,10 +908,10 @@ function SetupForPool(logger, poolOptions, setupFinished) {
case 'orphan':
case 'kicked':
case 'immature':
return true;
case 'generate':
r.category = 'immature';
return true;
return true;
case 'generate':
r.category = 'immature';
return true;
default:
return false;
};
@ -907,7 +920,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
// handle rounds
rounds.forEach(function(round, i){
var workerShares = allWorkerShares[i];
var workerShares = allWorkerShares[i];
if (!workerShares){
err = true;
logger.error(logSystem, logComponent, 'No worker shares for round: ' + round.height + ' blockHash: ' + round.blockHash);
@ -917,43 +930,43 @@ function SetupForPool(logger, poolOptions, setupFinished) {
var workerTimes = {};
var maxTime = 0;
if (pplntEnabled === true) {
for (var workerAddressWithPoolId in workerTimesWithPoolIds){
for (var workerAddressWithPoolId in workerTimesWithPoolIds){
var workerWithoutPoolId = workerAddressWithPoolId.split('.')[0];
var workerTimeFloat = parseFloat(workerTimesWithPoolIds[workerAddressWithPoolId]);
if (maxTime < workerTimeFloat) {
maxTime = workerTimeFloat;
}
if (!(workerWithoutPoolId in workerTimes)) {
workerTimes[workerWithoutPoolId] = workerTimeFloat;
} else {
if (!(workerWithoutPoolId in workerTimes)) {
workerTimes[workerWithoutPoolId] = workerTimeFloat;
} else {
// add time from other instances with penalty
if (workerTimes[workerWithoutPoolId] < workerTimeFloat) {
workerTimes[workerWithoutPoolId] = workerTimes[workerWithoutPoolId] * 0.5 + workerTimeFloat;
} else {
workerTimes[workerWithoutPoolId] = workerTimes[workerWithoutPoolId] + workerTimeFloat * 0.5;
if (workerTimes[workerWithoutPoolId] < workerTimeFloat) {
workerTimes[workerWithoutPoolId] = workerTimes[workerWithoutPoolId] * 0.5 + workerTimeFloat;
} else {
workerTimes[workerWithoutPoolId] = workerTimes[workerWithoutPoolId] + workerTimeFloat * 0.5;
}
if (workerTimes[workerWithoutPoolId] > maxTime) {
workerTimes[workerWithoutPoolId] = maxTime;
}
}
}
}
}
}
switch (round.category){
case 'kicked':
case 'orphan':
round.workerShares = workerShares;
break;
/* calculate immature balances */
case 'immature':
var feeSatoshi = coinsToSatoshies(fee);
var immature = coinsToSatoshies(round.reward);
var totalShares = parseFloat(0);
var sharesLost = parseFloat(0);
// adjust block immature .. tx fees
immature = Math.round(immature - feeSatoshi);
// total up shares for round
for (var workerAddress in workerShares){
var worker = workers[workerAddress] = (workers[workerAddress] || {});
@ -975,13 +988,13 @@ function SetupForPool(logger, poolOptions, setupFinished) {
worker.roundShares = shares;
totalShares += shares;
}
//console.log('--IMMATURE DEBUG--------------');
//console.log('performPayment: '+performPayment);
//console.log('blockHeight: '+round.height);
//console.log('blockReward: '+Math.round(immature));
//console.log('blockConfirmations: '+round.confirmations);
// calculate rewards for round
var totalAmount = 0;
for (var workerAddress in workerShares){
@ -992,7 +1005,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
worker.immature = (worker.immature || 0) + workerImmatureTotal;
totalAmount += workerImmatureTotal;
}
//console.log('----------------------------');
break;
@ -1002,10 +1015,10 @@ function SetupForPool(logger, poolOptions, setupFinished) {
var reward = coinsToSatoshies(round.reward);
var totalShares = parseFloat(0);
var sharesLost = parseFloat(0);
// adjust block reward .. tx fees
reward = Math.round(reward - feeSatoshi);
// total up shares for round
for (var workerAddress in workerShares){
var worker = workers[workerAddress] = (workers[workerAddress] || {});
@ -1062,7 +1075,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
break;
}
});
// if there was no errors
if (err === null) {
callback(null, workers, rounds, addressAccount);
@ -1070,18 +1083,18 @@ function SetupForPool(logger, poolOptions, setupFinished) {
// some error, stop waterfall
callback(true);
}
}); // end funds check
});// end share lookup
}); // end time lookup
},
/*
Step 4 - Generate RPC commands to send payments
When deciding the sent balance, it the difference should be -1*amount they had in db,
If not sending the balance, the differnce should be +(the amount they earned this round)
Step 4 - Generate RPC commands to send payments
When deciding the sent balance, it the difference should be -1*amount they had in db,
If not sending the balance, the differnce should be +(the amount they earned this round)
*/
function(workers, rounds, addressAccount, callback) {
@ -1160,13 +1173,26 @@ function SetupForPool(logger, poolOptions, setupFinished) {
callback(null, workers, rounds, []);
return;
}
// do final rounding of payments per address
// this forces amounts to be valid (0.12345678)
var totalcoinstosend = 0;
for (var a in addressAmounts) {
addressAmounts[a] = coinsRound(addressAmounts[a]);
totalcoinstosend = totalcoinstosend + addressAmounts[a];
}
if (poolOptions.coin.disablecb && poolOptions.rewardRecipients.length !== 0) {
var totalbr = coinsRound(totalcoinstosend*(poolperc+1));
//console.log(totalbr);
for (var r in poolOptions.rewardRecipients) {
var feetopay = coinsRound(totalbr*(poolOptions.rewardRecipients[r]/100));
addressAmounts[r] = feetopay;
}
}
//console.log(addressAmounts);
// POINT OF NO RETURN! GOOD LUCK!
// WE ARE SENDING PAYMENT CMD TO DAEMON
@ -1318,11 +1344,11 @@ function SetupForPool(logger, poolOptions, setupFinished) {
var paymentBlocks = rounds.filter(function(r){ return r.category == 'generate'; }).map(function(r){
return parseInt(r.height);
});
var paymentsUpdate = [];
var paymentsData = {time:Date.now(), txid:txid, shares:totalShares, paid:satoshisToCoins(totalSent), miners:Object.keys(addressAmounts).length, blocks: paymentBlocks, amounts: addressAmounts, balances: balanceAmounts, work:shareAmounts};
paymentsUpdate.push(['zadd', logComponent + ':payments', Date.now(), JSON.stringify(paymentsData)]);
callback(null, workers, rounds, paymentsUpdate);
} else {
@ -1350,7 +1376,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
function(workers, rounds, paymentsUpdate, callback){
var totalPaid = parseFloat(0);
var immatureUpdateCommands = [];
var balanceUpdateCommands = [];
var workerPayoutsCommand = [];
@ -1383,10 +1409,10 @@ function SetupForPool(logger, poolOptions, setupFinished) {
var movePendingCommands = [];
var roundsToDelete = [];
var orphanMergeCommands = [];
var confirmsUpdate = [];
var confirmsToDelete = [];
var moveSharesToCurrent = function(r){
var workerShares = r.workerShares;
if (workerShares != null) {
@ -1431,7 +1457,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
if (immatureUpdateCommands.length > 0)
finalRedisCommands = finalRedisCommands.concat(immatureUpdateCommands);
if (balanceUpdateCommands.length > 0)
finalRedisCommands = finalRedisCommands.concat(balanceUpdateCommands);
@ -1443,13 +1469,13 @@ function SetupForPool(logger, poolOptions, setupFinished) {
if (confirmsUpdate.length > 0)
finalRedisCommands = finalRedisCommands.concat(confirmsUpdate);
if (confirmsToDelete.length > 0)
finalRedisCommands = finalRedisCommands.concat(confirmsToDelete);
if (paymentsUpdate.length > 0)
finalRedisCommands = finalRedisCommands.concat(paymentsUpdate);
if (totalPaid !== 0)
finalRedisCommands.push(['hincrbyfloat', coin + ':stats', 'totalPaid', totalPaid]);
@ -1461,7 +1487,7 @@ function SetupForPool(logger, poolOptions, setupFinished) {
startRedisTimer();
redisClient.multi(finalRedisCommands).exec(function(error, results){
endRedisTimer();
if (error) {
if (error) {
clearInterval(paymentInterval);
logger.error(logSystem, logComponent,

Loading…
Cancel
Save