diff --git a/cronjobs/pplns_payout.php b/cronjobs/pplns_payout.php index 8c83b28d..a2fefa9f 100755 --- a/cronjobs/pplns_payout.php +++ b/cronjobs/pplns_payout.php @@ -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']); diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php index 553228c1..7c3515fb 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -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) { diff --git a/public/include/classes/block.class.php b/public/include/classes/block.class.php index a1c730b2..3f88887d 100644 --- a/public/include/classes/block.class.php +++ b/public/include/classes/block.class.php @@ -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 diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index fa30b8ce..705c41e1 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -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 **/ diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index 0ada5d61..bbf741ce 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -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 diff --git a/public/include/classes/worker.class.php b/public/include/classes/worker.class.php index 551e47f7..122d287d 100644 --- a/public/include/classes/worker.class.php +++ b/public/include/classes/worker.class.php @@ -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()) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index d2302b0c..f1cee025 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -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 * diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index 396eb751..8bc9549b 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -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(); diff --git a/public/templates/mmcFE/account/workers/default.tpl b/public/templates/mmcFE/account/workers/default.tpl index 65635b4b..2f35e617 100644 --- a/public/templates/mmcFE/account/workers/default.tpl +++ b/public/templates/mmcFE/account/workers/default.tpl @@ -7,13 +7,13 @@ - - - - {if $GLOBAL.config.disable_notifications != 1}{/if} - - - + + + + {if $GLOBAL.config.disable_notifications != 1}{/if} + + + {nocache} {section worker $WORKERS} @@ -29,6 +29,7 @@ {/if} + {/section} diff --git a/public/templates/mmcFE/global/sidebar_pplns.tpl b/public/templates/mmcFE/global/sidebar_pplns.tpl index fa11f94d..138b40c5 100644 --- a/public/templates/mmcFE/global/sidebar_pplns.tpl +++ b/public/templates/mmcFE/global/sidebar_pplns.tpl @@ -18,6 +18,10 @@ + + + + diff --git a/public/templates/mmcFE/global/sidebar_prop.tpl b/public/templates/mmcFE/global/sidebar_prop.tpl index 3c6206e0..8c9d5e0a 100644 --- a/public/templates/mmcFE/global/sidebar_prop.tpl +++ b/public/templates/mmcFE/global/sidebar_prop.tpl @@ -13,6 +13,10 @@ + + + + diff --git a/public/templates/mmcFE/statistics/blocks/default.tpl b/public/templates/mmcFE/statistics/blocks/default.tpl index 596ccfe9..06049426 100644 --- a/public/templates/mmcFE/statistics/blocks/default.tpl +++ b/public/templates/mmcFE/statistics/blocks/default.tpl @@ -12,7 +12,7 @@ {section block $BLOCKSFOUND step=-1} - + {/section} @@ -64,16 +64,15 @@ target and network difficulty and assuming a zero variance scenario. {else}{$GLOBAL.confirmations - $BLOCKSFOUND[block].confirmations} left{/if} - + diff --git a/public/templates/mmcFE/statistics/pool/authenticated.tpl b/public/templates/mmcFE/statistics/pool/authenticated.tpl index 870909c5..13415e93 100644 --- a/public/templates/mmcFE/statistics/pool/authenticated.tpl +++ b/public/templates/mmcFE/statistics/pool/authenticated.tpl @@ -48,7 +48,8 @@ - + {assign var=estshares value=(65536 * $DIFFICULTY) / pow(2, ($GLOBAL.config.targetdiff - 16))} + diff --git a/public/templates/mobile/account/workers/default.tpl b/public/templates/mobile/account/workers/default.tpl index 09ebf867..996c6467 100644 --- a/public/templates/mobile/account/workers/default.tpl +++ b/public/templates/mobile/account/workers/default.tpl @@ -4,6 +4,7 @@ + {section worker $WORKERS} {assign var="username" value="."|escape|explode:$WORKERS[worker].username:2} @@ -11,6 +12,7 @@ + {/section} diff --git a/public/templates/mobile/global/sidebar_pplns.tpl b/public/templates/mobile/global/sidebar_pplns.tpl index c60359b7..c56a968f 100644 --- a/public/templates/mobile/global/sidebar_pplns.tpl +++ b/public/templates/mobile/global/sidebar_pplns.tpl @@ -11,6 +11,10 @@ + + + + diff --git a/public/templates/mobile/global/sidebar_prop.tpl b/public/templates/mobile/global/sidebar_prop.tpl index f685171e..7ef6db53 100644 --- a/public/templates/mobile/global/sidebar_prop.tpl +++ b/public/templates/mobile/global/sidebar_prop.tpl @@ -7,6 +7,10 @@ + + + + diff --git a/public/templates/mobile/statistics/blocks/default.tpl b/public/templates/mobile/statistics/blocks/default.tpl index d9c8b94d..a8dff9b2 100644 --- a/public/templates/mobile/statistics/blocks/default.tpl +++ b/public/templates/mobile/statistics/blocks/default.tpl @@ -56,9 +56,9 @@ target and network difficulty and assuming a zero variance scenario. - + - + {/section} diff --git a/public/templates/mobile/statistics/pool/authenticated.tpl b/public/templates/mobile/statistics/pool/authenticated.tpl index 842a9829..255c6610 100644 --- a/public/templates/mobile/statistics/pool/authenticated.tpl +++ b/public/templates/mobile/statistics/pool/authenticated.tpl @@ -51,7 +51,7 @@ - + diff --git a/sql/000_base_structure.sql b/sql/000_base_structure.sql index 6f475060..487a3d07 100644 --- a/sql/000_base_structure.sql +++ b/sql/000_base_structure.sql @@ -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`), diff --git a/sql/008_vardiff_support.sql b/sql/008_vardiff_support.sql new file mode 100644 index 00000000..17c704c1 --- /dev/null +++ b/sql/008_vardiff_support.sql @@ -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` ;
Worker NamePasswordActiveMonitorKhash/s  Worker NamePasswordActiveMonitorKhash/sDifficultyAction
{$WORKERS[worker].hashrate|number_format}{$WORKERS[worker].difficulty|number_format:"2"}
Hashrate {$GLOBAL.userdata.hashrate|number_format:"2"} {$GLOBAL.hashunits.personal}
Share Rate{$GLOBAL.userdata.sharerate|number_format:"2"} S/s
Unpaid Shares
Hashrate {$GLOBAL.userdata.hashrate|number_format:"2"} {$GLOBAL.hashunits.personal}
Share Rate{$GLOBAL.userdata.sharerate|number_format:"2"} S/s
Unpaid Shares
Expected{round(pow(2,32 - $GLOBAL.config.targetdiff) * $BLOCKSFOUND[block].difficulty)}{$BLOCKSFOUND[block].estshares}
{if $BLOCKSFOUND[block].is_anonymous|default:"0" == 1}anonymous{else}{$BLOCKSFOUND[block].finder|default:"unknown"|escape}{/if} {$BLOCKSFOUND[block].time|date_format:"%d/%m %H:%M:%S"}{$BLOCKSFOUND[block].difficulty|number_format:"2"}{$BLOCKSFOUND[block].difficulty|number_format:"8"} {$BLOCKSFOUND[block].amount|number_format:"2"} - {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} {$BLOCKSFOUND[block].shares|number_format} - {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} {$percentage|number_format:"2"}
Est. Shares this Round{(pow(2, 32 - $GLOBAL.config.targetdiff) * $DIFFICULTY)|number_format:"0"} (done: {(100 / (pow(2, 32 - $GLOBAL.config.targetdiff) * $DIFFICULTY) * $GLOBAL.roundshares.valid)|number_format:"2"} %){$estshares|number_format:"0"} (done: {(100 / $estshares * $GLOBAL.roundshares.valid)|number_format:"2"} %)
Time Since Last BlockWorker Name Active Khash/sDifficulty
{$username.0|escape}.{$username.1|escape} {$WORKERS[worker].hashrate|number_format}{$WORKERS[worker].hashrate|number_format}
Hashrate {$GLOBAL.userdata.hashrate|number_format} KH/s
Share Rate{$GLOBAL.userdata.sharerate|number_format:"2"} S/s
Unpaid Shares
Hashrate {$GLOBAL.userdata.hashrate|number_format} KH/s
Share Rate{$GLOBAL.userdata.sharerate|number_format:"2"} S/s
 
Round Shares{if $BLOCKSFOUND[block].is_anonymous|default:"0" == 1}anonymous{else}{$BLOCKSFOUND[block].finder|default:"unknown"|escape}{/if} {$BLOCKSFOUND[block].time|date_format:"%d/%m %H:%M:%S"} {$BLOCKSFOUND[block].difficulty|number_format:"2"}{(pow(2,32 - $GLOBAL.config.targetdiff) * $BLOCKSFOUND[block].difficulty)|number_format}{(65536 * $BLOCKSFOUND[block].difficulty)|number_format} {$BLOCKSFOUND[block].shares|number_format}{($BLOCKSFOUND[block].shares / (pow(2,32 - $GLOBAL.config.targetdiff) * $BLOCKSFOUND[block].difficulty) * 100)|number_format:"2"}{($BLOCKSFOUND[block].shares / (65536 * $BLOCKSFOUND[block].difficulty) * 100)|number_format:"2"}
Est. Shares this Round{(pow(2, 32 - $GLOBAL.config.targetdiff) * $DIFFICULTY)|number_format:"0"} (done: {(100 / (pow(2, 32 - $GLOBAL.config.targetdiff) * $DIFFICULTY) * $GLOBAL.roundshares.valid)|number_format:"2"} %){((65536 * $DIFFICULTY) / pow(2, ($GLOBAL.config.targetdiff - 16)))|number_format:"0"} (done: {(100 / ((65536 * $DIFFICULTY) / pow(2, ($GLOBAL.config.targetdiff - 16))) * $GLOBAL.roundshares.valid)|number_format:"2"} %)
Time Since Last Block