Merge pull request #5 from TheSerapher/next

next
This commit is contained in:
obigal 2013-10-28 20:20:38 -07:00
commit 1aff62f636
30 changed files with 823 additions and 105 deletions

View File

@ -51,17 +51,17 @@ if (empty($aTransactions['transactions'])) {
// Let us add those blocks as unaccounted
foreach ($aTransactions['transactions'] as $iIndex => $aData) {
if ( $aData['category'] == 'generate' || $aData['category'] == 'immature' ) {
$aBlockInfo = $bitcoin->query('getblock', $aData['blockhash']);
$aBlockRPCInfo = $bitcoin->query('getblock', $aData['blockhash']);
$config['reward_type'] == 'block' ? $aData['amount'] = $aData['amount'] : $aData['amount'] = $config['reward'];
$aData['height'] = $aBlockInfo['height'];
$aData['difficulty'] = $aBlockInfo['difficulty'];
$aData['height'] = $aBlockRPCInfo['height'];
$aData['difficulty'] = $aBlockRPCInfo['difficulty'];
$log->logInfo(substr($aData['blockhash'], 0, 15) . "...\t" .
$aData['height'] . "\t" .
$aData['amount'] . "\t" .
$aData['confirmations'] . "\t\t" .
$aData['difficulty'] . "\t" .
strftime("%Y-%m-%d %H:%M:%S", $aData['time']));
if ( ! empty($aBlockInfo['flags']) && preg_match('/proof-of-stake/', $aBlockInfo['flags']) ) {
if ( ! empty($aBlockRPCInfo['flags']) && preg_match('/proof-of-stake/', $aBlockRPCInfo['flags']) ) {
$log->logInfo("Block above with height " . $aData['height'] . " not added to database, proof-of-stake block!");
continue;
}
@ -78,38 +78,65 @@ if (empty($aAllBlocks)) {
$log->logDebug('No new blocks without share_id found in database');
} else {
// Loop through our unaccounted blocks
$log->logInfo("Block ID\t\tHeight\tAmount\tShare ID\tShares\tFinder\tType");
$log->logInfo("Block ID\tHeight\t\tAmount\tShare ID\tShares\tFinder\t\tType");
foreach ($aAllBlocks as $iIndex => $aBlock) {
if (empty($aBlock['share_id'])) {
// Fetch this blocks upstream ID
$aBlockInfo = $bitcoin->query('getblock', $aBlock['blockhash']);
if ($share->setUpstream($aBlockInfo, $block->getLastUpstreamId())) {
$iCurrentUpstreamId = $share->getUpstreamId();
$iAccountId = $user->getUserId($share->getUpstreamFinder());
} else {
$log->logFatal('Unable to fetch blocks upstream share, aborted:' . $share->getError());
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Unable to fetch blocks " . $aBlock['height'] . " upstream share: " . $share->getError());
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit;
}
// Fetch share information
if (!$iPreviousShareId = $block->getLastShareId()) {
$iPreviousShareId = $block->getLastShareId();
if ( !$iPreviousShareId && $block->getBlockCount() > 1) {
$iPreviousShareId = 0;
$log->logInfo('Unable to find highest share ID found so far, if this is your first block, this is normal.');
// $log->logError('Unable to find highest share ID found so far, assuming share ID 0 as previous found upstream share.');
}
$iRoundShares = $share->getRoundShares($iPreviousShareId, $iCurrentUpstreamId);
// Store new information
if (!$block->setShareId($aBlock['id'], $iCurrentUpstreamId))
$log->logError('Failed to update share ID in database for block ' . $aBlock['height']);
if (!$block->setFinder($aBlock['id'], $iAccountId))
$log->logError('Failed to update finder account ID in database for block ' . $aBlock['height']);
if (!$block->setShares($aBlock['id'], $iRoundShares))
$log->logError('Failed to update share count in database for block ' . $aBlock['height']);
if ($config['block_bonus'] > 0 && !$transaction->addTransaction($iAccountId, $config['block_bonus'], 'Bonus', $aBlock['id'])) {
$log->logError('Failed to create Bonus transaction in database for user ' . $user->getUserName($iAccountId) . ' for block ' . $aBlock['height']);
// Fetch this blocks upstream ID
$aBlockRPCInfo = $bitcoin->query('getblock', $aBlock['blockhash']);
if ($share->findUpstreamShare($aBlockRPCInfo, $iPreviousShareId)) {
$iCurrentUpstreamId = $share->getUpstreamShareId();
// Out of order share detection
if ($iCurrentUpstreamId < $iPreviousShareId) {
// Fetch our offending block
$aBlockError = $block->getBlockByShareId($iPreviousShareId);
$log->logError('E0001: The block with height ' . $aBlock['height'] . ' found share ' . $iCurrentUpstreamId . ' which is < than ' . $iPreviousShareId . ' of block ' . $aBlockError['height'] . '.');
if ( !$aShareError = $share->getShareById($aBlockError['share_id']) || !$aShareCurrent = $share->getShareById($iCurrentUpstreamId)) {
// We were not able to fetch all shares that were causing this detection to trigger, bail out
$log->logFatal('E0002: Failed to fetch both offending shares ' . $iCurrentUpstreamId . ' and ' . $iPreviousShareId . '. Block height: ' . $aBlock['height']);
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "E0002: Upstream shares not found");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
}
// Shares seem to be out of order, so lets change them
if ( !$share->updateShareById($iCurrentUpstreamId, $aShareError) || !$share->updateShareById($iPreviousShareId, $aShareCurrent)) {
// We couldn't update one of the shares! That might mean they have been deleted already
$log->logFatal('E0003: Failed to change shares order!');
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "E0003: Failed share update");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
}
// Reset our offending block so the next run re-checks the shares
if (!$block->setShareId($aBlockError['id'], NULL) && !$block->setFinder($aBlockError['id'], NULL) || !$block->setShares($aBlockError['id'], NULL)) {
$log->logFatal('E0004: Failed to reset previous block: ' . $aBlockError['height']);
$log->logError('Failed to reset block in database: ' . $aBlockError['height']);
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "E0004: Failed to reset block");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
}
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Out of Order Share detected, autofixed");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(0);
} else {
$iRoundShares = $share->getRoundShares($iPreviousShareId, $iCurrentUpstreamId);
$iAccountId = $user->getUserId($share->getUpstreamFinder());
}
} else {
$log->logFatal('E0005: Unable to fetch blocks upstream share, aborted:' . $share->getError());
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Unable to fetch blocks " . $aBlock['height'] . " upstream share: " . $share->getError());
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
}
$log->logInfo(
@ -122,6 +149,17 @@ if (empty($aAllBlocks)) {
. $share->share_type
);
// Store new information
if (!$block->setShareId($aBlock['id'], $iCurrentUpstreamId))
$log->logError('Failed to update share ID in database for block ' . $aBlock['height']);
if (!$block->setFinder($aBlock['id'], $iAccountId))
$log->logError('Failed to update finder account ID in database for block ' . $aBlock['height']);
if (!$block->setShares($aBlock['id'], $iRoundShares))
$log->logError('Failed to update share count in database for block ' . $aBlock['height']);
if ($config['block_bonus'] > 0 && !$transaction->addTransaction($iAccountId, $config['block_bonus'], 'Bonus', $aBlock['id'])) {
$log->logError('Failed to create Bonus transaction in database for user ' . $user->getUserName($iAccountId) . ' for block ' . $aBlock['height']);
}
if ($setting->getValue('disable_notifications') != 1) {
// Notify users
$aAccounts = $notification->getNotificationAccountIdByType('new_block');

View File

@ -68,7 +68,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
$config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward'];
$aRoundAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']);
$log->logInfo('Shares: ' . $iRoundShares . "\t" . 'Height: ' . $aBlock['height'] . ' Amount: ' . $aBlock['amount'] . "\t" . 'Found by ID: ' . $aBlock['account_id']);
$log->logInfo('Target: ' . $pplns_target . '; Shares: ' . $iRoundShares . '; Height: ' . $aBlock['height'] . '; Amount: ' . $aBlock['amount'] . '; Found by ID: ' . $aBlock['account_id']);
if ($iRoundShares >= $pplns_target) {
$log->logDebug("Matching or exceeding PPLNS target of $pplns_target with $iRoundShares");

View File

@ -54,19 +54,17 @@ if ($config['pps']['reward']['type'] == 'blockavg' && $block->getBlockCount() >
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_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);
$pps_value = round($pps_reward / (pow(2, $config['target_bits']) * $dDifficulty), 12);
// Find our last share accounted and last inserted share for PPS calculations
$iPreviousShareId = $setting->getValue('pps_last_share_id');
@ -75,11 +73,16 @@ $iLastShareId = $share->getLastInsertedShareId();
// Check for all new shares, we start one higher as our last accounted share to avoid duplicates
$aAccountShares = $share->getSharesForAccounts($iPreviousShareId + 1, $iLastShareId);
$log->logInfo("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\tPayout\t\tDonation\tFee");
if (!empty($aAccountShares)) {
// Info for this payout
$log->logInfo("PPS reward type: " . $config['pps']['reward']['type'] . ", amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty . "\tPPS value: " . $pps_value);
$log->logInfo("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\tPayout\t\tDonation\tFee");
}
foreach ($aAccountShares as $aData) {
// Take our valid shares and multiply by per share value
$aData['payout'] = round($aData['valid'] * $pps_value, 8);
// MPOS uses a base difficulty setting to avoid showing weightened shares
// Since we need weightened shares here, we go back to the proper value for payouts
$aData['payout'] = round($aData['valid'] * pow(2, ($config['difficulty'] - 16)) * $pps_value, 8);
// Defaults
$aData['fee' ] = 0;
@ -94,7 +97,7 @@ foreach ($aAccountShares as $aData) {
$log->logInfo($aData['id'] . "\t" .
$aData['username'] . "\t" .
$aData['invalid'] . "\t" .
$aData['valid'] . "\t*\t" .
$aData['valid'] * pow(2, ($config['difficulty'] - 16)) . "\t*\t" .
number_format($pps_value, 12) . "\t=\t" .
number_format($aData['payout'], 8) . "\t" .
number_format($aData['donation'], 8) . "\t" .

View File

@ -1,5 +1,11 @@
<?php
// SHA/Scrypt check
if (empty($config['algorithm']) || $config['algorithm'] == 'scrypt') {
$config['target_bits'] = 16;
} else {
$config['target_bits'] = 32;
}
// Default classes
require_once(CLASS_DIR . '/debug.class.php');
require_once(INCLUDE_DIR . '/lib/KLogger.php');

View File

@ -8,8 +8,12 @@ if (!defined('SECURITY'))
// some cross-class functions.
class Base {
private $sError = '';
protected $table = '';
private $values = array(), $types = '';
public function getTableName() {
return $this->table;
}
public function setDebug($debug) {
$this->debug = $debug;
}
@ -50,6 +54,13 @@ class Base {
return $this->sError;
}
protected function getAllAssoc($value, $field='id', $type='i') {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE $field = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param($type, $value) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc();
return false;
}
/**
* Get a single row from the table
* @param value string Value to search for

View File

@ -56,6 +56,30 @@ class Block {
return false;
}
/**
* Get a specific block, by share_id
* @param share_id int Blocks share_id
* @return data array Block information from DB
**/
public function getBlockByShareId($share_id) {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE share_id = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $share_id) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc();
return false;
}
/**
* Get a specific block, by id
* @param share_id int Blocks share_id
* @return data array Block information from DB
**/
public function getBlockById($id) {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE id = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $id) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc();
return false;
}
/**
* Get our last, highest share ID inserted for a block
* @param none

View File

@ -225,6 +225,97 @@ class RoundStats {
return false;
}
/**
* Get ALL last blocks from height for admin panel
**/
public function getAllReportBlocksFoundHeight($iHeight=0, $limit=10) {
$stmt = $this->mysqli->prepare("
SELECT
height, shares
FROM $this->tableBlocks
WHERE height <= ?
ORDER BY height DESC LIMIT ?");
if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $iHeight, $limit) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
return false;
}
/**
* Get USER last blocks from height for admin panel
**/
public function getUserReportBlocksFoundHeight($iHeight=0, $limit=10, $iUser) {
$stmt = $this->mysqli->prepare("
SELECT
b.height, b.shares
FROM $this->tableBlocks AS b
LEFT JOIN $this->tableStats AS s ON s.block_id = b.id
LEFT JOIN $this->tableUsers AS a ON a.id = s.account_id
WHERE b.height <= ? AND a.id = ?
ORDER BY height DESC LIMIT ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $iHeight, $iUser, $limit) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
return false;
}
/**
* Get shares for block height for user admin panel
**/
public function getRoundStatsForUser($iHeight=0, $iUser) {
$stmt = $this->mysqli->prepare("
SELECT
s.valid,
s.invalid,
s.pplns_valid,
s.pplns_invalid
FROM $this->tableStats AS s
LEFT JOIN $this->tableBlocks AS b ON s.block_id = b.id
LEFT JOIN $this->tableUsers AS a ON a.id = s.account_id
WHERE b.height = ? AND a.id = ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iHeight, $iUser) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc();
return false;
}
/**
* Get credit transactions for round block height for admin panel
**/
public function getUserRoundTransHeight($iHeight=0, $iUser) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("
SELECT
IFNULL(t.amount, 0) AS amount
FROM $this->tableTrans AS t
LEFT JOIN $this->tableBlocks AS b ON t.block_id = b.id
LEFT JOIN $this->tableUsers AS a ON t.account_id = a.id
WHERE b.height = ? AND t.type = 'Credit' AND t.account_id = ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iHeight, $iUser) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->amount;
$this->debug->append('Unable to fetch transactions');
return false;
}
/**
* Get all users for admin panel
**/
public function getAllUsers($filter='%') {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("
SELECT
a.id AS id,
a.username AS username
FROM $this->tableUsers AS a
WHERE a.username LIKE ?
GROUP BY username
ORDER BY username");
if ($this->checkStmt($stmt) && $stmt->bind_param('s', $filter) && $stmt->execute() && $result = $stmt->get_result()) {
while ($row = $result->fetch_assoc()) {
$aData[$row['id']] = $row['username'];
}
return $aData;
}
return false;
}
private function checkStmt($bState) {
if ($bState ===! true) {
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error);

View File

@ -4,9 +4,8 @@
if (!defined('SECURITY'))
die('Hacking attempt');
class Share {
private $sError = '';
private $table = 'shares';
class Share Extends Base {
protected $table = 'shares';
private $tableArchive = 'shares_archive';
private $oUpstream;
private $iLastUpstreamId;
@ -22,14 +21,6 @@ class Share {
$this->debug->append("Instantiated Share class", 2);
}
// get and set methods
private function setErrorMessage($msg) {
$this->sError = $msg;
}
public function getError() {
return $this->sError;
}
/**
* Fetch archive tables name for this class
* @param none
@ -38,13 +29,43 @@ class Share {
public function getArchiveTableName() {
return $this->tableArchive;
}
/**
* Fetch normal table name for this class
* @param none
* @return data string Table name
* Fetch a single share by ID
* @param id int Share ID
* @return array Share data
**/
public function getTableName() {
return $this->table;
public function getShareById($id) {
return $this->getAllAssoc($id);
}
/**
* Update an entire shares data
**/
public function updateShareById($id, $data) {
$this->debug->append("STA " . __METHOD__, 4);
$sql = "UPDATE $this->table SET";
$start = true;
// Remove ID column
unset($data['id']);
foreach ($data as $column => $value) {
$start == true ? $sql .= " $column = ? " : $sql .= ", $column = ?";
$start = false;
switch($column) {
case 'difficulty':
$this->addParam('d', $value);
break;
default:
$this->addParam('s', $value);
break;
}
}
$sql .= " WHERE id = ? LIMIT 1";
$this->addParam('i', $id);
$stmt = $this->mysqli->prepare($sql);
if ($this->checkStmt($stmt) && call_user_func_array( array($stmt, 'bind_param'), $this->getParam()) && $stmt->execute())
return true;
return false;
}
/**
@ -229,7 +250,7 @@ class Share {
public function getUpstreamFinder() {
return @$this->oUpstream->account;
}
public function getUpstreamId() {
public function getUpstreamShareId() {
return @$this->oUpstream->id;
}
/**
@ -240,7 +261,7 @@ class Share {
* @param last int Skips all shares up to last to find new share
* @return bool
**/
public function setUpstream($aBlock, $last=0) {
public function findUpstreamShare($aBlock, $last=0) {
// Many use stratum, so we create our stratum check first
$version = pack("I*", sprintf('%08d', $aBlock['version']));
$previousblockhash = pack("H*", swapEndian($aBlock['previousblockhash']));
@ -343,10 +364,12 @@ class Share {
* Fetch the lowest needed share ID from archive
**/
function getMinArchiveShareId($iCount) {
// 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.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
SELECT share_id, @total := @total + IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty) AS total
FROM $this->tableArchive, (SELECT @total := 0) AS a
WHERE our_result = 'Y'
AND @total < ?
@ -356,20 +379,13 @@ class Share {
");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iCount, $iCount) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->share_id;
$this->setErrorMessage("Failed fetching additional shares from archive: " . $this->mysqli->error);
return false;
}
/**
* Helper function
**/
private function checkStmt($bState) {
if ($bState ===! true) {
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error);
$this->setErrorMessage('Internal application Error');
return false;
}
return true;
}
}
$share = new Share($debug, $mysqli, $user, $block, $config);
$share->setMysql($mysqli);
$share->setConfig($config);
$share->setUser($user);
$share->setBlock($block);

View File

@ -65,7 +65,7 @@ class Statistics {
b.*,
a.username AS finder,
a.is_anonymous AS is_anonymous,
ROUND((difficulty * 65535) / POW(2, (" . $this->config['difficulty'] . " -16)), 0) AS estshares
ROUND((difficulty * POW(2, 32 - " . $this->config['target_bits'] . ")) / 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
@ -90,7 +90,7 @@ class Statistics {
b.*,
a.username AS finder,
a.is_anonymous AS is_anonymous,
ROUND((difficulty * 65535) / POW(2, (" . $this->config['difficulty'] . " -16)), 0) AS estshares
ROUND((difficulty * POW(2, 32 - " . $this->config['target_bits'] . ")) / 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
@ -170,11 +170,11 @@ class Statistics {
SELECT
(
(
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0) AS hashrate
FROM " . $this->share->getTableName() . "
WHERE time > DATE_SUB(now(), INTERVAL ? SECOND)
) + (
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0) AS hashrate
FROM " . $this->share->getArchiveTableName() . "
WHERE time > DATE_SUB(now(), INTERVAL ? SECOND)
)
@ -370,14 +370,14 @@ class Statistics {
$stmt = $this->mysqli->prepare("
SELECT
(
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 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 ? SECOND)
AND u.id = ?
) + (
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0) AS hashrate
FROM " . $this->share->getArchiveTableName() . " AS s,
" . $this->user->getTableName() . " AS u
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
@ -392,7 +392,29 @@ class Statistics {
return false;
}
public function getUserUnpaidPPSShares($account_id, $last_paid_pps_id) {
$this->debug->append("STA " . __METHOD__, 4);
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
$stmt = $this->mysqli->prepare("
SELECT
ROUND(IFNULL(SUM(IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS total
FROM " . $this->share->getTableName() . " AS s
JOIN " . $this->user->getTableName() . " AS a
ON a.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND a.id = ?
AND s.id > ?");
if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $account_id, $last_paid_pps_id) && $stmt->execute() && $result = $stmt->get_result() )
return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->total);
$this->debug->append("Failed fetching average share dificulty: " . $this->mysqli->error, 3);
return 0;
}
/**
* Get average share difficulty across all workers for user
* @param account_id int Account ID
* @param interval int Data interval in seconds
* @return double Share difficulty or 0
**/
public function getUserShareDifficulty($account_id, $interval=600) {
$this->debug->append("STA " . __METHOD__, 4);
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
@ -454,7 +476,7 @@ 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(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / 600 / 1000), 0) AS hashrate
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / 600 / 1000), 0) AS hashrate
FROM " . $this->share->getTableName() . " AS s,
" . $this->user->getTableName() . " AS u
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
@ -524,7 +546,7 @@ class Statistics {
a.username AS account,
a.donate_percent AS donate_percent,
a.is_anonymous AS is_anonymous,
IFNULL(ROUND(SUM(t1.difficulty) * 65536 / 600 / 1000, 2), 0) AS hashrate
IFNULL(ROUND(SUM(t1.difficulty) * POW(2, " . $this->config['target_bits'] . ") / 600 / 1000, 2), 0) AS hashrate
FROM
(
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'
@ -553,7 +575,7 @@ class Statistics {
if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
$stmt = $this->mysqli->prepare("
SELECT
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate,
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * POW(2, " . $this->config['target_bits'] . ") / 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
@ -563,7 +585,7 @@ class Statistics {
GROUP BY HOUR(time)
UNION ALL
SELECT
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate,
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * POW(2, " . $this->config['target_bits'] . ") / 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
@ -594,7 +616,7 @@ class Statistics {
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__)) return $data;
$stmt = $this->mysqli->prepare("
SELECT
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate,
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * POW(2, " . $this->config['target_bits'] . ") / 3600 / 1000), 0) AS hashrate,
HOUR(s.time) AS hour
FROM " . $this->share->getTableName() . " AS s
WHERE time < NOW() - INTERVAL 1 HOUR
@ -602,7 +624,7 @@ class Statistics {
GROUP BY HOUR(time)
UNION ALL
SELECT
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate,
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * POW(2, " . $this->config['target_bits'] . ") / 3600 / 1000), 0) AS hashrate,
HOUR(s.time) AS hour
FROM " . $this->share->getArchiveTableName() . " AS s
WHERE time < NOW() - INTERVAL 1 HOUR
@ -645,18 +667,16 @@ class Statistics {
}
} else {
// Hack so we can use this method for PPS estimates too
// value1 = shares/s
// value2 = avg share difficulty
if (@$value1 > 0 && @$value2 > 0) {
// Default: No fees applied so multiply by 1
$fee = 1;
if ($this->config['fees'] > 0)
$bNoFees == 0 ? $fee = round(((float)$this->config['fees'] / 100), 8) : $fee = 1;
$hour = 60 * 60;
$pps = $value1 * $value2 * $ppsvalue;
$hour = 3600;
$aEstimates['hours1'] = $pps * $hour * $fee;
$aEstimates['hours1'] = $pps * $hour;
$aEstimates['hours24'] = $pps * 24 * $hour;
$aEstimates['days7'] = $pps * 24 * 7 * $hour;
$aEstimates['days14'] = $pps * 14 * 24 * 7 * $hour;
$aEstimates['days30'] = $pps * 30 * 24 * 7 * $hour;
$aEstimates['days14'] = $pps * 14 * 24 * $hour;
$aEstimates['days30'] = $pps * 30 * 24 * $hour;
} else {
$aEstimates['hours1'] = 0;
$aEstimates['hours24'] = 0;

View File

@ -5,7 +5,8 @@ if (!defined('SECURITY'))
die('Hacking attempt');
class Transaction extends Base {
private $sError = '', $table = 'transactions';
private $sError = '';
protected $table = 'transactions';
public $num_rows = 0, $insert_id = 0;
/**

View File

@ -176,6 +176,40 @@ class Worker {
return false;
}
/**
* Fetch all workers for admin panel
* @param limit int
* @return mixed array Workers and their settings or false
**/
public function getAllWorkers($iLimit=0) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("
SELECT id, username, password, monitor, difficulty,
(
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->getTableName() . "
WHERE
username = w.username
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
) + (
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
FROM $this->table AS w
ORDER BY hashrate DESC LIMIT ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iLimit) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
// Catchall
$this->setErrorMessage('Failed to fetch workers');
$this->debug->append('Fetching workers failed: ' . $this->mysqli->error);
return false;
}
/**
* Get all currently active workers in the past 10 minutes
* @param none

View File

@ -20,6 +20,17 @@ define('DEBUG', 0);
// SALT used to hash passwords
define('SALT', 'PLEASEMAKEMESOMETHINGRANDOM');
/**
* Underlying coin algorithm that you are mining on. Set this to whatever your coin needs:
*
* Options:
* sha256d : SHA coins like Bitcoin
* scrypt : Scrypt based coins like Litecoin
* Default:
* scrypt : Scrypt is default
**/
$config['algorithm'] = 'scrypt';
/**
* Database configuration
*
@ -308,10 +319,6 @@ $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

@ -0,0 +1,18 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
// Check user to ensure they are admin
if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) {
header("HTTP/1.1 404 Page not found");
die("404 Page not found");
}
$iActiveWorkers = $worker->getCountAllActiveWorkers();
$aWorkers = $worker->getAllWorkers($iActiveWorkers);
$smarty->assign('WORKERS', $aWorkers);
$smarty->assign('CONTENT', 'default.tpl');
?>

View File

@ -0,0 +1,86 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
// Check user to ensure they are admin
if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) {
header("HTTP/1.1 404 Page not found");
die("404 Page not found");
}
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
$debug->append('No cached version available, fetching from backend', 3);
$aUserList = $roundstats->getAllUsers('%');
$iHeight = 0;
$iUserId = 0;
$filter = 0;
$userName = 'None';
if (@$_REQUEST['id']) {
$iUserId = $_REQUEST['id'];
$userName = $user->getUserName($_REQUEST['id']);
}
$setting->getValue('statistics_block_count') ? $iLimit = $setting->getValue('statistics_block_count') : $iLimit = 20;
if (@$_REQUEST['limit']) {
$iLimit = $_REQUEST['limit'];
if ( $iLimit > 1000 )
$iLimit = 1000;
}
if (@$_REQUEST['next'] && !empty($_REQUEST['height'])) {
$iHeight = @$roundstats->getNextBlockForStats($_REQUEST['height'], $iLimit);
if (!$iHeight) {
$aBlock = $block->getLast();
$iHeight = $aBlock['height'];
}
} else if (@$_REQUEST['prev'] && !empty($_REQUEST['height'])) {
$iHeight = $_REQUEST['height'];
} else if (!empty($_REQUEST['height']) && is_numeric($_REQUEST['height'])) {
$iHeight = $_REQUEST['height'];
} else {
$aBlock = $block->getLast();
$iHeight = $aBlock['height'];
}
if (@$_REQUEST['search']) {
$iHeight = $roundstats->searchForBlockHeight($_REQUEST['search']);
}
if (@$_REQUEST['filter']) {
$filter = $_REQUEST['filter'];
}
$aBlocksData = array();
if ( $iUserId ) {
if ($filter) {
$aBlocksData = $roundstats->getAllReportBlocksFoundHeight($iHeight, $iLimit);
} else {
$aBlocksData = $roundstats->getUserReportBlocksFoundHeight($iHeight, $iLimit, $iUserId);
}
foreach($aBlocksData as $key => $aData) {
$aBlocksData[$key]['pplns_shares'] = @$roundstats->getPPLNSRoundShares($aData['height']);
$aBlocksData[$key]['user'] = @$roundstats->getRoundStatsForUser($aData['height'], $iUserId);
$aBlocksData[$key]['user_credit'] = @$roundstats->getUserRoundTransHeight($aData['height'], $iUserId);
}
}
$smarty->assign('REPORTDATA', $aBlocksData);
$smarty->assign("USERLIST", $aUserList);
$smarty->assign("USERNAME", $userName);
$smarty->assign("USERID", $iUserId);
$smarty->assign("BLOCKLIMIT", $iLimit);
$smarty->assign("HEIGHT", $iHeight);
$smarty->assign("FILTER", $filter);
} else {
$debug->append('Using cached page', 3);
}
if ($user->isAuthenticated(false)) {
$smarty->assign("CONTENT", "default.tpl");
} else {
$smarty->assign("CONTENT", "empty");
}
?>

View File

@ -41,7 +41,9 @@ $aRoundShares = $statistics->getRoundShares();
if ($config['payout_system'] != 'pps') {
$aEstimates = $statistics->getUserEstimates($aRoundShares, $aUserRoundShares, $user->getUserDonatePercent($user_id), $user->getUserNoFee($user_id));
$dUnpaidShares = 0;
} else {
$dUnpaidShares = $statistics->getUserUnpaidPPSShares($user_id, $setting->getValue('pps_last_share_id'));
if ($config['pps']['reward']['type'] == 'blockavg' && $block->getBlockCount() > 0) {
$pps_reward = round($block->getAvgBlockReward($config['pps']['blockavg']['blockcount']));
} else {
@ -56,7 +58,7 @@ if ($config['payout_system'] != 'pps') {
}
}
$ppsvalue = round($pps_reward / (pow(2,32) * $dDifficulty) * pow(2, $config['pps_target']), 12);
$ppsvalue = round($pps_reward / (pow(2,32) * $dDifficulty) * pow(2, $config['target_bits']), 12);
$aEstimates = $statistics->getUserEstimates($dPersonalSharerate, $dPersonalShareDifficulty, $user->getUserDonatePercent($user_id), $user->getUserNoFee($user_id), $ppsvalue);
}
@ -89,7 +91,7 @@ $data = array(
'raw' => array( 'personal' => array( 'hashrate' => $dPersonalHashrate ), 'pool' => array( 'hashrate' => $dPoolHashrate ), 'network' => array( 'hashrate' => $dNetworkHashrate / 1000 ) ),
'personal' => array (
'hashrate' => $dPersonalHashrateAdjusted, 'sharerate' => $dPersonalSharerate, 'sharedifficulty' => $dPersonalShareDifficulty,
'shares' => array('valid' => $aUserRoundShares['valid'], 'invalid' => $aUserRoundShares['invalid'], 'invalid_percent' => $dUserInvalidPercent),
'shares' => array('valid' => $aUserRoundShares['valid'], 'invalid' => $aUserRoundShares['invalid'], 'invalid_percent' => $dUserInvalidPercent, 'unpaid' => $dUnpaidShares ),
'balance' => $transaction->getBalance($user_id), 'estimates' => $aEstimates, 'workers' => $aWorkers ),
'pool' => array(
'workers' => $worker->getCountAllActiveWorkers(), 'hashrate' => $dPoolHashrateAdjusted,

View File

@ -140,7 +140,9 @@ if (@$_SESSION['USERDATA']['id']) {
}
}
$aGlobal['ppsvalue'] = number_format(round($pps_reward / (pow(2,32) * $dDifficulty) * pow(2, $config['pps_target']), 12) ,12);
$aGlobal['userdata']['pps']['unpaidshares'] = $statistics->getUserUnpaidPPSShares($_SESSION['USERDATA']['id'], $setting->getValue('pps_last_share_id'));
$aGlobal['ppsvalue'] = number_format(round($pps_reward / (pow(2, $config['target_bits']) * $dDifficulty), 12) ,12);
$aGlobal['poolppsvalue'] = $aGlobal['ppsvalue'] * pow(2, $config['difficulty'] - 16);
$aGlobal['userdata']['sharedifficulty'] = $statistics->getUserShareDifficulty($_SESSION['USERDATA']['id']);
$aGlobal['userdata']['estimates'] = $statistics->getUserEstimates($aGlobal['userdata']['sharerate'], $aGlobal['userdata']['sharedifficulty'], $aGlobal['userdata']['donate_percent'], $aGlobal['userdata']['no_fees'], $aGlobal['ppsvalue']);
break;

View File

@ -0,0 +1,34 @@
{include file="global/block_header.tpl" BLOCK_HEADER="{count($WORKERS)} Current Active Pool Workers"}
<center>
<table border="0" style="font-size:13px;">
<thead>
<tr style="background-color:#B6DAFF;">
<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>
</tr>
</thead>
{nocache}
{section worker $WORKERS}
<tbody>
<tr>
<td>{$WORKERS[worker].username|escape}</td>
<td>{$WORKERS[worker].password|escape}</td>
<td class="center"><img src="{$PATH}/images/{if $WORKERS[worker].hashrate > 0}success{else}error{/if}.gif" /></td>
{if $GLOBAL.config.disable_notifications != 1}
<td class="center">
<img src="{$PATH}/images/{if $WORKERS[worker].monitor}success{else}error{/if}.gif" />
</td>
{/if}
<td class="right">{$WORKERS[worker].hashrate|number_format|default:"0"}</td>
<td class="right">{if $WORKERS[worker].hashrate > 0}{$WORKERS[worker].difficulty|number_format:"2"|default:"0"}{else}0{/if}</td>
</tr>
{/section}
{/nocache}
</tbody>
</table>
</center>
{include file="global/block_footer.tpl"}

View File

@ -0,0 +1,2 @@
{include file="admin/reports/earnings_control.tpl"}
{include file="admin/reports/earnings_report.tpl"}

View File

@ -0,0 +1,50 @@
{include file="global/block_header.tpl" ALIGN="left" BLOCK_STYLE="width: 100%" BLOCK_HEADER="Earnings Information" STYLE="padding-left:5px;padding-right:5px;"}
<form action="{$smarty.server.PHP_SELF}" method="post">
<input type="hidden" name="page" value="{$smarty.request.page|escape}">
<input type="hidden" name="action" value="{$smarty.request.action|escape}">
<table width="100%" border="0" style="font-size:13px;">
<tbody>
<tr>
<td class="left">
<a href="{$smarty.server.PHP_SELF}?page={$smarty.request.page}&action={$smarty.request.action}&height={if is_array($REPORTDATA) && count($REPORTDATA) > ($BLOCKLIMIT - 1)}{$REPORTDATA[$BLOCKLIMIT - 1].height}{/if}&prev=1&limit={$BLOCKLIMIT}&id={$USERID}&filter={$FILTER}"><img src="{$PATH}/images/prev.png" /></a>
</td>
<td class="right">
<a href="{$smarty.server.PHP_SELF}?page={$smarty.request.page}&action={$smarty.request.action}&height={if is_array($REPORTDATA) && count($REPORTDATA) > 0}{$REPORTDATA[0].height}{/if}&next=1&limit={$BLOCKLIMIT}&id={$USERID}&filter={$FILTER}"><img src="{$PATH}/images/next.png" /></a>
</td>
</tr>
</tbody>
</table>
<table width="100%" border="0" style="font-size:13px;">
<thead>
<tr style="background-color:#B6DAFF;">
<th class="center">Select User</th>
<th class="center">Block Limit</th>
<th class="center">Starting Block Height</th>
<th class="center">Show Empty Rounds</th>
</tr>
</thead>
<tbody>
<tr>
<td class="center">
{html_options name="id" options=$USERLIST selected=$USERID|default:"0"}
</td>
<td class="center">
<input size="12" type="text" name="limit" value="{$BLOCKLIMIT|default:"20"}" />
</td>
<td class="center">
<input size="12" type="text" name="search" value="{$HEIGHT|default:"%"}">
</td>
<td class="center">
<input type="checkbox" name="filter" value="1" id="filter" {if $FILTER}checked{/if} />
<label for="filter"></label>
</td>
</tr>
<tr>
<td class="right" colspan="4">
<input type="submit" class="submit small" value="Submit">
</td>
</tr>
</tbody>
</table>
</form>
{include file="global/block_footer.tpl"}

View File

@ -0,0 +1,85 @@
{include file="global/block_header.tpl" ALIGN="left" BLOCK_STYLE="width: 100%" BLOCK_HEADER="Earnings Report Last {$BLOCKLIMIT} Blocks For User: {$USERNAME}" STYLE="padding-left:5px;padding-right:5px;"}
<table class="left" width="100%" class="sortable" style="font-size:12px;">
<thead>
<tr style="background-color:#B6DAFF;">
<th >Block</th>
<th class="right">Round Shares</th>
<th class="right">Round Valid</th>
<th class="right">Invalid</th>
<th class="right">Invalid %</th>
<th class="right">Round %</th>
<th class="right">PPLNS Shares</th>
<th class="right">PPLNS Valid</th>
<th class="right">Invalid</th>
<th class="right">Invalid %</th>
<th class="right">PPLNS %</th>
<th class="right">Variance</th>
<th class="right">Amount</th>
</tr>
</thead>
<tbody>
{assign var=percentage value=0}
{assign var=percentage1 value=0}
{assign var=percentage2 value=0}
{assign var=totalvalid value=0}
{assign var=totalinvalid value=0}
{assign var=totalshares value=0}
{assign var=usertotalshares value=0}
{assign var=totalpercentage value=0}
{assign var=pplnsshares value=0}
{assign var=userpplnsshares value=0}
{assign var=pplnsvalid value=0}
{assign var=pplnsinvalid value=0}
{assign var=amount value=0}
{section txs $REPORTDATA}
{assign var="totalshares" value=$totalshares+$REPORTDATA[txs].shares}
{assign var=totalvalid value=$totalvalid+$REPORTDATA[txs]['user'].valid}
{assign var=totalinvalid value=$totalinvalid+$REPORTDATA[txs]['user'].invalid}
{assign var="pplnsshares" value=$pplnsshares+$REPORTDATA[txs].pplns_shares}
{assign var=pplnsvalid value=$pplnsvalid+$REPORTDATA[txs]['user'].pplns_valid}
{assign var=pplnsinvalid value=$pplnsinvalid+$REPORTDATA[txs]['user'].pplns_invalid}
{assign var=amount value=$amount+$REPORTDATA[txs].user_credit}
{if $REPORTDATA[txs]['user'].pplns_valid > 0}
{assign var="userpplnsshares" value=$userpplnsshares+$REPORTDATA[txs].pplns_shares}
{/if}
{if $REPORTDATA[txs]['user'].valid > 0}
{assign var="usertotalshares" value=$usertotalshares+$REPORTDATA[txs].shares}
{/if}
<tr>
<td><a href="{$smarty.server.PHP_SELF}?page=statistics&action=round&height={$REPORTDATA[txs].height}">{$REPORTDATA[txs].height|default:"0"}</a></td>
<td class="right">{$REPORTDATA[txs].shares|default:"0"}</td>
<td class="right">{$REPORTDATA[txs]['user'].valid|number_format|default:"0"}</td>
<td class="right">{$REPORTDATA[txs]['user'].invalid|number_format|default:"0"}</td>
<td class="right">{if $REPORTDATA[txs]['user'].invalid > 0 }{($REPORTDATA[txs]['user'].invalid / $REPORTDATA[txs]['user'].valid * 100)|number_format:"2"|default:"0"}{else}0.00{/if}</td>
<td class="right">{if $REPORTDATA[txs]['user'].valid > 0 }{(( 100 / $REPORTDATA[txs].shares) * $REPORTDATA[txs]['user'].valid)|number_format:"2"}{else}0.00{/if}</td>
<td class="right">{$REPORTDATA[txs].pplns_shares|number_format|default:"0"}</td>
<td class="right">{$REPORTDATA[txs]['user'].pplns_valid|number_format|default:"0"}</td>
<td class="right">{$REPORTDATA[txs]['user'].pplns_invalid|number_format|default:"0"}</td>
<td class="right">{if $REPORTDATA[txs]['user'].pplns_invalid > 0 && $REPORTDATA[txs]['user'].pplns_valid > 0 }{($REPORTDATA[txs]['user'].pplns_invalid / $REPORTDATA[txs]['user'].pplns_valid * 100)|number_format:"2"|default:"0"}{else}0.00{/if}</td>
<td class="right">{if $REPORTDATA[txs].shares > 0 && $REPORTDATA[txs]['user'].pplns_valid > 0}{(( 100 / $REPORTDATA[txs].pplns_shares) * $REPORTDATA[txs]['user'].pplns_valid)|number_format:"2"|default:"0"}{else}0.00{/if}</td>
<td class="right">{if $REPORTDATA[txs]['user'].valid > 0 && $REPORTDATA[txs]['user'].pplns_valid > 0}{math assign="percentage1" equation=(100 / ((( 100 / $REPORTDATA[txs].shares) * $REPORTDATA[txs]['user'].valid) / (( 100 / $REPORTDATA[txs].pplns_shares) * $REPORTDATA[txs]['user'].pplns_valid)))}{else if $REPORTDATA[txs]['user'].pplns_valid == 0}{assign var=percentage1 value=0}{else}{assign var=percentage1 value=100}{/if}
<font color="{if ($percentage1 >= 100)}green{else}red{/if}">{$percentage1|number_format:"2"|default:"0"}</font></b></td>
<td class="right">{$REPORTDATA[txs].user_credit|default:"0"|number_format:"8"}</td>
{assign var=percentage1 value=0}
</tr>
{/section}
<tr>
<td><b>Totals</b></td>
<td class="right">{$totalshares|number_format}</td>
<td class="right">{$totalvalid|number_format}</td>
<td class="right">{$totalinvalid|number_format}</td>
<td class="right">{if $totalinvalid > 0 && $totalvalid > 0 }{($totalinvalid / $totalvalid * 100)|number_format:"2"|default:"0"}{else}0.00{/if}</td>
<td class="right">{if $usertotalshares > 0 && $totalvalid > 0}{(( 100 / $usertotalshares) * $totalvalid)|number_format:"2"|default:"0"}{else}0.00{/if}</td>
<td class="right">{$pplnsshares|number_format}</td>
<td class="right">{$pplnsvalid|number_format}</td>
<td class="right">{$pplnsinvalid|number_format}</td>
<td class="right">{if $pplnsinvalid > 0 && $pplnsvalid > 0 }{($pplnsinvalid / $pplnsvalid * 100)|number_format:"2"|default:"0"}{else}0.00{/if}</td>
<td class="right">{if $userpplnsshares > 0 && $pplnsvalid > 0}{(( 100 / $userpplnsshares) * $pplnsvalid)|number_format:"2"|default:"0"}{else}0.00{/if}</td>
<td class="right">{if $totalvalid > 0 && $pplnsvalid > 0}{math assign="percentage2" equation=(100 / ((( 100 / $usertotalshares) * $totalvalid) / (( 100 / $userpplnsshares) * $pplnsvalid)))}{else if $pplnsvalid == 0}{assign var=percentage2 value=0}{else}{assign var=percentage2 value=100}{/if}
<font color="{if ($percentage2 >= 100)}green{else}red{/if}">{$percentage2|number_format:"2"|default:"0"}</font></b></td>
<td class="right">{$amount|default:"0"|number_format:"8"}</td>
{assign var=percentage2 value=0}
</tr>
</tbody>
</table>
{include file="global/block_footer.tpl"}

View File

@ -21,6 +21,8 @@
<li><a href="{$smarty.server.PHP_SELF}?page=admin&action=transactions">Transactions</a></li>
<li><a href="{$smarty.server.PHP_SELF}?page=admin&action=settings">Settings</a></li>
<li><a href="{$smarty.server.PHP_SELF}?page=admin&action=news">News</a></li>
<li><a href="{$smarty.server.PHP_SELF}?page=admin&action=reports">Reports</a></li>
<li><a href="{$smarty.server.PHP_SELF}?page=admin&action=poolworkers">Pool Workers</a></li>
</ul>
</li>
{/if}

View File

@ -1,5 +1,5 @@
<article class="module width_full">
<header><h3>ThePool Collective</h3></header>
<header><h3>{$GLOBAL.website.name}</h3></header>
<div class="module_content">
<table width="100%">
<tbody>

View File

@ -0,0 +1,31 @@
<article class="module width_3_quarter">
<header><h3>{count($WORKERS)} Current Active Pool Workers</h3></header>
<table class="tablesorter" cellspacing="0">
<thead>
<tr>
<th align="left">Worker Name</th>
<th align="left">Password</th>
<th align="center">Active</th>
{if $GLOBAL.config.disable_notifications != 1}<th align="center">Monitor</th>{/if}
<th align="right">Khash/s</th>
<th align="right" style="padding-right: 25px;">Difficulty</th>
</tr>
</thead>
{nocache}
{section worker $WORKERS}
<tbody>
<tr>
<td align="left">{$WORKERS[worker].username|escape}</td>
<td align="left">{$WORKERS[worker].password|escape}</td>
<td align="center"><i class="icon-{if $WORKERS[worker].hashrate > 0}ok{else}cancel{/if}"></i></td>
{if $GLOBAL.config.disable_notifications != 1}
<td align="center"><i class="icon-{if $WORKERS[worker].monitor}ok{else}cancel{/if}"></i></td>
{/if}
<td align="right">{$WORKERS[worker].hashrate|number_format|default:"0"}</td>
<td align="right" style="padding-right: 25px;">{if $WORKERS[worker].hashrate > 0}{$WORKERS[worker].difficulty|number_format:"2"|default:"0"}{else}0{/if}</td>
</tr>
{/section}
{/nocache}
</tbody>
</table>
</article>

View File

@ -0,0 +1,2 @@
{include file="admin/reports/earnings_control.tpl"}
{include file="admin/reports/earnings_report.tpl"}

View File

@ -0,0 +1,55 @@
<form action="{$smarty.server.PHP_SELF}" method="post">
<input type="hidden" name="page" value="{$smarty.request.page|escape}">
<input type="hidden" name="action" value="{$smarty.request.action|escape}">
<article class="module width_full">
<header><h3>Earnings Information</h3></header>
<table class="tablesorter">
<tbody>
<td align="left">
<a href="{$smarty.server.PHP_SELF}?page={$smarty.request.page}&action={$smarty.request.action}&height={if is_array($REPORTDATA) && count($REPORTDATA) > ($BLOCKLIMIT - 1)}{$REPORTDATA[$BLOCKLIMIT - 1].height}{/if}&prev=1&limit={$BLOCKLIMIT}&id={$USERID}&filter={$FILTER}"<i class="icon-left-open"></i></a>
</td>
<td align="right">
<a href="{$smarty.server.PHP_SELF}?page={$smarty.request.page}&action={$smarty.request.action}&height={if is_array($REPORTDATA) && count($REPORTDATA) > 0}{$REPORTDATA[0].height}{/if}&next=1&limit={$BLOCKLIMIT}&id={$USERID}&filter={$FILTER}"><i class="icon-right-open"></i></a>
</td>
</tr>
</tbody>
</table>
<table class="tablesorter">
<tbody>
<tr>
<td>
<fieldset style="width:200px; padding-right:8px;">
<label>Select User</label>
{html_options name="id" options=$USERLIST selected=$USERID|default:"0"}
</fieldset>
</td>
<td>
<fieldset style="width:200px; padding-right:8px;">
<label>Block Limit</label>
<input size="10" type="text" name="limit" value="{$BLOCKLIMIT|default:"20"}" />
</fieldset>
</td>
<td>
<fieldset style="width:200px; padding-right:8px;">
<label>Starting block height</label>
<input type="text" class="pin" name="search" value="{$HEIGHT|default:"%"}">
</fieldset>
</td>
<td align="center"><b>SHOW EMPTY ROUNDS</b><br><br>
<span class="toggle">
<label for="Show empty rounds">
<input type="checkbox" class="ios-switch" name="filter" value="1" id="filter" {if $FILTER}checked{/if} />
<div class="switch"></div>
</label>
</span>
</td>
</tbody>
</table>
<footer>
<div class="submit_link">
<input type="submit" value="Submit" class="alt_btn">
</div>
</footer>
</article>
</form>

View File

@ -0,0 +1,88 @@
<article class="module width_full">
<header><h3>Earnings Report Last {$BLOCKLIMIT} Blocks For User: {$USERNAME}</h3></header>
<table class="tablesorter" cellspacing="0">
<thead>
<tr>
<th >Block</th>
<th align="right">Round Shares</th>
<th align="right">Round Valid</th>
<th align="right">Invalid</th>
<th align="right">Invalid %</th>
<th align="right">Round %</th>
<th align="right">PPLNS Shares</th>
<th align="right">PPLNS Valid</th>
<th align="right">Invalid</th>
<th align="right">Invalid %</th>
<th align="right">PPLNS %</th>
<th align="right">Variance</th>
<th align="right" style="padding-right: 25px;">Amount</th>
</tr>
</thead>
<tbody>
{assign var=percentage value=0}
{assign var=percentage1 value=0}
{assign var=percentage2 value=0}
{assign var=totalvalid value=0}
{assign var=totalinvalid value=0}
{assign var=totalshares value=0}
{assign var=usertotalshares value=0}
{assign var=totalpercentage value=0}
{assign var=pplnsshares value=0}
{assign var=userpplnsshares value=0}
{assign var=pplnsvalid value=0}
{assign var=pplnsinvalid value=0}
{assign var=amount value=0}
{section txs $REPORTDATA}
{assign var="totalshares" value=$totalshares+$REPORTDATA[txs].shares}
{assign var=totalvalid value=$totalvalid+$REPORTDATA[txs]['user'].valid}
{assign var=totalinvalid value=$totalinvalid+$REPORTDATA[txs]['user'].invalid}
{assign var="pplnsshares" value=$pplnsshares+$REPORTDATA[txs].pplns_shares}
{assign var=pplnsvalid value=$pplnsvalid+$REPORTDATA[txs]['user'].pplns_valid}
{assign var=pplnsinvalid value=$pplnsinvalid+$REPORTDATA[txs]['user'].pplns_invalid}
{assign var=amount value=$amount+$REPORTDATA[txs].user_credit}
{if $REPORTDATA[txs]['user'].pplns_valid > 0}
{assign var="userpplnsshares" value=$userpplnsshares+$REPORTDATA[txs].pplns_shares}
{/if}
{if $REPORTDATA[txs]['user'].valid > 0}
{assign var="usertotalshares" value=$usertotalshares+$REPORTDATA[txs].shares}
{/if}
<tr>
<td><a href="{$smarty.server.PHP_SELF}?page=statistics&action=round&height={$REPORTDATA[txs].height}">{$REPORTDATA[txs].height|default:"0"}</a></td>
<td align="right">{$REPORTDATA[txs].shares|default:"0"}</td>
<td align="right">{$REPORTDATA[txs]['user'].valid|number_format|default:"0"}</td>
<td align="right">{$REPORTDATA[txs]['user'].invalid|number_format|default:"0"}</td>
<td align="right">{if $REPORTDATA[txs]['user'].invalid > 0 }{($REPORTDATA[txs]['user'].invalid / $REPORTDATA[txs]['user'].valid * 100)|number_format:"2"|default:"0"}{else}0.00{/if}</td>
<td align="right">{if $REPORTDATA[txs]['user'].valid > 0 }{(( 100 / $REPORTDATA[txs].shares) * $REPORTDATA[txs]['user'].valid)|number_format:"2"}{else}0.00{/if}</td>
<td align="right">{$REPORTDATA[txs].pplns_shares|number_format|default:"0"}</td>
<td align="right">{$REPORTDATA[txs]['user'].pplns_valid|number_format|default:"0"}</td>
<td align="right">{$REPORTDATA[txs]['user'].pplns_invalid|number_format|default:"0"}</td>
<td align="right">{if $REPORTDATA[txs]['user'].pplns_invalid > 0 && $REPORTDATA[txs]['user'].pplns_valid > 0 }{($REPORTDATA[txs]['user'].pplns_invalid / $REPORTDATA[txs]['user'].pplns_valid * 100)|number_format:"2"|default:"0"}{else}0.00{/if}</td>
<td align="right">{if $REPORTDATA[txs].shares > 0 && $REPORTDATA[txs]['user'].pplns_valid > 0}{(( 100 / $REPORTDATA[txs].pplns_shares) * $REPORTDATA[txs]['user'].pplns_valid)|number_format:"2"|default:"0"}{else}0.00{/if}</td>
<td align="right">{if $REPORTDATA[txs]['user'].valid > 0 && $REPORTDATA[txs]['user'].pplns_valid > 0}{math assign="percentage1" equation=(100 / ((( 100 / $REPORTDATA[txs].shares) * $REPORTDATA[txs]['user'].valid) / (( 100 / $REPORTDATA[txs].pplns_shares) * $REPORTDATA[txs]['user'].pplns_valid)))}{else if $REPORTDATA[txs]['user'].pplns_valid == 0}{assign var=percentage1 value=0}{else}{assign var=percentage1 value=100}{/if}
<font color="{if ($percentage1 >= 100)}green{else}red{/if}">{$percentage1|number_format:"2"|default:"0"}</font></b></td>
<td align="right" style="padding-right: 25px;">{$REPORTDATA[txs].user_credit|default:"0"|number_format:"8"}</td>
{assign var=percentage1 value=0}
</tr>
{/section}
<tr>
<td><b>Totals</b></td>
<td align="right">{$totalshares|number_format}</td>
<td align="right">{$totalvalid|number_format}</td>
<td align="right">{$totalinvalid|number_format}</td>
<td align="right">{if $totalinvalid > 0 && $totalvalid > 0 }{($totalinvalid / $totalvalid * 100)|number_format:"2"|default:"0"}{else}0.00{/if}</td>
<td align="right">{if $usertotalshares > 0 && $totalvalid > 0}{(( 100 / $usertotalshares) * $totalvalid)|number_format:"2"|default:"0"}{else}0.00{/if}</td>
<td align="right">{$pplnsshares|number_format}</td>
<td align="right">{$pplnsvalid|number_format}</td>
<td align="right">{$pplnsinvalid|number_format}</td>
<td align="right">{if $pplnsinvalid > 0 && $pplnsvalid > 0 }{($pplnsinvalid / $pplnsvalid * 100)|number_format:"2"|default:"0"}{else}0.00{/if}</td>
<td align="right">{if $userpplnsshares > 0 && $pplnsvalid > 0}{(( 100 / $userpplnsshares) * $pplnsvalid)|number_format:"2"|default:"0"}{else}0.00{/if}</td>
<td align="right">{if $totalvalid > 0 && $pplnsvalid > 0}{math assign="percentage2" equation=(100 / ((( 100 / $usertotalshares) * $totalvalid) / (( 100 / $userpplnsshares) * $pplnsvalid)))}{else if $pplnsvalid == 0}{assign var=percentage2 value=0}{else}{assign var=percentage2 value=100}{/if}
<font color="{if ($percentage2 >= 100)}green{else}red{/if}">{$percentage2|number_format:"2"|default:"0"}</font></b></td>
<td align="right" style="padding-right: 25px;">{$amount|default:"0"|number_format:"8"}</td>
{assign var=percentage2 value=0}
</tr>
</tbody>
</table>
<footer>
</footer>
</article>

View File

@ -157,6 +157,7 @@ $(document).ready(function(){
$('#b-fee').html((parseFloat(data.getdashboarddata.data.personal.estimates.fee).toFixed(4)));
$('#b-donation').html((parseFloat(data.getdashboarddata.data.personal.estimates.donation).toFixed(4)));
{/literal}{else}{literal}
$('#b-ppsunpaid').html((parseFloat(data.getdashboarddata.data.personal.shares.unpaid).toFixed(0)));
$('#b-ppsdiff').html((parseFloat(data.getdashboarddata.data.personal.sharedifficulty).toFixed(2)));
$('#b-est1').html((parseFloat(data.getdashboarddata.data.personal.estimates.hours1).toFixed(8)));
$('#b-est24hours').html((parseFloat(data.getdashboarddata.data.personal.estimates.hours24).toFixed(8)));

View File

@ -10,8 +10,16 @@
</tr>
{elseif $GLOBAL.config.payout_system == 'pps'}
<tr>
<td><b>PPS Value</b></td>
<td>{$GLOBAL.ppsvalue}</td>
<td><b>Unpaid Shares</b></td>
<td id="b-ppsunpaid">{$GLOBAL.userdata.pps.unpaidshares}</td>
</tr>
<tr>
<td><b>Baseline PPS Rate</b></td>
<td>{$GLOBAL.ppsvalue} {$GLOBAL.config.currency}</td>
</tr>
<tr>
<td><b>Pools PPS Rate</b></td>
<td>{$GLOBAL.poolppsvalue} {$GLOBAL.config.currency}</td>
</tr>
<tr>
<td><b>PPS Difficulty</b></td>

View File

@ -22,6 +22,8 @@
<li class="icon-exchange"><a href="{$smarty.server.PHP_SELF}?page=admin&action=transactions">Transactions</a></li>
<li class="icon-cog"><a href="{$smarty.server.PHP_SELF}?page=admin&action=settings">Settings</a></li>
<li class="icon-doc"><a href="{$smarty.server.PHP_SELF}?page=admin&action=news">News</a></li>
<li class="icon-chart"><a href="{$smarty.server.PHP_SELF}?page=admin&action=reports">Reports</a></li>
<li class="icon-photo"><a href="{$smarty.server.PHP_SELF}?page=admin&action=poolworkers">Pool Workers</a></li>
</ul>
{/if}
{if $smarty.session.AUTHENTICATED|default}

View File

@ -103,14 +103,13 @@
<td align="right">{$BLOCKSFOUND[block].difficulty|number_format:"2"}</td>
<td align="right">{$BLOCKSFOUND[block].amount|number_format:"2"}</td>
<td align="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}
{assign var="totalexpectedshares" value=$totalexpectedshares+$BLOCKSFOUND[block].estshares}
{$BLOCKSFOUND[block].estshares|number_format}
</td>
{if $GLOBAL.config.payout_system == 'pplns'}<td align="right">{$BLOCKSFOUND[block].pplns_shares|number_format}</td>{/if}
<td align="right">{$BLOCKSFOUND[block].shares|number_format}</td>
<td align="right" style="padding-right: 25px;">
{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>