commit
1adbd960b2
@ -68,11 +68,11 @@ Features
|
||||
|
||||
The following feature have been implemented so far:
|
||||
|
||||
* Mobile WebUI **NEW**
|
||||
* Mobile WebUI
|
||||
* Reward Systems
|
||||
* Propotional
|
||||
* PPS
|
||||
* (Planned) PPLNS
|
||||
* PPLNS **NEW**
|
||||
* Use of memcache for statistics instead of a cronjob
|
||||
* Web User accounts
|
||||
* Re-Captcha protected registration form
|
||||
|
||||
29
cronjobs/archive_cleanup.php
Executable file
29
cronjobs/archive_cleanup.php
Executable file
@ -0,0 +1,29 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
|
||||
/*
|
||||
|
||||
Copyright:: 2013, Sebastian Grewe
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Include all settings and classes
|
||||
require_once('shared.inc.php');
|
||||
|
||||
// If we don't keep archives, delete some now to release disk space
|
||||
if (!$share->purgeArchive()) {
|
||||
$log->logError("Failed to delete archived shares, not critical but should be checked!");
|
||||
}
|
||||
?>
|
||||
169
cronjobs/pplns_payout.php
Executable file
169
cronjobs/pplns_payout.php
Executable file
@ -0,0 +1,169 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
|
||||
/*
|
||||
|
||||
Copyright:: 2013, Sebastian Grewe
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Include all settings and classes
|
||||
require_once('shared.inc.php');
|
||||
|
||||
// Check if we are set as the payout system
|
||||
if ($config['payout_system'] != 'pplns') {
|
||||
$log->logInfo("Please activate this cron in configuration via payout_system = pplns");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Fetch all unaccounted blocks
|
||||
$aAllBlocks = $block->getAllUnaccounted('ASC');
|
||||
if (empty($aAllBlocks)) {
|
||||
$log->logDebug("No new unaccounted blocks found");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
foreach ($aAllBlocks as $iIndex => $aBlock) {
|
||||
// We support some dynamic share targets but fall back to our fixed value
|
||||
// Re-calculate after each run due to re-targets in this loop
|
||||
if ($config['pplns']['shares']['type'] == 'blockavg' && $block->getBlockCount() > 0) {
|
||||
$pplns_target = round($block->getAvgBlockShares($config['pplns']['blockavg']['blockcount']));
|
||||
} else {
|
||||
$pplns_target = $config['pplns']['shares']['default'] ;
|
||||
}
|
||||
|
||||
if (!$aBlock['accounted']) {
|
||||
$iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0;
|
||||
$iCurrentUpstreamId = $aBlock['share_id'];
|
||||
if (!is_numeric($iCurrentUpstreamId)) {
|
||||
$log->logFatal("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue");
|
||||
exit(1);
|
||||
}
|
||||
$iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']);
|
||||
$iNewRoundShares = 0;
|
||||
$config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward'];
|
||||
$aRoundAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']);
|
||||
|
||||
if ($iRoundShares >= $pplns_target) {
|
||||
$log->logDebug("Matching or exceeding PPLNS target of $pplns_target with $iRoundShares");
|
||||
$aAccountShares = $share->getSharesForAccounts($aBlock['share_id'] - $pplns_target + 1, $aBlock['share_id']);
|
||||
if (empty($aAccountShares)) {
|
||||
$log->logFatal("No shares found for this block, aborted! Block Height : " . $aBlock['height']);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
$log->logDebug("Not able to match PPLNS target of $pplns_target with $iRoundShares");
|
||||
// We need to fill up with archived shares
|
||||
// Grab the full current round shares since we didn't match target
|
||||
$aAccountShares = $aRoundAccountShares;
|
||||
if (empty($aAccountShares)) {
|
||||
$log->logFatal("No shares found for this block, aborted! Block height: " . $aBlock['height']);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Grab only the most recent shares from Archive that fill the missing shares
|
||||
$log->logInfo('Fetching ' . ($pplns_target - $iRoundShares) . ' additional shares from archive');
|
||||
if (!$aArchiveShares = $share->getArchiveShares($pplns_target - $iRoundShares)) {
|
||||
$log->logError('Failed to fetch shares from archive, setting target to round total');
|
||||
$pplns_target = $iRoundShares;
|
||||
} else {
|
||||
// Add archived shares to users current shares, if we have any in archive
|
||||
if (is_array($aArchiveShares)) {
|
||||
$log->logDebug('Found shares in archive to match PPLNS target, calculating per-user shares');
|
||||
foreach($aAccountShares as $key => $aData) {
|
||||
if (array_key_exists($aData['username'], $aArchiveShares)) {
|
||||
$log->logDebug('Found user ' . $aData['username'] . ' in archived shares');
|
||||
$log->logDebug(' valid : ' . $aAccountShares[$key]['valid'] . ' + ' . $aArchiveShares[$aData['username']]['valid'] . ' = ' . ($aAccountShares[$key]['valid'] + $aArchiveShares[$aData['username']]['valid']) );
|
||||
$log->logDebug(' invalid : ' . $aAccountShares[$key]['invalid'] . ' + ' . $aArchiveShares[$aData['username']]['invalid'] . ' = ' . ($aAccountShares[$key]['invalid'] + $aArchiveShares[$aData['username']]['invalid']) );
|
||||
$aAccountShares[$key]['valid'] += $aArchiveShares[$aData['username']]['valid'];
|
||||
$aAccountShares[$key]['invalid'] += $aArchiveShares[$aData['username']]['invalid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
// We tried to fill up to PPLNS target, now we need to check the actual shares to properly payout users
|
||||
foreach($aAccountShares as $key => $aData) {
|
||||
$iNewRoundShares += $aData['valid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We filled from archive but still are not able to match PPLNS target, re-adjust
|
||||
if ($iRoundShares < $iNewRoundShares) {
|
||||
$log->logInfo('Adjusting round target to ' . $iNewRoundShares);
|
||||
$iRoundShares = $iNewRoundShares;
|
||||
}
|
||||
|
||||
// Table header for account shares
|
||||
$log->logInfo("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee");
|
||||
|
||||
// Loop through all accounts that have found shares for this round
|
||||
foreach ($aAccountShares as $key => $aData) {
|
||||
// Payout based on PPLNS target shares, proportional payout for all users
|
||||
$aData['percentage'] = number_format(round(( 100 / $pplns_target) * $aData['valid'], 8), 8);
|
||||
$aData['payout'] = number_format(round(( $aData['percentage'] / 100 ) * $dReward, 8), 8);
|
||||
// Defaults
|
||||
$aData['fee' ] = 0;
|
||||
$aData['donation'] = 0;
|
||||
|
||||
if ($config['fees'] > 0)
|
||||
$aData['fee'] = number_format(round($config['fees'] / 100 * $aData['payout'], 8), 8);
|
||||
// Calculate donation amount, fees not included
|
||||
$aData['donation'] = number_format(round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 8), 8);
|
||||
|
||||
// Verbose output of this users calculations
|
||||
$log->logInfo($aData['id'] . "\t" .
|
||||
$aData['username'] . "\t" .
|
||||
$aData['valid'] . "\t" .
|
||||
$aData['invalid'] . "\t" .
|
||||
$aData['percentage'] . "\t" .
|
||||
$aData['payout'] . "\t" .
|
||||
$aData['donation'] . "\t" .
|
||||
$aData['fee']);
|
||||
|
||||
// Add full round share statistics, not just PPLNS
|
||||
foreach ($aRoundAccountShares as $key => $aRoundData) {
|
||||
if ($aRoundData['username'] == $aData['username'])
|
||||
if (!$statistics->updateShareStatistics($aRoundData, $aBlock['id']))
|
||||
$log->logError('Failed to update share statistics for ' . $aData['username']);
|
||||
}
|
||||
// Add new credit transaction
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit', $aBlock['id']))
|
||||
$log->logFatal('Failed to insert new Credit transaction to database for ' . $aData['username']);
|
||||
// Add new fee debit for this block
|
||||
if ($aData['fee'] > 0 && $config['fees'] > 0)
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee', $aBlock['id']))
|
||||
$log->logFatal('Failed to insert new Fee transaction to database for ' . $aData['username']);
|
||||
// Add new donation debit
|
||||
if ($aData['donation'] > 0)
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation', $aBlock['id']))
|
||||
$log->logFatal('Failed to insert new Donation transaction to database for ' . $aData['username']);
|
||||
}
|
||||
|
||||
// Move counted shares to archive before this blockhash upstream share
|
||||
if (!$share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId))
|
||||
$log->logError('Failed to copy shares to archive table');
|
||||
// Delete all accounted shares
|
||||
if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) {
|
||||
$log->logFatal("Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting!");
|
||||
exit(1);
|
||||
}
|
||||
// Mark this block as accounted for
|
||||
if (!$block->setAccounted($aBlock['id'])) {
|
||||
$log->logFatal("Failed to mark block as accounted! Aborting!");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -120,7 +120,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
|
||||
$log->logError("Failed to update stats for this block on : " . $aData['username']);
|
||||
}
|
||||
// Move shares to archive
|
||||
if ($config['archive_shares'] && $aBlock['share_id'] < $iLastShareId) {
|
||||
if ($aBlock['share_id'] < $iLastShareId) {
|
||||
if (!$share->moveArchive($aBlock['share_id'], $aBlock['id'], @$iLastBlockShare))
|
||||
$log->logError("Archving failed");
|
||||
}
|
||||
|
||||
@ -97,7 +97,8 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
|
||||
}
|
||||
|
||||
// Move counted shares to archive before this blockhash upstream share
|
||||
if ($config['archive_shares']) $share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId);
|
||||
if (!$share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId))
|
||||
$log->logError('Failed to copy shares to archive');
|
||||
// Delete all accounted shares
|
||||
if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) {
|
||||
$log->logFatal('Failed to delete accounted shares from ' . $iPreviousShareId . ' to ' . $iCurrentUpstreamId . ', aborted');
|
||||
|
||||
@ -16,7 +16,7 @@ PIDFILE='/tmp/mmcfe-ng-cron.pid'
|
||||
CRONHOME='.'
|
||||
|
||||
# List of cruns to execute
|
||||
CRONS="findblock.php proportional_payout.php pps_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php statistics.php"
|
||||
CRONS="findblock.php proportional_payout.php pplns_payout.php pps_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php statistics.php archive_cleanup.php"
|
||||
|
||||
# Additional arguments to pass to cronjobs
|
||||
CRONARGS="-v"
|
||||
|
||||
@ -43,6 +43,18 @@ class Block {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific block, by block height
|
||||
* @param height int Block Height
|
||||
* @return data array Block information from DB
|
||||
**/
|
||||
public function getBlock($height) {
|
||||
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE height = ? LIMIT 1");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $height) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_assoc();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get our last, highest share ID inserted for a block
|
||||
* @param none
|
||||
@ -79,6 +91,30 @@ class Block {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total amount of blocks in our table
|
||||
* @param noone
|
||||
* @return data int Count of rows
|
||||
**/
|
||||
public function getBlockCount() {
|
||||
$stmt = $this->mysqli->prepare("SELECT COUNT(id) AS blocks FROM $this->table");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return (int)$result->fetch_object()->blocks;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch our average share count for the past N blocks
|
||||
* @param limit int Maximum blocks to check
|
||||
* @return data float Float value of average shares
|
||||
**/
|
||||
public function getAvgBlockShares($limit=10) {
|
||||
$stmt = $this->mysqli->prepare("SELECT AVG(shares) AS average FROM $this->table LIMIT ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $limit) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return (float)$result->fetch_object()->average;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all unconfirmed blocks from table
|
||||
* @param confirmations int Required confirmations to consider block confirmed
|
||||
|
||||
@ -13,9 +13,12 @@ class Share {
|
||||
// This defines each share
|
||||
public $rem_host, $username, $our_result, $upstream_result, $reason, $solution, $time;
|
||||
|
||||
public function __construct($debug, $mysqli, $salt) {
|
||||
public function __construct($debug, $mysqli, $user, $block, $config) {
|
||||
$this->debug = $debug;
|
||||
$this->mysqli = $mysqli;
|
||||
$this->user = $user;
|
||||
$this->config = $config;
|
||||
$this->block = $block;
|
||||
$this->debug->append("Instantiated Share class", 2);
|
||||
}
|
||||
|
||||
@ -70,7 +73,7 @@ class Share {
|
||||
count(id) as total
|
||||
FROM $this->table
|
||||
WHERE our_result = 'Y'
|
||||
AND id BETWEEN ? AND ?
|
||||
AND id > ? AND id <= ?
|
||||
");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param('ii', $previous_upstream, $current_upstream);
|
||||
@ -86,47 +89,94 @@ class Share {
|
||||
* Fetch all shares grouped by accounts to count share per account
|
||||
* @param previous_upstream int Previous found share accepted by upstream to limit results
|
||||
* @param current_upstream int Current upstream accepted share
|
||||
* @param limit int Limit to this amount of shares for PPLNS
|
||||
* @return data array username, valid and invalid shares from account
|
||||
**/
|
||||
public function getSharesForAccounts($previous_upstream=0, $current_upstream) {
|
||||
$stmt = $this->mysqli->prepare("SELECT
|
||||
a.id,
|
||||
validT.account AS username,
|
||||
sum(validT.valid) as valid,
|
||||
IFNULL(sum(invalidT.invalid),0) as invalid
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT
|
||||
SUBSTRING_INDEX( `username` , '.', 1 ) as account,
|
||||
COUNT(id) AS valid
|
||||
FROM $this->table
|
||||
WHERE id BETWEEN ? AND ?
|
||||
AND our_result = 'Y'
|
||||
GROUP BY account
|
||||
) validT
|
||||
LEFT JOIN
|
||||
(
|
||||
SELECT DISTINCT
|
||||
SUBSTRING_INDEX( `username` , '.', 1 ) as account,
|
||||
COUNT(id) AS invalid
|
||||
FROM $this->table
|
||||
WHERE id BETWEEN ? AND ?
|
||||
AND our_result = 'N'
|
||||
GROUP BY account
|
||||
) invalidT
|
||||
ON validT.account = invalidT.account
|
||||
INNER JOIN accounts a ON a.username = validT.account
|
||||
GROUP BY a.username DESC");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param('iiii', $previous_upstream, $current_upstream, $previous_upstream, $current_upstream);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$stmt->close();
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
a.id,
|
||||
SUBSTRING_INDEX( s.username , '.', 1 ) as username,
|
||||
IFNULL(SUM(IF(our_result='Y', 1, 0)), 0) AS valid,
|
||||
IFNULL(SUM(IF(our_result='N', 1, 0)), 0) AS invalid
|
||||
FROM $this->table AS s
|
||||
LEFT JOIN " . $this->user->getTableName() . " AS a
|
||||
ON a.username = SUBSTRING_INDEX( s.username , '.', 1 )
|
||||
WHERE s.id > ? AND s.id <= ?
|
||||
GROUP BY username DESC
|
||||
");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the highest available share ID from archive
|
||||
**/
|
||||
function getMaxArchiveShareId() {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT MAX(share_id) AS share_id FROM $this->tableArchive
|
||||
");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_object()->share_id;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* We need a certain amount of valid archived shares
|
||||
* param left int Left/lowest share ID
|
||||
* param right int Right/highest share ID
|
||||
* return array data Returns an array with usernames as keys for easy access
|
||||
**/
|
||||
function getArchiveShares($iCount) {
|
||||
$iMinId = $this->getMaxArchiveShareId() - $iCount;
|
||||
$iMaxId = $this->getMaxArchiveShareId();
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
a.id,
|
||||
SUBSTRING_INDEX( s.username , '.', 1 ) as account,
|
||||
IFNULL(SUM(IF(our_result='Y', 1, 0)), 0) AS valid,
|
||||
IFNULL(SUM(IF(our_result='N', 1, 0)), 0) AS invalid
|
||||
FROM $this->tableArchive AS s
|
||||
LEFT JOIN " . $this->user->getTableName() . " AS a
|
||||
ON a.username = SUBSTRING_INDEX( s.username , '.', 1 )
|
||||
WHERE s.share_id > ? AND s.share_id <= ?
|
||||
GROUP BY account DESC");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $iMinId, $iMaxId) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
$aData = NULL;
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$aData[$row['account']] = $row;
|
||||
}
|
||||
if (is_array($aData)) return $aData;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* We keep shares only up to a certain point
|
||||
* This can be configured by the user.
|
||||
* @return return bool true or false
|
||||
**/
|
||||
public function purgeArchive() {
|
||||
if ($this->config['payout_system'] == 'pplns') {
|
||||
// Fetch our last block so we can go back configured rounds
|
||||
$aLastBlock = $this->block->getLast();
|
||||
// Fetch the block we need to find the share_id
|
||||
$aBlock = $this->block->getBlock($aLastBlock['height'] - $this->config['archive']['maxrounds']);
|
||||
// Now that we know our block, remove those shares
|
||||
$stmt = $this->mysqli->prepare("DELETE FROM $this->tableArchive WHERE block_id < ? AND time < DATE_SUB(now(), INTERVAL ? MINUTE)");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $aBlock['id'], $config['archive']['maxage']) && $stmt->execute())
|
||||
return true;
|
||||
} else {
|
||||
// We are not running pplns, so we just need to keep shares of the past <interval> minutes
|
||||
$stmt = $this->mysqli->prepare("DELETE FROM $this->tableArchive WHERE time < DATE_SUB(now(), INTERVAL ? MINUTE)");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $config['archive']['maxage']) && $stmt->execute())
|
||||
return true;
|
||||
}
|
||||
// Catchall
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move accounted shares to archive table, this step is optional
|
||||
* @param previous_upstream int Previous found share accepted by upstream to limit results
|
||||
@ -135,10 +185,11 @@ class Share {
|
||||
* @return bool
|
||||
**/
|
||||
public function moveArchive($current_upstream, $block_id, $previous_upstream=0) {
|
||||
$archive_stmt = $this->mysqli->prepare("INSERT INTO $this->tableArchive (share_id, username, our_result, upstream_result, block_id, time)
|
||||
SELECT id, username, our_result, upstream_result, ?, time
|
||||
FROM $this->table
|
||||
WHERE id BETWEEN ? AND ?");
|
||||
$archive_stmt = $this->mysqli->prepare("
|
||||
INSERT INTO $this->tableArchive (share_id, username, our_result, upstream_result, block_id, time)
|
||||
SELECT id, username, our_result, upstream_result, ?, time
|
||||
FROM $this->table
|
||||
WHERE id > ? AND id <= ?");
|
||||
if ($this->checkStmt($archive_stmt) && $archive_stmt->bind_param('iii', $block_id, $previous_upstream, $current_upstream) && $archive_stmt->execute()) {
|
||||
$archive_stmt->close();
|
||||
return true;
|
||||
@ -148,7 +199,7 @@ class Share {
|
||||
}
|
||||
|
||||
public function deleteAccountedShares($current_upstream, $previous_upstream=0) {
|
||||
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id BETWEEN ? AND ?");
|
||||
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id > ? AND id <= ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute())
|
||||
return true;
|
||||
// Catchall
|
||||
@ -267,4 +318,4 @@ class Share {
|
||||
}
|
||||
}
|
||||
|
||||
$share = new Share($debug, $mysqli, SALT);
|
||||
$share = new Share($debug, $mysqli, $user, $block, $config);
|
||||
|
||||
@ -169,8 +169,35 @@ $config['block_bonus'] = 0;
|
||||
**/
|
||||
$config['payout_system'] = 'prop';
|
||||
|
||||
// For debugging purposes you can archive shares in the archive_shares table, default: true
|
||||
$config['archive_shares'] = true;
|
||||
/**
|
||||
* Archiving configuration for debugging
|
||||
*
|
||||
* Explanation:
|
||||
* By default, we don't need to archive for a long time. PPLNS and Hashrate
|
||||
* calculations rely on this archive, but all shares past a certain point can
|
||||
* safely be deleted.
|
||||
*
|
||||
* To ensure we have enough shares on stack for PPLNS, this
|
||||
* is set to the past 10 rounds. Even with lucky ones in between those should
|
||||
* fit the PPLNS target. On top of that, even if we have more than 10 rounds,
|
||||
* we still keep the last maxage shares to ensure we can calculate hashrates.
|
||||
* Both conditions need to be met in order for shares to be purged from archive.
|
||||
*
|
||||
* Proportional mode will only keep the past 24 hours. These are required for
|
||||
* hashrate calculations to work past a round, hence 24 hours was selected as
|
||||
* the default. You may want to increase the time for debugging, then add any
|
||||
* integer reflecting minutes of shares to keep.
|
||||
*
|
||||
* Availabe Options:
|
||||
* maxrounds : PPLNS, keep shares for maxrounds
|
||||
* maxage : PROP and PPLNS, delete shares older than maxage minutes
|
||||
*
|
||||
* Default:
|
||||
* maxrounds = 10
|
||||
* maxage = 60 * 60 * 24 (24h)
|
||||
**/
|
||||
$config['archive']['maxrounds'] = 10;
|
||||
$config['archive']['maxage'] = 60 * 60 * 24;
|
||||
|
||||
// URL prefix for block searches, used for block links, default: `http://explorer.litecoin.net/search?q=`
|
||||
// If empty, the block link to the block information page will be removed
|
||||
@ -183,6 +210,30 @@ $config['chaininfo'] = 'http://allchains.info';
|
||||
// Pool fees applied to users in percent, default: 0 (disabled)
|
||||
$config['fees'] = 0;
|
||||
|
||||
/**
|
||||
* PPLNS requires some settings to run properly. First we need to define
|
||||
* a default shares count that is applied if we don't have a proper type set.
|
||||
* Different dynamic types can be applied, or you can run a fixed scheme.
|
||||
*
|
||||
* Explanation
|
||||
* default : Default target shares for PPLNS
|
||||
* type : Payout type used in PPLNS
|
||||
* blockcount : Amount of blocks to check for avg shares
|
||||
*
|
||||
* Available Options:
|
||||
* default : amount of shares, integeger
|
||||
* type : blockavg or fixed
|
||||
* blockcount : amount of blocks, any integer
|
||||
*
|
||||
* Defaults:
|
||||
* default = 4000000
|
||||
* type = `blockavg`
|
||||
* blockcount = 10
|
||||
**/
|
||||
$config['pplns']['shares']['default'] = 4000000;
|
||||
$config['pplns']['shares']['type'] = 'blockavg';
|
||||
$config['pplns']['blockavg']['blockcount'] = 10;
|
||||
|
||||
// Pool target difficulty as set in pushpoold configuration file
|
||||
// Please also read this for stratum: https://github.com/TheSerapher/php-mmcfe-ng/wiki/FAQ
|
||||
$config['difficulty'] = 20;
|
||||
|
||||
@ -83,9 +83,7 @@ if (@$_SESSION['USERDATA']['id']) {
|
||||
$aGlobal['userdata']['sharerate'] = $statistics->getUserSharerate($_SESSION['USERDATA']['id']);
|
||||
|
||||
switch ($config['payout_system']) {
|
||||
case 'pps':
|
||||
break;
|
||||
default:
|
||||
case 'prop' || 'pplns':
|
||||
// Some estimations
|
||||
if (@$aRoundShares['valid'] > 0) {
|
||||
$aGlobal['userdata']['est_block'] = round(( (int)$aGlobal['userdata']['shares']['valid'] / (int)$aRoundShares['valid'] ) * (float)$config['reward'], 8);
|
||||
@ -98,6 +96,14 @@ if (@$_SESSION['USERDATA']['id']) {
|
||||
$aGlobal['userdata']['est_donation'] = 0;
|
||||
$aGlobal['userdata']['est_payout'] = 0;
|
||||
}
|
||||
case 'pplns':
|
||||
if ($iAvgBlockShares = round($block->getAvgBlockShares($config['pplns']['blockavg']['blockcount']))) {
|
||||
$aGlobal['pplns']['target'] = $iAvgBlockShares;
|
||||
} else {
|
||||
$aGlobal['pplns']['target'] = $config['pplns']['shares']['default'];
|
||||
}
|
||||
break;
|
||||
case 'pps':
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
74
public/templates/mmcFE/global/sidebar_pplns.tpl
Normal file
74
public/templates/mmcFE/global/sidebar_pplns.tpl
Normal file
@ -0,0 +1,74 @@
|
||||
<div class="block" style="clear:none; margin-top:15px; margin-left:13px;">
|
||||
<div class="block_head">
|
||||
<div class="bheadl"></div>
|
||||
<div class="bheadr"></div>
|
||||
<h1>Dashboard</h1>
|
||||
</div>
|
||||
<div class="block_content" style="padding-top:10px;">
|
||||
<table class="sidebar" style="width: 196px">
|
||||
<tr>
|
||||
<td><b>PPLNS Target</b></td>
|
||||
<td class="right">{$GLOBAL.pplns.target|number_format}</td>
|
||||
</tr>
|
||||
<tr><td colspan="2"> </td></tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>Your Stats</u></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Hashrate</b></td>
|
||||
<td class="right">{$GLOBAL.userdata.hashrate|number_format} KH/s</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>Unpaid Shares</u></b> <span id='tt'><img src='{$PATH}/images/questionmark.png' height='15px' width='15px' title='Submitted shares between the last 120 confirms block until now.'></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Your Valid<b></td>
|
||||
<td class="right"><i>{$GLOBAL.userdata.shares.valid|number_format}</i><font size='1px'></font></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Pool Valid</td>
|
||||
<td class="right"><i>{$GLOBAL.roundshares.valid|number_format}</i> <font size='1px'></font></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>Round Shares</u></b> <span id='tt'><img src='{$PATH}/images/questionmark.png' height='15px' width='15px' title='Submitted shares since last found block (ie. round shares)'></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Pool Valid</b></td>
|
||||
<td class="right"><i>{$GLOBAL.roundshares.valid|number_format}</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Pool Invalid</b></td>
|
||||
<td class="right"><i>{$GLOBAL.roundshares.invalid|number_format}</i>{if $GLOBAL.roundshares.valid > 0}<font size='1px'> ({(100 / $GLOBAL.roundshares.valid * $GLOBAL.roundshares.invalid)|number_format:"2"}%)</font>{/if}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Your Invalid</b></td>
|
||||
<td class="right"><i>{$GLOBAL.userdata.shares.invalid|number_format}</i>{if $GLOBAL.roundshares.valid > 0}<font size='1px'> ({(100 / $GLOBAL.roundshares.valid * $GLOBAL.userdata.shares.invalid)|number_format:"2"}%)</font>{/if}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>{$GLOBAL.config.currency} Round Estimate</u></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Block</b></td>
|
||||
<td class="right">{$GLOBAL.userdata.est_block|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Fees</b></td>
|
||||
<td class="right">{$GLOBAL.userdata.est_fee|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Donation</b></td>
|
||||
<td class="right">{$GLOBAL.userdata.est_donation|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Payout</b></td>
|
||||
<td class="right">{$GLOBAL.userdata.est_payout|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr><td colspan="2"> </td></tr>
|
||||
<tr><td colspan="2"><b><u>{$GLOBAL.config.currency} Account Balance</u></b></td></tr>
|
||||
<tr><td>Confirmed</td><td class="right"><b>{$GLOBAL.userdata.balance.confirmed|default:"0"}</td></tr>
|
||||
<tr><td>Unconfirmed</td><td class="right"><b>{$GLOBAL.userdata.balance.unconfirmed|default:"0"}</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="bendl"></div>
|
||||
<div class="bendr"></div>
|
||||
</div>
|
||||
63
public/templates/mobile/global/sidebar_pplns.tpl
Normal file
63
public/templates/mobile/global/sidebar_pplns.tpl
Normal file
@ -0,0 +1,63 @@
|
||||
<table>
|
||||
<tr>
|
||||
<td><b>PPLNS Target</b></td>
|
||||
<td align="right">{$GLOBAL.pplns.target|number_format}</td>
|
||||
</tr>
|
||||
<tr><td colspan="2"> </td></tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>Your Stats</u></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Hashrate</b></td>
|
||||
<td align="right">{$GLOBAL.userdata.hashrate|number_format} KH/s</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>Unpaid Shares</u></b> <span id='tt'><img src='{$PATH}/images/questionmark.png' height='15px' width='15px' title='Submitted shares between the last 120 confirms block until now.'></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Your Valid<b></td>
|
||||
<td align="right"><i>{$GLOBAL.userdata.shares.valid|number_format}</i><font size='1px'></font></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Pool Valid</td>
|
||||
<td align="right"><i>{$GLOBAL.roundshares.valid|number_format}</i> <font size='1px'></font></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>Round Shares</u></b> <span id='tt'><img src='{$PATH}/images/questionmark.png' height='15px' width='15px' title='Submitted shares since last found block (ie. round shares)'></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Pool Valid</b></td>
|
||||
<td align="right"><i>{$GLOBAL.roundshares.valid|number_format}</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Pool Invalid</b></td>
|
||||
<td align="right"><i>{$GLOBAL.roundshares.invalid|number_format}</i>{if $GLOBAL.roundshares.valid > 0}<font size='1px'> ({(100 / $GLOBAL.roundshares.valid * $GLOBAL.roundshares.invalid)|number_format:"2"}%)</font>{/if}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Your Invalid</b></td>
|
||||
<td align="right"><i>{$GLOBAL.userdata.shares.invalid|number_format}</i>{if $GLOBAL.roundshares.valid > 0}<font size='1px'> ({(100 / $GLOBAL.roundshares.valid * $GLOBAL.userdata.shares.invalid)|number_format:"2"}%)</font>{/if}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>{$GLOBAL.config.currency} Round Estimate</u></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Block</b></td>
|
||||
<td align="right">{$GLOBAL.userdata.est_block|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Fees</b></td>
|
||||
<td align="right">{$GLOBAL.userdata.est_fee|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Donation</b></td>
|
||||
<td align="right">{$GLOBAL.userdata.est_donation|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Payout</b></td>
|
||||
<td align="right">{$GLOBAL.userdata.est_payout|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr><td colspan="2"> </td></tr>
|
||||
<tr><td colspan="2"><b><u>{$GLOBAL.config.currency} Account Balance</u></b></td></tr>
|
||||
<tr><td>Confirmed</td><td align="right"><b>{$GLOBAL.userdata.balance.confirmed|default:"0"}</td></tr>
|
||||
<tr><td>Unconfirmed</td><td align="right"><b>{$GLOBAL.userdata.balance.unconfirmed|default:"0"}</td></tr>
|
||||
</table>
|
||||
Loading…
Reference in New Issue
Block a user