diff --git a/cronjobs/findblock.php b/cronjobs/findblock.php
index 114d117b..50d03a61 100755
--- a/cronjobs/findblock.php
+++ b/cronjobs/findblock.php
@@ -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');
diff --git a/cronjobs/pplns_payout.php b/cronjobs/pplns_payout.php
index 15e8d6b4..1fe17879 100755
--- a/cronjobs/pplns_payout.php
+++ b/cronjobs/pplns_payout.php
@@ -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");
diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php
index 7c3515fb..ca0a8c3c 100755
--- a/cronjobs/pps_payout.php
+++ b/cronjobs/pps_payout.php
@@ -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" .
diff --git a/public/include/autoloader.inc.php b/public/include/autoloader.inc.php
index 8950f103..b393387e 100644
--- a/public/include/autoloader.inc.php
+++ b/public/include/autoloader.inc.php
@@ -1,5 +1,11 @@
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
diff --git a/public/include/classes/block.class.php b/public/include/classes/block.class.php
index 027d06c5..bdb30cb1 100644
--- a/public/include/classes/block.class.php
+++ b/public/include/classes/block.class.php
@@ -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
diff --git a/public/include/classes/roundstats.class.php b/public/include/classes/roundstats.class.php
index 45dbb7ea..efe2e161 100644
--- a/public/include/classes/roundstats.class.php
+++ b/public/include/classes/roundstats.class.php
@@ -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);
diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php
index 705c41e1..7b31e1a0 100644
--- a/public/include/classes/share.class.php
+++ b/public/include/classes/share.class.php
@@ -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);
diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php
index 38f5360a..650fa288 100644
--- a/public/include/classes/statistics.class.php
+++ b/public/include/classes/statistics.class.php
@@ -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;
diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php
index 557ce3e3..6d7beb4b 100644
--- a/public/include/classes/transaction.class.php
+++ b/public/include/classes/transaction.class.php
@@ -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;
/**
diff --git a/public/include/classes/worker.class.php b/public/include/classes/worker.class.php
index 42af02de..2960bfe7 100644
--- a/public/include/classes/worker.class.php
+++ b/public/include/classes/worker.class.php
@@ -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
diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php
index b9cfda0d..1609b8b8 100644
--- a/public/include/config/global.inc.dist.php
+++ b/public/include/config/global.inc.dist.php
@@ -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
*
diff --git a/public/include/pages/admin/poolworkers.inc.php b/public/include/pages/admin/poolworkers.inc.php
new file mode 100644
index 00000000..54ea4e3c
--- /dev/null
+++ b/public/include/pages/admin/poolworkers.inc.php
@@ -0,0 +1,18 @@
+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');
+
+?>
diff --git a/public/include/pages/admin/reports.inc.php b/public/include/pages/admin/reports.inc.php
new file mode 100644
index 00000000..1ce870f4
--- /dev/null
+++ b/public/include/pages/admin/reports.inc.php
@@ -0,0 +1,86 @@
+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");
+}
+?>
diff --git a/public/include/pages/api/getdashboarddata.inc.php b/public/include/pages/api/getdashboarddata.inc.php
index 4d15763d..f4e48fbd 100644
--- a/public/include/pages/api/getdashboarddata.inc.php
+++ b/public/include/pages/api/getdashboarddata.inc.php
@@ -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,
diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php
index 9bb4ac60..5f2c691e 100644
--- a/public/include/smarty_globals.inc.php
+++ b/public/include/smarty_globals.inc.php
@@ -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;
diff --git a/public/templates/mmcFE/admin/poolworkers/default.tpl b/public/templates/mmcFE/admin/poolworkers/default.tpl
new file mode 100644
index 00000000..f7afe308
--- /dev/null
+++ b/public/templates/mmcFE/admin/poolworkers/default.tpl
@@ -0,0 +1,34 @@
+{include file="global/block_header.tpl" BLOCK_HEADER="{count($WORKERS)} Current Active Pool Workers"}
+
+
+
+
+ | Worker Name |
+ Password |
+ Active |
+ {if $GLOBAL.config.disable_notifications != 1}Monitor | {/if}
+ Khash/s |
+ Difficulty |
+
+
+ {nocache}
+ {section worker $WORKERS}
+
+
+ | {$WORKERS[worker].username|escape} |
+ {$WORKERS[worker].password|escape} |
+  |
+ {if $GLOBAL.config.disable_notifications != 1}
+
+
+ |
+ {/if}
+ {$WORKERS[worker].hashrate|number_format|default:"0"} |
+ {if $WORKERS[worker].hashrate > 0}{$WORKERS[worker].difficulty|number_format:"2"|default:"0"}{else}0{/if} |
+
+ {/section}
+ {/nocache}
+
+
+
+{include file="global/block_footer.tpl"}
diff --git a/public/templates/mmcFE/admin/reports/default.tpl b/public/templates/mmcFE/admin/reports/default.tpl
new file mode 100644
index 00000000..53ca1869
--- /dev/null
+++ b/public/templates/mmcFE/admin/reports/default.tpl
@@ -0,0 +1,2 @@
+{include file="admin/reports/earnings_control.tpl"}
+{include file="admin/reports/earnings_report.tpl"}
diff --git a/public/templates/mmcFE/admin/reports/earnings_control.tpl b/public/templates/mmcFE/admin/reports/earnings_control.tpl
new file mode 100644
index 00000000..d6c7d0a5
--- /dev/null
+++ b/public/templates/mmcFE/admin/reports/earnings_control.tpl
@@ -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;"}
+
+{include file="global/block_footer.tpl"}
diff --git a/public/templates/mmcFE/admin/reports/earnings_report.tpl b/public/templates/mmcFE/admin/reports/earnings_report.tpl
new file mode 100644
index 00000000..a724eb13
--- /dev/null
+++ b/public/templates/mmcFE/admin/reports/earnings_report.tpl
@@ -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;"}
+
+
+
+ | Block |
+ Round Shares |
+ Round Valid |
+ Invalid |
+ Invalid % |
+ Round % |
+ PPLNS Shares |
+ PPLNS Valid |
+ Invalid |
+ Invalid % |
+ PPLNS % |
+ Variance |
+ Amount |
+
+
+
+{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}
+
+ | {$REPORTDATA[txs].height|default:"0"} |
+ {$REPORTDATA[txs].shares|default:"0"} |
+ {$REPORTDATA[txs]['user'].valid|number_format|default:"0"} |
+ {$REPORTDATA[txs]['user'].invalid|number_format|default:"0"} |
+ {if $REPORTDATA[txs]['user'].invalid > 0 }{($REPORTDATA[txs]['user'].invalid / $REPORTDATA[txs]['user'].valid * 100)|number_format:"2"|default:"0"}{else}0.00{/if} |
+ {if $REPORTDATA[txs]['user'].valid > 0 }{(( 100 / $REPORTDATA[txs].shares) * $REPORTDATA[txs]['user'].valid)|number_format:"2"}{else}0.00{/if} |
+ {$REPORTDATA[txs].pplns_shares|number_format|default:"0"} |
+ {$REPORTDATA[txs]['user'].pplns_valid|number_format|default:"0"} |
+ {$REPORTDATA[txs]['user'].pplns_invalid|number_format|default:"0"} |
+ {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} |
+ {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} |
+ {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}
+ {$percentage1|number_format:"2"|default:"0"} |
+ {$REPORTDATA[txs].user_credit|default:"0"|number_format:"8"} |
+ {assign var=percentage1 value=0}
+
+{/section}
+
+ | Totals |
+ {$totalshares|number_format} |
+ {$totalvalid|number_format} |
+ {$totalinvalid|number_format} |
+ {if $totalinvalid > 0 && $totalvalid > 0 }{($totalinvalid / $totalvalid * 100)|number_format:"2"|default:"0"}{else}0.00{/if} |
+ {if $usertotalshares > 0 && $totalvalid > 0}{(( 100 / $usertotalshares) * $totalvalid)|number_format:"2"|default:"0"}{else}0.00{/if} |
+ {$pplnsshares|number_format} |
+ {$pplnsvalid|number_format} |
+ {$pplnsinvalid|number_format} |
+ {if $pplnsinvalid > 0 && $pplnsvalid > 0 }{($pplnsinvalid / $pplnsvalid * 100)|number_format:"2"|default:"0"}{else}0.00{/if} |
+ {if $userpplnsshares > 0 && $pplnsvalid > 0}{(( 100 / $userpplnsshares) * $pplnsvalid)|number_format:"2"|default:"0"}{else}0.00{/if} |
+ {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}
+ {$percentage2|number_format:"2"|default:"0"} |
+ {$amount|default:"0"|number_format:"8"} |
+ {assign var=percentage2 value=0}
+
+
+
+{include file="global/block_footer.tpl"}
diff --git a/public/templates/mmcFE/global/navigation.tpl b/public/templates/mmcFE/global/navigation.tpl
index c7f762e9..8d9e2fa6 100644
--- a/public/templates/mmcFE/global/navigation.tpl
+++ b/public/templates/mmcFE/global/navigation.tpl
@@ -21,6 +21,8 @@
Transactions
Settings
News
+ Reports
+ Pool Workers
{/if}
diff --git a/public/templates/mpos/about/pool/default.tpl b/public/templates/mpos/about/pool/default.tpl
index 25ff710c..030f2a74 100644
--- a/public/templates/mpos/about/pool/default.tpl
+++ b/public/templates/mpos/about/pool/default.tpl
@@ -1,5 +1,5 @@
-
+
diff --git a/public/templates/mpos/admin/poolworkers/default.tpl b/public/templates/mpos/admin/poolworkers/default.tpl
new file mode 100644
index 00000000..5d721421
--- /dev/null
+++ b/public/templates/mpos/admin/poolworkers/default.tpl
@@ -0,0 +1,31 @@
+
+ {count($WORKERS)} Current Active Pool Workers
+
+
+
+ | Worker Name |
+ Password |
+ Active |
+ {if $GLOBAL.config.disable_notifications != 1}Monitor | {/if}
+ Khash/s |
+ Difficulty |
+
+
+ {nocache}
+ {section worker $WORKERS}
+
+
+ | {$WORKERS[worker].username|escape} |
+ {$WORKERS[worker].password|escape} |
+ |
+ {if $GLOBAL.config.disable_notifications != 1}
+ |
+ {/if}
+ {$WORKERS[worker].hashrate|number_format|default:"0"} |
+ {if $WORKERS[worker].hashrate > 0}{$WORKERS[worker].difficulty|number_format:"2"|default:"0"}{else}0{/if} |
+
+ {/section}
+ {/nocache}
+
+
+
diff --git a/public/templates/mpos/admin/reports/default.tpl b/public/templates/mpos/admin/reports/default.tpl
new file mode 100644
index 00000000..53ca1869
--- /dev/null
+++ b/public/templates/mpos/admin/reports/default.tpl
@@ -0,0 +1,2 @@
+{include file="admin/reports/earnings_control.tpl"}
+{include file="admin/reports/earnings_report.tpl"}
diff --git a/public/templates/mpos/admin/reports/earnings_control.tpl b/public/templates/mpos/admin/reports/earnings_control.tpl
new file mode 100644
index 00000000..bbdf7321
--- /dev/null
+++ b/public/templates/mpos/admin/reports/earnings_control.tpl
@@ -0,0 +1,55 @@
+
diff --git a/public/templates/mpos/admin/reports/earnings_report.tpl b/public/templates/mpos/admin/reports/earnings_report.tpl
new file mode 100644
index 00000000..292642bf
--- /dev/null
+++ b/public/templates/mpos/admin/reports/earnings_report.tpl
@@ -0,0 +1,88 @@
+
+ Earnings Report Last {$BLOCKLIMIT} Blocks For User: {$USERNAME}
+
+
+
+ | Block |
+ Round Shares |
+ Round Valid |
+ Invalid |
+ Invalid % |
+ Round % |
+ PPLNS Shares |
+ PPLNS Valid |
+ Invalid |
+ Invalid % |
+ PPLNS % |
+ Variance |
+ Amount |
+
+
+
+{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}
+
+ | {$REPORTDATA[txs].height|default:"0"} |
+ {$REPORTDATA[txs].shares|default:"0"} |
+ {$REPORTDATA[txs]['user'].valid|number_format|default:"0"} |
+ {$REPORTDATA[txs]['user'].invalid|number_format|default:"0"} |
+ {if $REPORTDATA[txs]['user'].invalid > 0 }{($REPORTDATA[txs]['user'].invalid / $REPORTDATA[txs]['user'].valid * 100)|number_format:"2"|default:"0"}{else}0.00{/if} |
+ {if $REPORTDATA[txs]['user'].valid > 0 }{(( 100 / $REPORTDATA[txs].shares) * $REPORTDATA[txs]['user'].valid)|number_format:"2"}{else}0.00{/if} |
+ {$REPORTDATA[txs].pplns_shares|number_format|default:"0"} |
+ {$REPORTDATA[txs]['user'].pplns_valid|number_format|default:"0"} |
+ {$REPORTDATA[txs]['user'].pplns_invalid|number_format|default:"0"} |
+ {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} |
+ {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} |
+ {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}
+ {$percentage1|number_format:"2"|default:"0"} |
+ {$REPORTDATA[txs].user_credit|default:"0"|number_format:"8"} |
+ {assign var=percentage1 value=0}
+
+{/section}
+
+ | Totals |
+ {$totalshares|number_format} |
+ {$totalvalid|number_format} |
+ {$totalinvalid|number_format} |
+ {if $totalinvalid > 0 && $totalvalid > 0 }{($totalinvalid / $totalvalid * 100)|number_format:"2"|default:"0"}{else}0.00{/if} |
+ {if $usertotalshares > 0 && $totalvalid > 0}{(( 100 / $usertotalshares) * $totalvalid)|number_format:"2"|default:"0"}{else}0.00{/if} |
+ {$pplnsshares|number_format} |
+ {$pplnsvalid|number_format} |
+ {$pplnsinvalid|number_format} |
+ {if $pplnsinvalid > 0 && $pplnsvalid > 0 }{($pplnsinvalid / $pplnsvalid * 100)|number_format:"2"|default:"0"}{else}0.00{/if} |
+ {if $userpplnsshares > 0 && $pplnsvalid > 0}{(( 100 / $userpplnsshares) * $pplnsvalid)|number_format:"2"|default:"0"}{else}0.00{/if} |
+ {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}
+ {$percentage2|number_format:"2"|default:"0"} |
+ {$amount|default:"0"|number_format:"8"} |
+ {assign var=percentage2 value=0}
+
+
+
+
+
diff --git a/public/templates/mpos/dashboard/js.tpl b/public/templates/mpos/dashboard/js.tpl
index 8c4822c2..1bf84344 100644
--- a/public/templates/mpos/dashboard/js.tpl
+++ b/public/templates/mpos/dashboard/js.tpl
@@ -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)));
diff --git a/public/templates/mpos/dashboard/system_stats.tpl b/public/templates/mpos/dashboard/system_stats.tpl
index 04a97692..46b12f12 100644
--- a/public/templates/mpos/dashboard/system_stats.tpl
+++ b/public/templates/mpos/dashboard/system_stats.tpl
@@ -10,8 +10,16 @@
{elseif $GLOBAL.config.payout_system == 'pps'}
- | PPS Value |
- {$GLOBAL.ppsvalue} |
+ Unpaid Shares |
+ {$GLOBAL.userdata.pps.unpaidshares} |
+
+
+ | Baseline PPS Rate |
+ {$GLOBAL.ppsvalue} {$GLOBAL.config.currency} |
+
+
+ | Pools PPS Rate |
+ {$GLOBAL.poolppsvalue} {$GLOBAL.config.currency} |
| PPS Difficulty |
diff --git a/public/templates/mpos/global/navigation.tpl b/public/templates/mpos/global/navigation.tpl
index af922939..6f2552ed 100644
--- a/public/templates/mpos/global/navigation.tpl
+++ b/public/templates/mpos/global/navigation.tpl
@@ -22,6 +22,8 @@
Transactions
Settings
News
+ Reports
+ Pool Workers
{/if}
{if $smarty.session.AUTHENTICATED|default}
diff --git a/public/templates/mpos/statistics/blocks/default.tpl b/public/templates/mpos/statistics/blocks/default.tpl
index e3a770a1..c2587a12 100644
--- a/public/templates/mpos/statistics/blocks/default.tpl
+++ b/public/templates/mpos/statistics/blocks/default.tpl
@@ -103,14 +103,13 @@
{$BLOCKSFOUND[block].difficulty|number_format:"2"} |
{$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}
+{assign var="totalexpectedshares" value=$totalexpectedshares+$BLOCKSFOUND[block].estshares}
+ {$BLOCKSFOUND[block].estshares|number_format}
|
{if $GLOBAL.config.payout_system == 'pplns'}{$BLOCKSFOUND[block].pplns_shares|number_format} | {/if}
{$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"}
|