Merge pull request #648 from TheSerapher/issue-145

Issue 145
This commit is contained in:
Sebastian Grewe 2013-09-30 00:53:45 -07:00
commit db89768cd5
20 changed files with 235 additions and 88 deletions

View File

@ -48,7 +48,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
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'] ;
$pplns_target = $config['pplns']['shares']['default'];
}
if (!$aBlock['accounted']) {
@ -68,7 +68,9 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
if ($iRoundShares >= $pplns_target) {
$log->logDebug("Matching or exceeding PPLNS target of $pplns_target with $iRoundShares");
$aAccountShares = $share->getSharesForAccounts($aBlock['share_id'] - $pplns_target, $aBlock['share_id']);
$iMinimumShareId = $share->getMinimumShareId($pplns_target, $aBlock['share_id']);
// We need to go one ID lower due to `id >` or we won't match if minimum share ID == $aBlock['share_id']
$aAccountShares = $share->getSharesForAccounts($iMinimumShareId - 1, $aBlock['share_id']);
if (empty($aAccountShares)) {
$log->logFatal("No shares found for this block, aborted! Block Height : " . $aBlock['height']);
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
@ -76,8 +78,11 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
}
$log->logInfo('Adjusting round target to PPLNS target ' . $pplns_target);
$iRoundShares = $pplns_target;
foreach($aAccountShares as $key => $aData) {
$iNewRoundShares += $aData['valid'];
}
$log->logInfo('Adjusting round target to PPLNS target ' . $iNewRoundShares);
$iRoundShares = $iNewRoundShares;
} else {
$log->logDebug("Not able to match PPLNS target of $pplns_target with $iRoundShares");
// We need to fill up with archived shares
@ -156,6 +161,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
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']);

View File

@ -45,18 +45,29 @@ if ( $bitcoin->can_connect() === true ){
exit(1);
}
// Value per share calculation
if ($config['reward_type'] != 'block') {
$pps_value = round($config['reward'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12);
// We support some dynamic reward targets but fall back to our fixed value
// Re-calculate after each run due to re-targets in this loop
if ($config['pps']['reward']['type'] == 'blockavg' && $block->getBlockCount() > 0) {
$pps_reward = round($block->getAvgBlockReward($config['pps']['blockavg']['blockcount']));
$log->logInfo("PPS reward using block average, amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty);
} else {
// Try to find the last block value and use that for future payouts, revert to fixed reward if none found
if ($aLastBlock = $block->getLast()) {
$pps_value = round($aLastBlock['amount'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12);
if ($config['pps']['reward']['type'] == 'block') {
if ($aLastBlock = $block->getLast()) {
$pps_reward = $aLastBlock['amount'];
$log->logInfo("PPS reward using last block, amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty);
} else {
$pps_reward = $config['pps']['reward']['default'];
$log->logInfo("PPS reward using default, amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty);
}
} else {
$pps_value = round($config['reward'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12);
$pps_reward = $config['pps']['reward']['default'];
$log->logInfo("PPS reward fixed default, amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty);
}
}
// Per-share value to be paid out to users
$pps_value = round($pps_reward / (pow(2,32) * $dDifficulty) * pow(2, $config['pps_target']), 12);
// Find our last share accounted and last inserted share for PPS calculations
$iPreviousShareId = $setting->getValue('pps_last_share_id');
$iLastShareId = $share->getLastInsertedShareId();
@ -87,7 +98,7 @@ foreach ($aAccountShares as $aData) {
number_format($pps_value, 12) . "\t=\t" .
number_format($aData['payout'], 8) . "\t" .
number_format($aData['donation'], 8) . "\t" .
number_format($aData['fee']), 8);
number_format($aData['fee'], 8));
// Add new credit transaction
if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit_PPS'))
@ -107,9 +118,7 @@ $setting->setValue('pps_last_share_id', $iLastShareId);
// Fetch all unaccounted blocks
$aAllBlocks = $block->getAllUnaccounted('ASC');
if (empty($aAllBlocks)) {
$log->logDebug("No new unaccounted blocks found");
}
if (empty($aAllBlocks)) $log->logDebug("No new unaccounted blocks found");
// Go through blocks and archive/delete shares that have been accounted for
foreach ($aAllBlocks as $iIndex => $aBlock) {

View File

@ -116,6 +116,18 @@ class Block {
return false;
}
/**
* Fetch our average rewards for the past N blocks
* @param limit int Maximum blocks to check
* @return data float Float value of average shares
**/
public function getAvgBlockReward($limit=1) {
$stmt = $this->mysqli->prepare("SELECT AVG(x.amount) AS average FROM (SELECT amount FROM $this->table ORDER BY height DESC LIMIT ?) AS x");
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

View File

@ -11,7 +11,7 @@ class Share {
private $oUpstream;
private $iLastUpstreamId;
// This defines each share
public $rem_host, $username, $our_result, $upstream_result, $reason, $solution, $time;
public $rem_host, $username, $our_result, $upstream_result, $reason, $solution, $time, $difficulty;
public function __construct($debug, $mysqli, $user, $block, $config) {
$this->debug = $debug;
@ -70,7 +70,7 @@ class Share {
**/
public function getRoundShares($previous_upstream=0, $current_upstream) {
$stmt = $this->mysqli->prepare("SELECT
count(id) as total
ROUND(IFNULL(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 8) AS total
FROM $this->table
WHERE our_result = 'Y'
AND id > ? AND id <= ?
@ -98,8 +98,8 @@ class Share {
a.id,
SUBSTRING_INDEX( s.username , '.', 1 ) as username,
a.no_fees AS no_fees,
IFNULL(SUM(IF(our_result='Y', 1, 0)), 0) AS valid,
IFNULL(SUM(IF(our_result='N', 1, 0)), 0) AS invalid
ROUND(IFNULL(SUM(IF(our_result='Y', IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 8) AS valid,
ROUND(IFNULL(SUM(IF(our_result='N', IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 8) AS invalid
FROM $this->table AS s
LEFT JOIN " . $this->user->getTableName() . " AS a
ON a.username = SUBSTRING_INDEX( s.username , '.', 1 )
@ -140,15 +140,15 @@ class Share {
* return array data Returns an array with usernames as keys for easy access
**/
function getArchiveShares($iCount) {
$iMinId = $this->getMaxArchiveShareId() - $iCount;
$iMinId = $this->getMinArchiveShareId($iCount);
$iMaxId = $this->getMaxArchiveShareId();
$stmt = $this->mysqli->prepare("
SELECT
a.id,
SUBSTRING_INDEX( s.username , '.', 1 ) as account,
a.no_fees AS no_fees,
IFNULL(SUM(IF(our_result='Y', 1, 0)), 0) AS valid,
IFNULL(SUM(IF(our_result='N', 1, 0)), 0) AS invalid
ROUND(IFNULL(SUM(IF(our_result='Y', IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 8) AS valid,
ROUND(IFNULL(SUM(IF(our_result='N', IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 8) AS invalid
FROM $this->tableArchive AS s
LEFT JOIN " . $this->user->getTableName() . " AS a
ON a.username = SUBSTRING_INDEX( s.username , '.', 1 )
@ -198,8 +198,8 @@ class Share {
**/
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
INSERT INTO $this->tableArchive (share_id, username, our_result, upstream_result, block_id, time, difficulty)
SELECT id, username, our_result, upstream_result, ?, time, IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty) AS difficulty
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()) {
@ -317,6 +317,48 @@ class Share {
return false;
}
/**
* Fetch the lowest needed share ID from shares
**/
function getMinimumShareId($iCount, $current_upstream) {
// We don't use baseline here to be more accurate
$iCount = $iCount * pow(2, ($this->config['difficulty'] - 16));
$stmt = $this->mysqli->prepare("
SELECT MIN(b.id) AS id FROM
(
SELECT id, @total := @total + IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty) AS total
FROM $this->table, (SELECT @total := 0) AS a
WHERE our_result = 'Y'
AND id <= ? AND @total < ?
ORDER BY id DESC
) AS b
WHERE total <= ?
");
if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $current_upstream, $iCount, $iCount) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->id;
return false;
}
/**
* Fetch the lowest needed share ID from archive
**/
function getMinArchiveShareId($iCount) {
$stmt = $this->mysqli->prepare("
SELECT MIN(b.share_id) AS share_id FROM
(
SELECT share_id, @total := @total + (IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty) / POW(2, (" . $this->config['difficulty'] . " - 16))) AS total
FROM $this->tableArchive, (SELECT @total := 0) AS a
WHERE our_result = 'Y'
AND @total < ?
ORDER BY share_id DESC
) AS b
WHERE total <= ?
");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iCount, $iCount) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->share_id;
return false;
}
/**
* Helper function
**/

View File

@ -64,7 +64,8 @@ class Statistics {
SELECT
b.*,
a.username AS finder,
a.is_anonymous AS is_anonymous
a.is_anonymous AS is_anonymous,
ROUND((difficulty * 65535) / POW(2, (" . $this->config['difficulty'] . " -16)), 0) AS estshares
FROM " . $this->block->getTableName() . " AS b
LEFT JOIN " . $this->user->getTableName() . " AS a
ON b.account_id = a.id
@ -105,13 +106,13 @@ class Statistics {
SELECT
(
(
SELECT IFNULL(ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ")/600/1000), 0) AS hashrate
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / 600 / 1000), 0) AS hashrate
FROM " . $this->share->getTableName() . "
WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)
WHERE time > DATE_SUB(now(), INTERVAL 600 SECOND)
) + (
SELECT IFNULL(ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ")/600/1000), 0) AS hashrate
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / 600 / 1000), 0) AS hashrate
FROM " . $this->share->getArchiveTableName() . "
WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)
WHERE time > DATE_SUB(now(), INTERVAL 600 SECOND)
)
) AS hashrate
FROM DUAL");
@ -172,10 +173,10 @@ class Statistics {
}
$stmt = $this->mysqli->prepare("
SELECT
IFNULL(SUM(IF(our_result='Y', 1, 0)), 0) AS valid,
IFNULL(SUM(IF(our_result='N', 1, 0)), 0) AS invalid
ROUND(IFNULL(SUM(IF(our_result='Y', IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS valid,
ROUND(IFNULL(SUM(IF(our_result='N', IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS invalid
FROM " . $this->share->getTableName() . "
WHERE UNIX_TIMESTAMP(time) >IFNULL((SELECT MAX(time) FROM " . $this->block->getTableName() . "),0)");
WHERE UNIX_TIMESTAMP(time) > IFNULL((SELECT MAX(time) FROM " . $this->block->getTableName() . "), 0)");
if ( $this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() )
return $this->memcache->setCache(STATISTICS_ROUND_SHARES, $result->fetch_assoc());
// Catchall
@ -197,8 +198,8 @@ class Statistics {
}
$stmt = $this->mysqli->prepare("
SELECT
IFNULL(SUM(IF(our_result='Y', 1, 0)), 0) AS valid,
IFNULL(SUM(IF(our_result='N', 1, 0)), 0) AS invalid,
ROUND(IFNULL(SUM(IF(our_result='Y', IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS valid,
ROUND(IFNULL(SUM(IF(our_result='N', IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS invalid,
u.id AS id,
u.donate_percent AS donate_percent,
u.is_anonymous AS is_anonymous,
@ -243,8 +244,8 @@ class Statistics {
if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
$stmt = $this->mysqli->prepare("
SELECT
IFNULL(SUM(IF(our_result='Y', 1, 0)), 0) AS valid,
IFNULL(SUM(IF(our_result='N', 1, 0)), 0) AS invalid
ROUND(IFNULL(SUM(IF(our_result='Y', IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS valid,
ROUND(IFNULL(SUM(IF(our_result='N', IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS invalid
FROM " . $this->share->getTableName() . " AS s,
" . $this->user->getTableName() . " AS u
WHERE
@ -274,7 +275,7 @@ class Statistics {
a.username AS username,
a.donate_percent AS donate_percent,
a.email AS email,
COUNT(s.id) AS shares
ROUND(IFNULL(SUM(IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS shares
FROM " . $this->user->getTableName() . " AS a
LEFT JOIN " . $this->share->getTableName() . " AS s
ON a.username = SUBSTRING_INDEX( s.username, '.', 1 )
@ -298,18 +299,18 @@ class Statistics {
$stmt = $this->mysqli->prepare("
SELECT
(
SELECT IFNULL(ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 600 / 1000), 0) AS hashrate
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / 600 / 1000), 0) AS hashrate
FROM " . $this->share->getTableName() . " AS s,
" . $this->user->getTableName() . " AS u
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND s.time > DATE_SUB(now(), INTERVAL 10 MINUTE)
AND s.time > DATE_SUB(now(), INTERVAL 600 SECOND)
AND u.id = ?
) + (
SELECT IFNULL(ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 600 / 1000), 0) AS hashrate
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / 600 / 1000), 0) AS hashrate
FROM " . $this->share->getArchiveTableName() . " AS s,
" . $this->user->getTableName() . " AS u
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND s.time > DATE_SUB(now(), INTERVAL 10 MINUTE)
AND s.time > DATE_SUB(now(), INTERVAL 600 SECOND)
AND u.id = ?
) AS hashrate
FROM DUAL");
@ -364,11 +365,11 @@ class Statistics {
$this->debug->append("STA " . __METHOD__, 4);
if ($data = $this->memcache->get(__FUNCTION__ . $worker_id)) return $data;
$stmt = $this->mysqli->prepare("
SELECT IFNULL(ROUND(COUNT(s.id) * POW(2,21)/600/1000), 0) AS hashrate
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / 600 / 1000), 0) AS hashrate
FROM " . $this->share->getTableName() . " AS s,
" . $this->user->getTableName() . " AS u
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND s.time > DATE_SUB(now(), INTERVAL 10 MINUTE)
AND s.time > DATE_SUB(now(), INTERVAL 600 SECOND)
AND u.id = ?");
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result() )
return $this->memcache->setCache(__FUNCTION__ . $worker_id, $result->fetch_object()->hashrate);
@ -407,7 +408,7 @@ class Statistics {
SELECT
a.donate_percent AS donate_percent,
a.is_anonymous AS is_anonymous,
COUNT(s.id) AS shares,
ROUND(IFNULL(SUM(IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS shares,
SUBSTRING_INDEX( s.username, '.', 1 ) AS account
FROM " . $this->share->getTableName() . " AS s
LEFT JOIN " . $this->user->getTableName() . " AS a
@ -424,16 +425,16 @@ class Statistics {
case 'hashes':
$stmt = $this->mysqli->prepare("
SELECT
SELECT
a.donate_percent AS donate_percent,
a.is_anonymous AS is_anonymous,
IFNULL(ROUND(COUNT(t1.id) * POW(2," . $this->config['difficulty'] . ")/600/1000, 2), 0) AS hashrate,
IFNULL(ROUND(SUM(t1.difficulty) * 65536/600/1000, 2), 0) AS hashrate,
SUBSTRING_INDEX( t1.username, '.', 1 ) AS account
FROM
(
SELECT id, username FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y'
UNION
SELECT id, username FROM " . $this->share->getArchiveTableName() ." WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y'
SELECT IFNULL(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0) AS difficulty, username FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y'
UNION ALL
SELECT IFNULL(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0) AS difficulty, username FROM " . $this->share->getArchiveTableName() ." WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y'
) AS t1
LEFT JOIN " . $this->user->getTableName() . " AS a
ON SUBSTRING_INDEX( t1.username, '.', 1 ) = a.username
@ -441,7 +442,7 @@ class Statistics {
ORDER BY hashrate DESC LIMIT ?");
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result())
return $this->memcache->setCache(__FUNCTION__ . $type . $limit, $result->fetch_all(MYSQLI_ASSOC));
$this->debug->append("Fetching shares failed: ");
$this->debug->append("Fetching shares failed: " . $this->mysqli->error);
return false;
break;
}
@ -457,7 +458,7 @@ class Statistics {
if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
$stmt = $this->mysqli->prepare("
SELECT
ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000) AS hashrate,
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate,
HOUR(s.time) AS hour
FROM " . $this->share->getTableName() . " AS s, accounts AS a
WHERE time < NOW() - INTERVAL 1 HOUR
@ -467,7 +468,7 @@ class Statistics {
GROUP BY HOUR(time)
UNION ALL
SELECT
ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000) AS hashrate,
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate,
HOUR(s.time) AS hour
FROM " . $this->share->getArchiveTableName() . " AS s, accounts AS a
WHERE time < NOW() - INTERVAL 1 HOUR
@ -498,7 +499,7 @@ class Statistics {
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__)) return $data;
$stmt = $this->mysqli->prepare("
SELECT
IFNULL(ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000), 0) AS hashrate,
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate,
HOUR(s.time) AS hour
FROM " . $this->share->getTableName() . " AS s
WHERE time < NOW() - INTERVAL 1 HOUR
@ -506,7 +507,7 @@ class Statistics {
GROUP BY HOUR(time)
UNION ALL
SELECT
IFNULL(ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000), 0) AS hashrate,
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate,
HOUR(s.time) AS hour
FROM " . $this->share->getArchiveTableName() . " AS s
WHERE time < NOW() - INTERVAL 1 HOUR

View File

@ -96,21 +96,32 @@ class Worker {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("
SELECT id, username, password, monitor,
( SELECT COUNT(id) FROM " . $this->share->getTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS count_all,
( SELECT COUNT(id) FROM " . $this->share->getArchiveTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS count_all_archive,
(
SELECT
IFNULL(IF(our_result='Y', ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ") / 600 / 1000), 0), 0) AS hashrate
IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536/600/1000), 0), 0) AS hashrate
FROM " . $this->share->getTableName() . "
WHERE
username = w.username
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
) + (
SELECT
IFNULL(IF(our_result='Y', ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ") / 600 / 1000), 0), 0) AS hashrate
IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536/600/1000), 0), 0) AS hashrate
FROM " . $this->share->getArchiveTableName() . "
WHERE
username = w.username
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
) AS hashrate
) AS hashrate,
(
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all, 2), 0)
FROM " . $this->share->getTableName() . "
WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
) + (
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all_archive, 2), 0)
FROM " . $this->share->getArchiveTableName() . "
WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
) AS difficulty
FROM $this->table AS w
WHERE id = ?
");
@ -129,21 +140,32 @@ class Worker {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("
SELECT id, username, password, monitor,
( SELECT COUNT(id) FROM " . $this->share->getTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS count_all,
( SELECT COUNT(id) FROM " . $this->share->getArchiveTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS count_all_archive,
(
SELECT
IFNULL(IF(our_result='Y', ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ") / 600 / 1000), 0), 0) AS hashrate
IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536/600/1000), 0), 0) AS hashrate
FROM " . $this->share->getTableName() . "
WHERE
username = w.username
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
) + (
SELECT
IFNULL(IF(our_result='Y', ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ") / 600 / 1000), 0), 0) AS hashrate
) + (
SELECT
IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536/600/1000), 0), 0) AS hashrate
FROM " . $this->share->getArchiveTableName() . "
WHERE
username = w.username
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
) AS hashrate
) AS hashrate,
(
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all, 2), 0)
FROM " . $this->share->getTableName() . "
WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
) + (
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all_archive, 2), 0)
FROM " . $this->share->getArchiveTableName() . "
WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
) AS difficulty
FROM $this->table AS w
WHERE account_id = ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result())

View File

@ -279,6 +279,28 @@ $config['confirmations'] = 120;
// Confirmations per block required in network to confirm its transactions, default: 120
$config['network_confirmations'] = 120;
/**
* Available pps options:
* reward_type:
* fixed : Fixed value according to `reward` setting
* blockavg : Dynamic value based on average of x number of block rewards
* block : Dynamic value based on LAST block amount
* reward:
* float value : Any value of your choice but should reflect base block values
* blockcount : amount of blocks to average, any integer
* Default:
* pps_reward_type = `fixed` default $config['pps']['reward']['default']
* reward = 50
*
**/
$config['pps']['reward']['default'] = 50;
$config['pps']['reward']['type'] = 'blockavg';
$config['pps']['blockavg']['blockcount'] = 10;
// pps base payout target, default 16 = difficulty 1 shares for vardiff
// (1/(65536 * difficulty) * reward) = (reward / (pow(2,32) * difficulty) * pow(2, 16))
$config['pps_target'] = 16; // do not change unless you know what it does
/**
* Memcache configuration
*

View File

@ -9,10 +9,12 @@ $debug->append('Global smarty variables', 3);
$debug->append('No cached page detected, loading smarty globals', 3);
// Defaults to get rid of PHP Notice warnings
$dDifficulty = 1;
$aRoundShares = 1;
// Only run these if the user is logged in
$aRoundShares = $statistics->getRoundShares();
// Fetch round shares
if (!$aRoundShares = $statistics->getRoundShares()) {
$aRoundShares = array('valid' => 0, 'invalid' => 0);
}
if ($bitcoin->can_connect() === true) {
$dDifficulty = $bitcoin->getdifficulty();
$dNetworkHashrate = $bitcoin->getnetworkhashps();
@ -91,18 +93,24 @@ $aGlobal['acl']['pool']['statistics'] = $setting->getValue('acl_pool_statistics'
$aGlobal['acl']['block']['statistics'] = $setting->getValue('acl_block_statistics');
$aGlobal['acl']['round']['statistics'] = $setting->getValue('acl_round_statistics');
// We support some dynamic reward targets but fall back to our fixed value
// Special calculations for PPS Values based on reward_type setting and/or available blocks
if ($config['reward_type'] != 'block') {
$aGlobal['ppsvalue'] = number_format(round($config['reward'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12);
if ($config['pps']['reward']['type'] == 'blockavg' && $block->getBlockCount() > 0) {
$pps_reward = round($block->getAvgBlockReward($config['pps']['blockavg']['blockcount']));
} else {
// Try to find the last block value and use that for future payouts, revert to fixed reward if none found
if ($aLastBlock = $block->getLast()) {
$aGlobal['ppsvalue'] = number_format(round($aLastBlock['amount'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12);
if ($config['pps']['reward']['type'] == 'block') {
if ($aLastBlock = $block->getLast()) {
$pps_reward = $aLastBlock['amount'];
} else {
$pps_reward = $config['pps']['reward']['default'];
}
} else {
$aGlobal['ppsvalue'] = number_format(round($config['reward'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12);
$pps_reward = $config['pps']['reward']['default'];
}
}
$aGlobal['ppsvalue'] = number_format(round($pps_reward / (pow(2,32) * $dDifficulty) * pow(2, $config['pps_target']), 12) ,12);
// We don't want these session infos cached
if (@$_SESSION['USERDATA']['id']) {
$aGlobal['userdata'] = $_SESSION['USERDATA']['id'] ? $user->getUserData($_SESSION['USERDATA']['id']) : array();

View File

@ -7,13 +7,13 @@
<table border="0" cellpadding="3" cellspacing="3">
<tbody>
<tr>
<td>Worker Name</td>
<td>Password</td>
<td class="center">Active</td>
{if $GLOBAL.config.disable_notifications != 1}<td class="center">Monitor</td>{/if}
<td class="right">Khash/s</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<th>Worker Name</th>
<th>Password</th>
<th class="center">Active</th>
{if $GLOBAL.config.disable_notifications != 1}<th class="center">Monitor</th>{/if}
<th class="right">Khash/s</th>
<th class="right">Difficulty</th>
<th>Action</th>
</tr>
{nocache}
{section worker $WORKERS}
@ -29,6 +29,7 @@
</td>
{/if}
<td class="right">{$WORKERS[worker].hashrate|number_format}</td>
<td class="right">{$WORKERS[worker].difficulty|number_format:"2"}</td>
<td align="right"><a href="{$smarty.server.PHP_SELF}?page={$smarty.request.page|escape}&action={$smarty.request.action|escape}&do=delete&id={$WORKERS[worker].id|escape}"><button style="padding:5px" type="button">Delete</button></a></td>
</tr>
{/section}

View File

@ -18,6 +18,10 @@
<td><b>Hashrate</b></td>
<td class="right">{$GLOBAL.userdata.hashrate|number_format:"2"} {$GLOBAL.hashunits.personal}</td>
</tr>
<tr>
<td><b>Share Rate</b></td>
<td class="right">{$GLOBAL.userdata.sharerate|number_format:"2"} S/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>

View File

@ -13,6 +13,10 @@
<td><b>Hashrate</b></td>
<td class="right">{$GLOBAL.userdata.hashrate|number_format:"2"} {$GLOBAL.hashunits.personal}</td>
</tr>
<tr>
<td><b>Share Rate</b></td>
<td class="right">{$GLOBAL.userdata.sharerate|number_format:"2"} S/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>

View File

@ -12,7 +12,7 @@
<tr>
<th scope="row">Expected</th>
{section block $BLOCKSFOUND step=-1}
<td>{round(pow(2,32 - $GLOBAL.config.targetdiff) * $BLOCKSFOUND[block].difficulty)}</td>
<td>{$BLOCKSFOUND[block].estshares}</td>
{/section}
</tr>
<tr>
@ -64,16 +64,15 @@ target and network difficulty and assuming a zero variance scenario.
{else}{$GLOBAL.confirmations - $BLOCKSFOUND[block].confirmations} left{/if}</td>
<td>{if $BLOCKSFOUND[block].is_anonymous|default:"0" == 1}anonymous{else}{$BLOCKSFOUND[block].finder|default:"unknown"|escape}{/if}</td>
<td class="center">{$BLOCKSFOUND[block].time|date_format:"%d/%m %H:%M:%S"}</td>
<td class="right">{$BLOCKSFOUND[block].difficulty|number_format:"2"}</td>
<td class="right">{$BLOCKSFOUND[block].difficulty|number_format:"8"}</td>
<td class="right">{$BLOCKSFOUND[block].amount|number_format:"2"}</td>
<td class="right">
{math assign="estshares" equation="(pow(2,32 - targetdiff) * blockdiff)" targetdiff=$GLOBAL.config.targetdiff blockdiff=$BLOCKSFOUND[block].difficulty}
{assign var="totalexpectedshares" value=$totalexpectedshares+$estshares}
{$estshares|number_format}
{$BLOCKSFOUND[block].estshares|number_format}
{assign var="totalexpectedshares" value=$totalexpectedshares+$BLOCKSFOUND[block].estshares}
</td>
<td class="right">{$BLOCKSFOUND[block].shares|number_format}</td>
<td class="right">
{math assign="percentage" equation="shares / estshares * 100" shares=$BLOCKSFOUND[block].shares estshares=$estshares}
{math assign="percentage" equation="shares / estshares * 100" shares=$BLOCKSFOUND[block].shares estshares=$BLOCKSFOUND[block].estshares}
{assign var="totalpercentage" value=$totalpercentage+$percentage}
<font color="{if ($percentage <= 100)}green{else}red{/if}">{$percentage|number_format:"2"}</font>
</td>

View File

@ -48,7 +48,8 @@
</tr>
<tr>
<td class="leftheader">Est. Shares this Round</td>
<td>{(pow(2, 32 - $GLOBAL.config.targetdiff) * $DIFFICULTY)|number_format:"0"} <font size="1">(done: {(100 / (pow(2, 32 - $GLOBAL.config.targetdiff) * $DIFFICULTY) * $GLOBAL.roundshares.valid)|number_format:"2"} %)</td>
{assign var=estshares value=(65536 * $DIFFICULTY) / pow(2, ($GLOBAL.config.targetdiff - 16))}
<td>{$estshares|number_format:"0"} <font size="1">(done: {(100 / $estshares * $GLOBAL.roundshares.valid)|number_format:"2"} %)</td>
</tr>
<tr>
<td class="leftheader">Time Since Last Block</td>

View File

@ -4,6 +4,7 @@
<th align="left">Worker Name</th>
<th align="center">Active</th>
<th align="right">Khash/s</th>
<th align="right">Difficulty</th>
</tr>
{section worker $WORKERS}
{assign var="username" value="."|escape|explode:$WORKERS[worker].username:2}
@ -11,6 +12,7 @@
<td align="left"{if $WORKERS[worker].active} style="color: orange"{/if}>{$username.0|escape}.{$username.1|escape}</td>
<td align="center"><img src="{$PATH}/images/{if $WORKERS[worker].hashrate > 0}success{else}error{/if}.gif" /></td>
<td align="right">{$WORKERS[worker].hashrate|number_format}</td>
<td class="right">{$WORKERS[worker].hashrate|number_format}</td>
</tr>
{/section}
</tbody>

View File

@ -11,6 +11,10 @@
<td><b>Hashrate</b></td>
<td align="right">{$GLOBAL.userdata.hashrate|number_format} KH/s</td>
</tr>
<tr>
<td><b>Share Rate</b></td>
<td align="right">{$GLOBAL.userdata.sharerate|number_format:"2"} S/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>

View File

@ -7,6 +7,10 @@
<td><b>Hashrate</b></td>
<td align="right">{$GLOBAL.userdata.hashrate|number_format} KH/s</td>
</tr>
<tr>
<td><b>Share Rate</b></td>
<td align="right">{$GLOBAL.userdata.sharerate|number_format:"2"} S/s</td>
</tr>
<tr><td colspan="2">&nbsp;</td></tr>
<tr>
<td colspan="2"><b><u>Round Shares</u></b></td>

View File

@ -56,9 +56,9 @@ target and network difficulty and assuming a zero variance scenario.
<td>{if $BLOCKSFOUND[block].is_anonymous|default:"0" == 1}anonymous{else}{$BLOCKSFOUND[block].finder|default:"unknown"|escape}{/if}</td>
<td class="center">{$BLOCKSFOUND[block].time|date_format:"%d/%m %H:%M:%S"}</td>
<td class="right">{$BLOCKSFOUND[block].difficulty|number_format:"2"}</td>
<td class="right">{(pow(2,32 - $GLOBAL.config.targetdiff) * $BLOCKSFOUND[block].difficulty)|number_format}</td>
<td class="right">{(65536 * $BLOCKSFOUND[block].difficulty)|number_format}</td>
<td class="right">{$BLOCKSFOUND[block].shares|number_format}</td>
<td class="right">{($BLOCKSFOUND[block].shares / (pow(2,32 - $GLOBAL.config.targetdiff) * $BLOCKSFOUND[block].difficulty) * 100)|number_format:"2"}</td>
<td class="right">{($BLOCKSFOUND[block].shares / (65536 * $BLOCKSFOUND[block].difficulty) * 100)|number_format:"2"}</td>
</tr>
{/section}
</tbody>

View File

@ -51,7 +51,7 @@
</tr>
<tr>
<td class="leftheader">Est. Shares this Round</td>
<td>{(pow(2, 32 - $GLOBAL.config.targetdiff) * $DIFFICULTY)|number_format:"0"} <font size="1">(done: {(100 / (pow(2, 32 - $GLOBAL.config.targetdiff) * $DIFFICULTY) * $GLOBAL.roundshares.valid)|number_format:"2"} %)</td>
<td>{((65536 * $DIFFICULTY) / pow(2, ($GLOBAL.config.targetdiff - 16)))|number_format:"0"} <font size="1">(done: {(100 / ((65536 * $DIFFICULTY) / pow(2, ($GLOBAL.config.targetdiff - 16))) * $GLOBAL.roundshares.valid)|number_format:"2"} %)</td>
</tr>
<tr>
<td class="leftheader">Time Since Last Block</td>

View File

@ -80,6 +80,7 @@ CREATE TABLE IF NOT EXISTS `pool_worker` (
`account_id` int(255) NOT NULL,
`username` char(50) DEFAULT NULL,
`password` char(255) DEFAULT NULL,
`difficulty` float NOT NULL DEFAULT '0',
`monitor` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
@ -101,6 +102,7 @@ CREATE TABLE IF NOT EXISTS `shares` (
`upstream_result` enum('Y','N') DEFAULT NULL,
`reason` varchar(50) DEFAULT NULL,
`solution` varchar(257) NOT NULL,
`difficulty` float NOT NULL DEFAULT '0',
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `time` (`time`),
@ -116,6 +118,7 @@ CREATE TABLE IF NOT EXISTS `shares_archive` (
`our_result` enum('Y','N') DEFAULT NULL,
`upstream_result` enum('Y','N') DEFAULT NULL,
`block_id` int(10) unsigned NOT NULL,
`difficulty` float NOT NULL DEFAULT '0',
`time` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `share_id` (`share_id`),

View File

@ -0,0 +1,3 @@
ALTER TABLE `shares` ADD `difficulty` FLOAT NOT NULL AFTER `solution` ;
ALTER TABLE `shares_archive` ADD `difficulty` FLOAT NOT NULL AFTER `time` ;
ALTER TABLE `pool_worker` ADD `difficulty` FLOAT NOT NULL AFTER `password` ;