From ab85af19a149686825e66d3d66fc31f8f8e92ee3 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 5 Jul 2013 23:23:19 +0200 Subject: [PATCH 001/137] Re-add shares archive table for 24h hashrates This should fix #312 and fix #256 where hashrates are reset on quick rounds. Users are not able to properly view their past 24h hashrates. Will be merged once confirmed working. --- public/include/classes/statistics.class.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index 2ca5bfd1..8f530003 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -374,6 +374,16 @@ class Statistics { ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000) AS hashrate, HOUR(s.time) AS hour FROM " . $this->share->getTableName() . " AS s, accounts AS a + WHERE time < NOW() - INTERVAL 1 HOUR + AND time > NOW() - INTERVAL 25 HOUR + AND a.username = SUBSTRING_INDEX( s.username, '.', 1 ) + AND a.id = ? + GROUP BY HOUR(time) + UNION ALL + SELECT + ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000) AS hashrate, + HOUR(s.time) AS hour + FROM " . $this->share->getArchiveTableName() . " AS s, accounts AS a WHERE time < NOW() - INTERVAL 1 HOUR AND time > NOW() - INTERVAL 25 HOUR AND a.username = SUBSTRING_INDEX( s.username, '.', 1 ) From 2a47fbba4fecea72da0acf5bab3e87ee8d5a6770 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 5 Jul 2013 23:45:32 +0200 Subject: [PATCH 002/137] Fixing PHP Warning on admin->user panel This fixes #383 --- public/include/pages/admin/user.inc.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/public/include/pages/admin/user.inc.php b/public/include/pages/admin/user.inc.php index 9bb9ecee..ecb447b9 100644 --- a/public/include/pages/admin/user.inc.php +++ b/public/include/pages/admin/user.inc.php @@ -34,10 +34,17 @@ if (@$_POST['query']) { $aBalance = $transaction->getBalance($aUser['id']); $aUser['balance'] = $aBalance['confirmed']; $aUser['hashrate'] = $statistics->getUserHashrate($aUser['id']); - $aUser['payout']['est_block'] = round(( (int)$aUser['shares'] / (int)$aRoundShares['valid'] ) * (int)$config['reward'], 3); - $aUser['payout']['est_fee'] = round(($config['fees'] / 100) * $aUser['payout']['est_block'], 3); - $aUser['payout']['est_donation'] = round((( $aUser['donate_percent'] / 100) * ($aUser['payout']['est_block'] - $aUser['payout']['est_fee'])), 3); - $aUser['payout']['est_payout'] = round($aUser['payout']['est_block'] - $aUser['payout']['est_donation'] - $aUser['payout']['est_fee'], 3); + if ($aUser['shares'] > 0) { + $aUser['payout']['est_block'] = round(( (int)$aUser['shares'] / (int)$aRoundShares['valid'] ) * (int)$config['reward'], 3); + $aUser['payout']['est_fee'] = round(($config['fees'] / 100) * $aUser['payout']['est_block'], 3); + $aUser['payout']['est_donation'] = round((( $aUser['donate_percent'] / 100) * ($aUser['payout']['est_block'] - $aUser['payout']['est_fee'])), 3); + $aUser['payout']['est_payout'] = round($aUser['payout']['est_block'] - $aUser['payout']['est_donation'] - $aUser['payout']['est_fee'], 3); + } else { + $aUser['payout']['est_block'] = 0; + $aUser['payout']['est_fee'] = 0; + $aUser['payout']['est_donation'] = 0; + $aUser['payout']['est_payout'] = 0; + } $aUsers[$iKey] = $aUser; } // Assign our variables From 380e802c75d4bc6abc71a39445c672ca0271b2e8 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sat, 6 Jul 2013 08:34:41 +0200 Subject: [PATCH 003/137] fixing stmt issue --- public/include/classes/statistics.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index 8f530003..42ef3019 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -389,7 +389,7 @@ class Statistics { AND a.username = SUBSTRING_INDEX( s.username, '.', 1 ) AND a.id = ? GROUP BY HOUR(time)"); - if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result()) { + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $account_id, $account_id) && $stmt->execute() && $result = $stmt->get_result()) { $aData = array(); while ($row = $result->fetch_assoc()) { $aData[$row['hour']] = $row['hashrate']; From 89af793a28248a727ba2d1186376c450aed3249d Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sat, 6 Jul 2013 17:32:58 +0200 Subject: [PATCH 004/137] Fixing pool hashrate graph Should fix #312 completely. --- public/include/classes/statistics.class.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index 42ef3019..41fd7d95 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -410,6 +410,14 @@ class Statistics { $this->debug->append("STA " . __METHOD__, 4); if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__)) return $data; $stmt = $this->mysqli->prepare(" + SELECT + IFNULL(ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000), 0) AS hashrate, + HOUR(s.time) AS hour + FROM " . $this->share->getTableName() . " AS s + WHERE time < NOW() - INTERVAL 1 HOUR + AND time > NOW() - INTERVAL 25 HOUR + GROUP BY HOUR(time) + UNION ALL SELECT IFNULL(ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000), 0) AS hashrate, HOUR(s.time) AS hour From 9412107b53ffbd576df7508fc937e55ac76177d8 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sat, 6 Jul 2013 17:38:23 +0200 Subject: [PATCH 005/137] Hard coded block limit for pool statistics This should fix a potential DoS like attack when fetching a random amount of blocks continously. Fixes #387 --- public/include/pages/statistics/blocks.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/pages/statistics/blocks.inc.php b/public/include/pages/statistics/blocks.inc.php index c65ea861..6c3b00b8 100644 --- a/public/include/pages/statistics/blocks.inc.php +++ b/public/include/pages/statistics/blocks.inc.php @@ -5,7 +5,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); if (!$user->isAuthenticated()) header("Location: index.php?page=home"); // Grab the last blocks found -empty($_REQUEST['limit']) ? $iLimit = 20 : $iLimit = $_REQUEST['limit']; +$iLimit = 20; $aBlocksFoundData = $statistics->getBlocksFound($iLimit); // Propagate content our template From 307c7ee23f753064937bf186f6a49d750dd75625 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sat, 6 Jul 2013 17:47:50 +0200 Subject: [PATCH 006/137] Enforce unique account emails * display an error if user tries to re-use an email address * moved SQL files to indicate the order of SQL import * added unique email index SQL file This will address #389, still needs email validation. --- public/include/classes/user.class.php | 9 ++++++++- sql/{mmcfe_ng_structure.sql => 000_base_structure.sql} | 0 sql/001_unique_email.sql | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) rename sql/{mmcfe_ng_structure.sql => 000_base_structure.sql} (100%) create mode 100644 sql/001_unique_email.sql diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 69ebf8de..68616e3e 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -50,6 +50,9 @@ class User { public function getUserIp($id) { return $this->getSingle($id, 'loggedIp', 'id'); } + public function getEmail($email) { + return $this->getSingle($email, 'email', 'email', 's'); + } public function getUserFailed($id) { return $this->getSingle($id, 'failed_logins', 'id'); } @@ -439,6 +442,10 @@ class User { **/ public function register($username, $password1, $password2, $pin, $email1='', $email2='') { $this->debug->append("STA " . __METHOD__, 4); + if ($this->getEmail($email1)) { + $this->setErrorMessage( 'This e-mail address is already taken' ); + return false; + } if (strlen($password1) < 8) { $this->setErrorMessage( 'Password is too short, minimum of 8 characters required' ); return false; @@ -479,7 +486,7 @@ class User { if ($this->checkStmt($stmt) && $stmt->bind_param('sssss', $username, $password_hash, $email1, $pin_hash, $apikey_hash)) { if (!$stmt->execute()) { $this->setErrorMessage( 'Unable to register' ); - if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username already exists' ); + if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username or email already registered' ); return false; } $stmt->close(); diff --git a/sql/mmcfe_ng_structure.sql b/sql/000_base_structure.sql similarity index 100% rename from sql/mmcfe_ng_structure.sql rename to sql/000_base_structure.sql diff --git a/sql/001_unique_email.sql b/sql/001_unique_email.sql new file mode 100644 index 00000000..30d2882e --- /dev/null +++ b/sql/001_unique_email.sql @@ -0,0 +1 @@ +ALTER TABLE `accounts` ADD UNIQUE (`email`); From fdb562b078edac7dd451311d151a413c10a5aa0c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sat, 6 Jul 2013 17:57:37 +0200 Subject: [PATCH 007/137] Fixing PHP warning message for empty price var Fixes #375 --- public/templates/mmcFE/global/header.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/templates/mmcFE/global/header.tpl b/public/templates/mmcFE/global/header.tpl index c195c18a..b3d150f6 100644 --- a/public/templates/mmcFE/global/header.tpl +++ b/public/templates/mmcFE/global/header.tpl @@ -4,7 +4,7 @@
- {if $GLOBAL.config.price.currency}{/if} + {if $GLOBAL.config.price.currency}{/if} From 32d764127ed855a615aec942de99d06ac2352f3e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sat, 6 Jul 2013 18:56:35 +0200 Subject: [PATCH 008/137] More descriptive error message exceeding LTC threshold Fixes #388 --- public/include/smarty_globals.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index e7bfd4b5..147b38e9 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -103,7 +103,7 @@ if (@$_SESSION['USERDATA']['id']) { // Site-wide notifications, based on user events if ($aGlobal['userdata']['balance']['confirmed'] >= $config['ap_threshold']['max']) - $_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded your accounts balance. Please transfer some ' . $config['currency'] . "!", 'TYPE' => 'errormsg'); + $_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded the pools configured ' . $config['currency'] . ' warning threshold. Please initiate a transfer!', 'TYPE' => 'warning'); if ($user->getUserFailed($_SESSION['USERDATA']['id']) > 0) $_SESSION['POPUP'][] = array('CONTENT' => 'You have ' . $user->getUserFailed($_SESSION['USERDATA']['id']) . ' failed login attempts! Reset Counter', 'TYPE' => 'errormsg'); } From 752b3e810fa9687e964122d602d2291123b3640a Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sat, 6 Jul 2013 23:11:28 +0200 Subject: [PATCH 009/137] Fixing issue with delayed inserts for shares When shares are inserted with a delay for performance optimizations, this will cause block timestamps to be completely off with share timestamps. Hence the timestamp limited search is now removed, it would cause too many issues for Stratum users/pools. Addresses #392 --- cronjobs/findblock.php | 2 +- public/include/classes/share.class.php | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/cronjobs/findblock.php b/cronjobs/findblock.php index efcc9f11..78bacd3e 100755 --- a/cronjobs/findblock.php +++ b/cronjobs/findblock.php @@ -77,7 +77,7 @@ if (empty($aAllBlocks)) { foreach ($aAllBlocks as $iIndex => $aBlock) { if (empty($aBlock['share_id'])) { // Fetch this blocks upstream ID - if ($share->setUpstream($block->getLastUpstreamId(), $aBlock['time'])) { + if ($share->setUpstream($block->getLastUpstreamId())) { $iCurrentUpstreamId = $share->getUpstreamId(); $iAccountId = $user->getUserId($share->getUpstreamFinder()); } else { diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index 6653814a..85935a2d 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -177,16 +177,15 @@ class Share { * @param last int Skips all shares up to last to find new share * @return bool **/ - public function setUpstream($last=0, $time=0) { + public function setUpstream($last=0) { $stmt = $this->mysqli->prepare(" SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE upstream_result = 'Y' AND id > ? - AND UNIX_TIMESTAMP(time) >= ? ORDER BY id ASC LIMIT 1"); - if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $last, $time) && $stmt->execute() && $result = $stmt->get_result()) { + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $last) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) return true; From 5d57593f5d68a9c52b2c8bc1067faeaecb9dddd3 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 7 Jul 2013 00:19:56 +0200 Subject: [PATCH 010/137] Abort payout process if `share_id` is missing If we have a block with NO `share_id` we abort the entire process. Less of an issue with proportional since PPS is still being paid out, but a block round will never actually end. This will allow for manual intervention by the user. Otherwise blocks found after the one having issues might trigger the payout process and pay out shares of the old block in a more recently found one. Please use #392 for further help on this, I have yet to replicate the unknown block finder issue with the proper amount of upstream shares. --- cronjobs/pps_payout.php | 1 + cronjobs/proportional_payout.php | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php index 22e5fcf7..9c108e78 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -113,6 +113,7 @@ if (empty($aAllBlocks)) { foreach ($aAllBlocks as $iIndex => $aBlock) { // If we are running through more than one block, check for previous share ID $iLastBlockShare = @$aAllBlocks[$iIndex - 1]['share_id'] ? @$aAllBlocks[$iIndex - 1]['share_id'] : 0; + if (!is_numeric($aBlock['share_id'])) die("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue\n"); // Per account statistics $aAccountShares = $share->getSharesForAccounts(@$iLastBlockShare, $aBlock['share_id']); foreach ($aAccountShares as $key => $aData) { diff --git a/cronjobs/proportional_payout.php b/cronjobs/proportional_payout.php index 57b97c2c..fa346dfc 100755 --- a/cronjobs/proportional_payout.php +++ b/cronjobs/proportional_payout.php @@ -40,12 +40,13 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { if (!$aBlock['accounted']) { $iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0; $iCurrentUpstreamId = $aBlock['share_id']; + if (!is_numeric($iCurrentUpstreamId)) die("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue\n"); $aAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']); $iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']); $config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward']; if (empty($aAccountShares)) { - verbose("\nNo shares found for this block\n\n"); + verbose("\nNo shares found for this block: " . $aBlock['height'] . " \n\n"); sleep(2); continue; } From 9d50c9a2229a7e5d58a9b3111a8b5b7f9af46a39 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 7 Jul 2013 19:39:16 +0200 Subject: [PATCH 011/137] User proper archive table for union Addresses #312 --- public/include/classes/statistics.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index 41fd7d95..3e375249 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -421,7 +421,7 @@ class Statistics { SELECT IFNULL(ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000), 0) AS hashrate, HOUR(s.time) AS hour - FROM " . $this->share->getTableName() . " AS s + FROM " . $this->share->getArchiveTableName() . " AS s WHERE time < NOW() - INTERVAL 1 HOUR AND time > NOW() - INTERVAL 25 HOUR GROUP BY HOUR(time)"); From 2586aca34e241b5ad253db7ce2e6435987453274 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 7 Jul 2013 19:52:24 +0200 Subject: [PATCH 012/137] Revert "Fixing issue with delayed inserts for shares" This reverts commit 752b3e810fa9687e964122d602d2291123b3640a. --- cronjobs/findblock.php | 2 +- public/include/classes/share.class.php | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cronjobs/findblock.php b/cronjobs/findblock.php index 78bacd3e..efcc9f11 100755 --- a/cronjobs/findblock.php +++ b/cronjobs/findblock.php @@ -77,7 +77,7 @@ if (empty($aAllBlocks)) { foreach ($aAllBlocks as $iIndex => $aBlock) { if (empty($aBlock['share_id'])) { // Fetch this blocks upstream ID - if ($share->setUpstream($block->getLastUpstreamId())) { + if ($share->setUpstream($block->getLastUpstreamId(), $aBlock['time'])) { $iCurrentUpstreamId = $share->getUpstreamId(); $iAccountId = $user->getUserId($share->getUpstreamFinder()); } else { diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index 85935a2d..6653814a 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -177,15 +177,16 @@ class Share { * @param last int Skips all shares up to last to find new share * @return bool **/ - public function setUpstream($last=0) { + public function setUpstream($last=0, $time=0) { $stmt = $this->mysqli->prepare(" SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE upstream_result = 'Y' AND id > ? + AND UNIX_TIMESTAMP(time) >= ? ORDER BY id ASC LIMIT 1"); - if ($this->checkStmt($stmt) && $stmt->bind_param('i', $last) && $stmt->execute() && $result = $stmt->get_result()) { + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $last, $time) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) return true; From 6dc795fd7747ece2722949e6258b3087868871fc Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 7 Jul 2013 22:04:43 +0200 Subject: [PATCH 013/137] Improved cron logging via logfiles * Added 3rd party logger library KLogger * Adjusted all cronjobs from verbose output to logging * Added new logs folder for crons to write to * Added new .gitignore for logs folder data * Updated blocks class to only fetch blocks with no share_id * Adjusted findblocks to use no blocks class method --- .gitignore | 1 + cronjobs/auto_payout.php | 28 ++--- cronjobs/blockupdate.php | 18 ++- cronjobs/findblock.php | 51 ++++----- cronjobs/logs/README.md | 1 + cronjobs/notifications.php | 30 +++-- cronjobs/pps_payout.php | 33 +++--- cronjobs/proportional_payout.php | 40 ++++--- cronjobs/shared.inc.php | 17 +-- cronjobs/statistics.php | 32 ++---- cronjobs/tickerupdate.php | 8 +- public/include/autoloader.inc.php | 1 + public/include/classes/block.class.php | 18 ++- public/include/lib/KLogger.php | 148 +++++++++++++++++++++++++ 14 files changed, 269 insertions(+), 157 deletions(-) create mode 100644 cronjobs/logs/README.md create mode 100755 public/include/lib/KLogger.php diff --git a/.gitignore b/.gitignore index d67a566e..66b6c501 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /public/include/config/global.inc.php /public/templates/compile/*.php +/cronjobs/logs/*.txt diff --git a/cronjobs/auto_payout.php b/cronjobs/auto_payout.php index aaf715e0..943a6cd7 100755 --- a/cronjobs/auto_payout.php +++ b/cronjobs/auto_payout.php @@ -22,10 +22,8 @@ limitations under the License. // Include all settings and classes require_once('shared.inc.php'); -verbose("Running auto-payouts ..."); - if ($bitcoin->can_connect() !== true) { - verbose(" unable to connect to RPC server, exiting\n"); + $log->logFatal(" unable to connect to RPC server, exiting\n"); exit(1); } @@ -36,16 +34,16 @@ $setting->setValue('auto_payout_active', 1); $users = $user->getAllAutoPayout(); // Quick summary -verbose(" found " . count($users) . " queued payout(s)\n"); +$log->logInfo(" found " . count($users) . " queued payout(s)\n"); // Go through users and run transactions if (! empty($users)) { - verbose("\tUserID\tUsername\tBalance\tThreshold\tAddress\t\t\t\t\tStatus\n\n"); + $log->logInfo("\tUserID\tUsername\tBalance\tThreshold\tAddress"); foreach ($users as $aUserData) { $aBalance = $transaction->getBalance($aUserData['id']); $dBalance = $aBalance['confirmed']; - verbose("\t" . $aUserData['id'] . "\t" . $aUserData['username'] . "\t" . $dBalance . "\t" . $aUserData['ap_threshold'] . "\t\t" . $aUserData['coin_address'] . "\t"); + $log->logInfo("\t" . $aUserData['id'] . "\t" . $aUserData['username'] . "\t" . $dBalance . "\t" . $aUserData['ap_threshold'] . "\t\t" . $aUserData['coin_address']); // Only run if balance meets threshold and can pay the potential transaction fee if ($dBalance > $aUserData['ap_threshold'] && $dBalance > $config['txfee']) { @@ -53,7 +51,7 @@ if (! empty($users)) { try { $bitcoin->validateaddress($aUserData['coin_address']); } catch (BitcoinClientException $e) { - verbose("VERIFY FAILED\n"); + $log->logError('Failed to verifu this users coin address, skipping payout'); continue; } @@ -61,7 +59,7 @@ if (! empty($users)) { try { $bitcoin->sendtoaddress($aUserData['coin_address'], $dBalance); } catch (BitcoinClientException $e) { - verbose("SEND FAILED\n"); + $log->logError('Failed to send requested balance to coin address, please check payout process'); continue; } @@ -71,21 +69,15 @@ if (! empty($users)) { $aMailData['email'] = $user->getUserEmail($user->getUserName($aUserData['id'])); $aMailData['subject'] = 'Auto Payout Completed'; $aMailData['amount'] = $dBalance; - if (!$notification->sendNotification($aUserData['id'], 'auto_payout', $aMailData)) { - verbose("NOTIFY FAILED\n"); - } else { - verbose("OK\n"); - } + if (!$notification->sendNotification($aUserData['id'], 'auto_payout', $aMailData)) + $log->logError('Failed to send notification email to users address: ' . $aMailData['email']); } else { - verbose("FAILED\n"); + $log->logError('Failed to add new Debit_AP transaction in database for user ' . $user->getUserName($aUserData['id'])); } - - } else { - verbose("SKIPPED\n"); } } } else { - verbose(" no user has configured their AP > 0\n"); + $log->logDebug(" no user has configured their AP > 0\n"); } // Mark this job as inactive diff --git a/cronjobs/blockupdate.php b/cronjobs/blockupdate.php index 65779a3a..89a27be1 100755 --- a/cronjobs/blockupdate.php +++ b/cronjobs/blockupdate.php @@ -23,33 +23,31 @@ limitations under the License. require_once('shared.inc.php'); if ( $bitcoin->can_connect() !== true ) { - verbose("Failed to connect to RPC server\n"); + $log->logFatal("Failed to connect to RPC server\n"); exit(1); } // Fetch all unconfirmed blocks $aAllBlocks = $block->getAllUnconfirmed($config['confirmations']); -verbose("ID\tBlockhash\tConfirmations\t\n"); +$log->logInfo("ID\tBlockhash\tConfirmations"); foreach ($aAllBlocks as $iIndex => $aBlock) { $aBlockInfo = $bitcoin->query('getblock', $aBlock['blockhash']); // Fetch this blocks transaction details to find orphan blocks $aTxDetails = $bitcoin->query('gettransaction', $aBlockInfo['tx'][0]); - verbose($aBlock['id'] . "\t" . $aBlock['blockhash'] . "\t" . $aBlock['confirmations'] . " -> " . $aBlockInfo['confirmations'] . "\t"); + $log->logInfo($aBlock['id'] . "\t" . $aBlock['blockhash'] . "\t" . $aBlock['confirmations'] . " -> " . $aBlockInfo['confirmations']); if ($aTxDetails['details'][0]['category'] == 'orphan') { // We have an orphaned block, we need to invalidate all transactions for this one if ($transaction->setOrphan($aBlock['id']) && $block->setConfirmations($aBlock['id'], -1)) { - verbose("ORPHAN\n"); + $log->logInfo(" Block marked as orphan"); } else { - verbose("ORPHAN_ERR"); + $log->logError(" Block became orphaned but unable to update database entries"); } continue; } if ($aBlock['confirmations'] == $aBlockInfo['confirmations']) { - verbose("SKIPPED\n"); - } else if ($block->setConfirmations($aBlock['id'], $aBlockInfo['confirmations'])) { - verbose("UPDATED\n"); - } else { - verbose("ERROR\n"); + $log->logDebug(' No update needed'); + } else if (!$block->setConfirmations($aBlock['id'], $aBlockInfo['confirmations'])) { + $log->logError(' Failed to update block confirmations'); } } diff --git a/cronjobs/findblock.php b/cronjobs/findblock.php index efcc9f11..28dddb4b 100755 --- a/cronjobs/findblock.php +++ b/cronjobs/findblock.php @@ -25,24 +25,22 @@ require_once('shared.inc.php'); // Fetch our last block found from the DB as a starting point $aLastBlock = @$block->getLast(); $strLastBlockHash = $aLastBlock['blockhash']; -if (!$strLastBlockHash) { - $strLastBlockHash = ''; -} +if (!$strLastBlockHash) $strLastBlockHash = ''; // Fetch all transactions since our last block if ( $bitcoin->can_connect() === true ){ $aTransactions = $bitcoin->query('listsinceblock', $strLastBlockHash); } else { - verbose("Aborted: " . $bitcoin->can_connect() . "\n"); + $log->logFatal('Unable to conenct to RPC server backend'); exit(1); } // Nothing to do so bail out if (empty($aTransactions['transactions'])) { - verbose("No new RPC transactions since last block\n"); + $log->logDebug('No new RPC transactions since last block'); } else { // Table header - verbose("Blockhash\t\tHeight\tAmount\tConfirmations\tDiff\t\tTime\t\t\tStatus\n"); + $log->logInfo("Blockhash\t\tHeight\tAmount\tConfirmations\tDiff\t\tTime"); // Let us add those blocks as unaccounted foreach ($aTransactions['transactions'] as $iIndex => $aData) { @@ -51,29 +49,26 @@ if (empty($aTransactions['transactions'])) { $config['reward_type'] == 'block' ? $aData['amount'] = $aData['amount'] : $aData['amount'] = $config['reward']; $aData['height'] = $aBlockInfo['height']; $aData['difficulty'] = $aBlockInfo['difficulty']; - verbose(substr($aData['blockhash'], 0, 15) . "...\t" . + $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']) . "\t"); - if ( $block->addBlock($aData) ) { - verbose("Added\n"); - } else { - verbose("Failed" . "\n"); + strftime("%Y-%m-%d %H:%M:%S", $aData['time'])); + if (!$block->addBlock($aData) ) { + $log->logFatal('Unable to add this block to database: ' . $aData['height']); } } } } -verbose("\n"); // Now with our blocks added we can scan for their upstream shares -$aAllBlocks = $block->getAllUnaccounted('ASC'); +$aAllBlocks = $block->getAllUnsetShareId('ASC'); if (empty($aAllBlocks)) { - verbose("No new unaccounted blocks found\n"); + $log->logDebug('No new blocks without share_id found in database'); } else { // Loop through our unaccounted blocks - verbose("\nBlock ID\tBlock Height\tAmount\tShare ID\tShares\tFinder\t\t\tStatus\n"); + $log->logInfo("Block ID\t\tHeight\tAmount\tShare ID\tShares\tFinder"); foreach ($aAllBlocks as $iIndex => $aBlock) { if (empty($aBlock['share_id'])) { // Fetch this blocks upstream ID @@ -81,40 +76,35 @@ if (empty($aAllBlocks)) { $iCurrentUpstreamId = $share->getUpstreamId(); $iAccountId = $user->getUserId($share->getUpstreamFinder()); } else { - verbose("\nUnable to fetch blocks upstream share. Aborting!\n"); - verbose($share->getError() . "\n"); + $log->logFatal('Unable to fetch blocks upstream share, aborted:' . $share->getError()); exit; } // Fetch share information if (!$iPreviousShareId = $block->getLastShareId()) { $iPreviousShareId = 0; - verbose("\nUnable to find highest share ID found so far\n"); - verbose("If this is your first block, this is normal\n\n"); + $log->logInfo('Unable to find highest share ID found so far, if this is your first block, this is normal.'); } $iRoundShares = $share->getRoundShares($iPreviousShareId, $iCurrentUpstreamId); // Store new information - $strStatus = "OK"; if (!$block->setShareId($aBlock['id'], $iCurrentUpstreamId)) - $strStatus = "Share ID Failed"; + $log->logError('Failed to update share ID in database for block ' . $aBlock['height']); if (!$block->setFinder($aBlock['id'], $iAccountId)) - $strStatus = "Finder Failed"; + $log->logError('Failed to update finder account ID in database for block ' . $aBlock['height']); if (!$block->setShares($aBlock['id'], $iRoundShares)) - $strStatus = "Shares Failed"; + $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'])) { - $strStatus = "Bonus Failed"; + $log->logError('Failed to create Bonus transaction in database for user ' . $user->getUserName($iAccountId) . ' for block ' . $aBlock['height']); } - verbose( + $log->logInfo( $aBlock['id'] . "\t\t" . $aBlock['height'] . "\t\t" . $aBlock['amount'] . "\t" . $iCurrentUpstreamId . "\t\t" . $iRoundShares . "\t" - . "[$iAccountId] " . $user->getUserName($iAccountId) . "\t\t" - . $strStatus - . "\n" + . "[$iAccountId] " . $user->getUserName($iAccountId) ); // Notify users @@ -125,7 +115,8 @@ if (empty($aAllBlocks)) { $aMailData['subject'] = 'New Block'; $aMailData['email'] = $user->getUserEmail($user->getUserName($aData['account_id'])); $aMailData['shares'] = $iRoundShares; - $notification->sendNotification($aData['account_id'], 'new_block', $aMailData); + if (!$notification->sendNotification($aData['account_id'], 'new_block', $aMailData)) + $log->logError('Failed to notify user of new found block: ' . $user->getUserName($aData['account_id'])); } } } diff --git a/cronjobs/logs/README.md b/cronjobs/logs/README.md new file mode 100644 index 00000000..864999fd --- /dev/null +++ b/cronjobs/logs/README.md @@ -0,0 +1 @@ +Logging directory for cronjobs. diff --git a/cronjobs/notifications.php b/cronjobs/notifications.php index 61a608ab..a60e4657 100755 --- a/cronjobs/notifications.php +++ b/cronjobs/notifications.php @@ -22,51 +22,47 @@ limitations under the License. // Include all settings and classes require_once('shared.inc.php'); -verbose("Running system notifications\n"); -verbose(" IDLE Worker Notifications ..."); +$log->logDebug(" IDLE Worker Notifications ..."); // Find all IDLE workers $aWorkers = $worker->getAllIdleWorkers(); if (empty($aWorkers)) { - verbose(" no idle workers found\n"); + $log->logDebug(" no idle workers found\n"); } else { - verbose(" found " . count($aWorkers) . " IDLE workers\n"); + $log->logInfo(" found " . count($aWorkers) . " IDLE workers\n"); foreach ($aWorkers as $aWorker) { $aData = $aWorker; $aData['username'] = $user->getUserName($aWorker['account_id']); $aData['subject'] = 'IDLE Worker : ' . $aWorker['username']; $aData['worker'] = $aWorker['username']; $aData['email'] = $user->getUserEmail($aData['username']); - verbose(" " . $aWorker['username'] . "..."); - if (!$notification->sendNotification($aWorker['account_id'], 'idle_worker', $aData)) { - verbose(" " . $notification->getError() . "\n"); - } else { - verbose(" sent\n"); - } + $log->logInfo(" " . $aWorker['username'] . "..."); + if (!$notification->sendNotification($aWorker['account_id'], 'idle_worker', $aData)) + $log->logError(" Failed sending notifications: " . $notification->getError() . "\n"); } } -verbose(" Reset IDLE Worker Notifications ..."); +$log->logDebug(" Reset IDLE Worker Notifications ..."); // We notified, lets check which recovered $aNotifications = $notification->getAllActive('idle_worker'); if (!empty($aNotifications)) { - verbose(" found " . count($aNotifications) . " active notification(s)\n"); + $log->logInfo(" found " . count($aNotifications) . " active notification(s)\n"); foreach ($aNotifications as $aNotification) { $aData = json_decode($aNotification['data'], true); $aWorker = $worker->getWorker($aData['id']); - verbose(" " . $aWorker['username'] . " ..."); + $log->logInfo(" " . $aWorker['username'] . " ..."); if ($aWorker['active'] == 1) { if ($notification->setInactive($aNotification['id'])) { - verbose(" updated #" . $aNotification['id'] . " for " . $aWorker['username'] . " as inactive\n"); + $log->logInfo(" updated #" . $aNotification['id'] . " for " . $aWorker['username'] . " as inactive\n"); } else { - verbose(" failed to update #" . $aNotification['id'] . " for " . $aWorker['username'] . "\n"); + $log->logInfo(" failed to update #" . $aNotification['id'] . " for " . $aWorker['username'] . "\n"); } } else { - verbose(" still inactive\n"); + $log->logInfo(" still inactive\n"); } } } else { - verbose(" no active IDLE worker notifications\n"); + $log->logDebug(" no active IDLE worker notifications\n"); } ?> diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php index 9c108e78..388f49ac 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -25,7 +25,7 @@ require_once('shared.inc.php'); // Check if we are set as the payout system if ($config['payout_system'] != 'pps') { - verbose("Please activate this cron in configuration via payout_system = pps\n"); + $log->logInfo("Please activate this cron in configuration via payout_system = pps\n"); exit(0); } @@ -35,7 +35,7 @@ if ( $bitcoin->can_connect() === true ){ if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) $dDifficulty = $dDifficulty['proof-of-work']; } else { - verbose("Aborted: " . $bitcoin->can_connect() . "\n"); + $log->logFatal("Aborted: " . $bitcoin->can_connect() . "\n"); exit(1); } @@ -58,7 +58,7 @@ $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); -verbose("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\tPayout\t\tDonation\tFee\t\tStatus\n"); +$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 @@ -74,60 +74,59 @@ foreach ($aAccountShares as $aData) { // Calculate donation amount $aData['donation'] = number_format(round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 8), 8); - verbose($aData['id'] . "\t" . + $log->logInfo($aData['id'] . "\t" . $aData['username'] . "\t" . $aData['invalid'] . "\t" . $aData['valid'] . "\t*\t" . $pps_value . "\t=\t" . $aData['payout'] . "\t" . $aData['donation'] . "\t" . - $aData['fee'] . "\t"); + $aData['fee']); - $strStatus = "OK"; // Add new credit transaction if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit_PPS')) - $strStatus = "Transaction Failed"; + $log->logError('Failed to add Credit_PPS transaction in database'); // Add new fee debit for this block if ($aData['fee'] > 0 && $config['fees'] > 0) if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee_PPS')) - $strStatus = "Fee Failed"; + $log->logError('Failed to add Fee_PPS transaction in database'); // Add new donation debit if ($aData['donation'] > 0) if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation_PPS')) - $strStatus = "Donation Failed"; - verbose($strStatus . "\n"); + $log->logError('Failed to add Donation_PPS transaction in database'); } // Store our last inserted ID for the next run $setting->setValue('pps_last_share_id', $iLastShareId); -verbose("\n\n------------------------------------------------------------------------------------\n\n"); - // Fetch all unaccounted blocks $aAllBlocks = $block->getAllUnaccounted('ASC'); if (empty($aAllBlocks)) { - verbose("No new unaccounted blocks found\n"); + $log->logDebug("No new unaccounted blocks found"); } // Go through blocks and archive/delete shares that have been accounted for foreach ($aAllBlocks as $iIndex => $aBlock) { // If we are running through more than one block, check for previous share ID $iLastBlockShare = @$aAllBlocks[$iIndex - 1]['share_id'] ? @$aAllBlocks[$iIndex - 1]['share_id'] : 0; - if (!is_numeric($aBlock['share_id'])) die("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue\n"); + if (!is_numeric($aBlock['share_id'])) { + $log->logFatal("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue"); + exit(1); + } // Per account statistics $aAccountShares = $share->getSharesForAccounts(@$iLastBlockShare, $aBlock['share_id']); foreach ($aAccountShares as $key => $aData) { if (!$statistics->updateShareStatistics($aData, $aBlock['id'])) - verbose("Failed to update stats for this block on : " . $aData['username'] . "\n"); + $log->logError("Failed to update stats for this block on : " . $aData['username']); } // Move shares to archive if ($config['archive_shares'] && $aBlock['share_id'] < $iLastShareId) { if (!$share->moveArchive($aBlock['share_id'], $aBlock['id'], @$iLastBlockShare)) - verbose("Archving failed\n"); + $log->logError("Archving failed"); } // Delete shares if ($aBlock['share_id'] < $iLastShareId && !$share->deleteAccountedShares($aBlock['share_id'], $iLastBlockShare)) { - verbose("\nERROR : Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . $iLastBlockShare . ", aborting!\n"); + $log->logFatal("Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . $iLastBlockShare . ", aborting!"); exit(1); } // Mark this block as accounted for diff --git a/cronjobs/proportional_payout.php b/cronjobs/proportional_payout.php index fa346dfc..c9c61539 100755 --- a/cronjobs/proportional_payout.php +++ b/cronjobs/proportional_payout.php @@ -24,36 +24,39 @@ require_once('shared.inc.php'); // Check if we are set as the payout system if ($config['payout_system'] != 'prop') { - verbose("Please activate this cron in configuration via payout_system = prop\n"); + $log->logInfo("Please activate this cron in configuration via payout_system = prop"); exit(0); } // Fetch all unaccounted blocks $aAllBlocks = $block->getAllUnaccounted('ASC'); if (empty($aAllBlocks)) { - verbose("No new unaccounted blocks found\n"); + $log->logDebug('No new unaccounted blocks found in database'); exit(0); } $count = 0; +// Table header for account shares +$log->logInfo("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee"); foreach ($aAllBlocks as $iIndex => $aBlock) { if (!$aBlock['accounted']) { $iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0; $iCurrentUpstreamId = $aBlock['share_id']; - if (!is_numeric($iCurrentUpstreamId)) die("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue\n"); + if (!is_numeric($iCurrentUpstreamId)) { + $log->logFatal("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue."); + $log->logFatal("Please assign a valid share ID to this block to continue the payout process."); + exit(1); + } $aAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']); $iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']); $config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward']; if (empty($aAccountShares)) { - verbose("\nNo shares found for this block: " . $aBlock['height'] . " \n\n"); + $log->logFatal('No shares found for this block, aborted: ' . $aBlock['height']); sleep(2); continue; } - // Table header for account shares - verbose("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee\t\tStatus\n"); - // Loop through all accounts that have found shares for this round foreach ($aAccountShares as $key => $aData) { // Payout based on shares, PPS system @@ -69,45 +72,40 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $aData['donation'] = number_format(round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 8), 8); // Verbose output of this users calculations - verbose($aData['id'] . "\t" . + $log->logInfo($aData['id'] . "\t" . $aData['username'] . "\t" . $aData['valid'] . "\t" . $aData['invalid'] . "\t" . $aData['percentage'] . "\t" . $aData['payout'] . "\t" . $aData['donation'] . "\t" . - $aData['fee'] . "\t"); + $aData['fee']); - $strStatus = "OK"; // Update user share statistics if (!$statistics->updateShareStatistics($aData, $aBlock['id'])) - $strStatus = "Stats Failed"; + $log->logFatal('Failed to update share statistics for ' . $aData['username']); // Add new credit transaction if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit', $aBlock['id'])) - $strStatus = "Transaction Failed"; + $log->logFatal('Failed to insert new Credit transaction to database for ' . $aData['username']); // Add new fee debit for this block if ($aData['fee'] > 0 && $config['fees'] > 0) if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee', $aBlock['id'])) - $strStatus = "Fee Failed"; + $log->logFatal('Failed to insert new Fee transaction to database for ' . $aData['username']); // Add new donation debit if ($aData['donation'] > 0) if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation', $aBlock['id'])) - $strStatus = "Donation Failed"; - verbose("\t$strStatus\n"); + $log->logFatal('Failed to insert new Donation transaction to database for ' . $aData['username']); } // Move counted shares to archive before this blockhash upstream share if ($config['archive_shares']) $share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId); // Delete all accounted shares if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) { - verbose("\nERROR : Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting!\n"); + $log->logFatal('Failed to delete accounted shares from ' . $iPreviousShareId . ' to ' . $iCurrentUpstreamId . ', aborted'); exit(1); } // Mark this block as accounted for - if (!$block->setAccounted($aBlock['id'])) { - verbose("\nERROR : Failed to mark block as accounted! Aborting!\n"); - } - - verbose("------------------------------------------------------------------------\n\n"); + if (!$block->setAccounted($aBlock['id'])) + $log->logFatal('Failed to mark block as accounted! Aborted.'); } } diff --git a/cronjobs/shared.inc.php b/cronjobs/shared.inc.php index a0b1dc5d..9d3ae322 100644 --- a/cronjobs/shared.inc.php +++ b/cronjobs/shared.inc.php @@ -31,16 +31,7 @@ require_once(BASEPATH . 'include/config/global.inc.php'); // We include all needed files here, even though our templates could load them themself require_once(INCLUDE_DIR . '/autoloader.inc.php'); -// Parse command line -$options = getopt("v"); -if (array_key_exists('v', $options)) { - define("VERBOSE", true); -} else { - define("VERBOSE", false); -} - -// Command line cron functions only -function verbose($msg) { - if (VERBOSE) echo $msg; -} - +// Load 3rd party logging library for running crons +$log = new KLogger ( 'logs/' . basename($_SERVER['PHP_SELF'], '.php') . '.txt' , KLogger::DEBUG ); +$log->LogDebug('Starting ' . basename($_SERVER['PHP_SELF'], '.php')); +?> diff --git a/cronjobs/statistics.php b/cronjobs/statistics.php index 69c1db5f..9ce96bde 100755 --- a/cronjobs/statistics.php +++ b/cronjobs/statistics.php @@ -25,44 +25,34 @@ require_once('shared.inc.php'); // Fetch all cachable values but disable fetching from cache $statistics->setGetCache(false); -// Verbose output -verbose("Running statistical cache updates\n"); - // Since fetching from cache is disabled, overwrite our stats -verbose(" getRoundShares ..."); $start = microtime(true); if (!$statistics->getRoundShares()) - verbose(" update failed"); -verbose(" " . number_format(microtime(true) - $start, 2) . " seconds\n"); -verbose(" getTopContributors shares ..."); + $log->logError("getRoundShares update failed"); +$log->logInfo("getRoundShares update " . number_format(microtime(true) - $start, 2) . " seconds"); $start = microtime(true); if (!$statistics->getTopContributors('shares')) - verbose(" update failed"); -verbose(" " . number_format(microtime(true) - $start, 2) . " seconds\n"); -verbose(" getTopContributors hashes ..."); + $log->logError("getTopContributors shares update failed"); +$log->logInfo("getTopContributors shares " . number_format(microtime(true) - $start, 2) . " seconds"); $start = microtime(true); if (!$statistics->getTopContributors('hashes')) - verbose(" update failed"); -verbose(" " . number_format(microtime(true) - $start, 2) . " seconds\n"); -verbose(" getCurrentHashrate ..."); + $log->logError("getTopContributors hashes update failed"); +$log->logInfo("getTopContributors hashes " . number_format(microtime(true) - $start, 2) . " seconds"); $start = microtime(true); if (!$statistics->getCurrentHashrate()) - verbose(" update failed"); -verbose(" " . number_format(microtime(true) - $start, 2) . " seconds\n"); + $log->logError("getCurrentHashrate update failed"); +$log->logInfo("getCurrentHashrate " . number_format(microtime(true) - $start, 2) . " seconds"); // Admin specific statistics, we cache the global query due to slowness -verbose(" getAllUserStats ..."); $start = microtime(true); if (!$statistics->getAllUserStats('%')) - verbose(" update failed"); -verbose(" " . number_format(microtime(true) - $start, 2) . " seconds\n"); + $log->logError("getAllUserStats update failed"); +$log->logInfo("getAllUserStats " . number_format(microtime(true) - $start, 2) . " seconds"); // Per user share statistics based on all shares submitted -verbose(" getAllUserShares ..."); $start = microtime(true); $aUserShares = $statistics->getAllUserShares(); -verbose(" " . number_format(microtime(true) - $start, 2) . " seconds"); +$log->logInfo("getAllUserShares " . number_format(microtime(true) - $start, 2) . " seconds"); foreach ($aUserShares as $aShares) { $memcache->setCache('getUserShares'. $aShares['id'], $aShares); } -verbose("\n"); ?> diff --git a/cronjobs/tickerupdate.php b/cronjobs/tickerupdate.php index 26323738..4b36adb2 100755 --- a/cronjobs/tickerupdate.php +++ b/cronjobs/tickerupdate.php @@ -25,14 +25,12 @@ require_once('shared.inc.php'); // Include additional file not set in autoloader require_once(CLASS_DIR . '/tools.class.php'); -verbose("Running scheduled updates\n"); -verbose(" Price API Call ... "); if ($price = $tools->getPrice()) { - verbose("found $price as price\n"); + $log->logInfo("Price update: found $price as price"); if (!$setting->setValue('price', $price)) - verbose("unable to update value in settings table\n"); + $log->logError("unable to update value in settings table"); } else { - verbose("failed to fetch API data: " . $tools->getError() . "\n"); + $log->logFatal("failed to fetch API data: " . $tools->getError()); } ?> diff --git a/public/include/autoloader.inc.php b/public/include/autoloader.inc.php index e3a4534b..c74fa62b 100644 --- a/public/include/autoloader.inc.php +++ b/public/include/autoloader.inc.php @@ -17,6 +17,7 @@ require_once(CLASS_DIR . '/debug.class.php'); require_once(CLASS_DIR . '/bitcoin.class.php'); require_once(CLASS_DIR . '/statscache.class.php'); require_once(CLASS_DIR . '/bitcoinwrapper.class.php'); +require_once(INCLUDE_DIR . '/lib/KLogger.php'); require_once(INCLUDE_DIR . '/database.inc.php'); require_once(INCLUDE_DIR . '/smarty.inc.php'); // Load classes that need the above as dependencies diff --git a/public/include/classes/block.class.php b/public/include/classes/block.class.php index 743ce67e..2fcbad09 100644 --- a/public/include/classes/block.class.php +++ b/public/include/classes/block.class.php @@ -55,6 +55,18 @@ class Block { return false; } + /** + * Fetch all blocks without a share ID + * @param order string Sort order, default ASC + * @return data array Array with database fields as keys + **/ + public function getAllUnsetShareId($order='ASC') { + $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE ISNULL(share_id) ORDER BY height $order"); + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_all(MYSQLI_ASSOC); + return false; + } + /** * Fetch all unaccounted blocks * @param order string Sort order, default ASC @@ -62,12 +74,8 @@ class Block { **/ public function getAllUnaccounted($order='ASC') { $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE accounted = 0 ORDER BY height $order"); - if ($this->checkStmt($stmt)) { - $stmt->execute(); - $result = $stmt->get_result(); - $stmt->close(); + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - } return false; } diff --git a/public/include/lib/KLogger.php b/public/include/lib/KLogger.php new file mode 100755 index 00000000..9f53788e --- /dev/null +++ b/public/include/lib/KLogger.php @@ -0,0 +1,148 @@ + + * Date : July 26, 2008 + * Comments : Originally written for use with wpSearch + * Website : http://codefury.net + * Version : 1.0 + * + * Usage: + * $log = new KLogger ( "log.txt" , KLogger::INFO ); + * $log->LogInfo("Returned a million search results"); //Prints to the log file + * $log->LogFATAL("Oh dear."); //Prints to the log file + * $log->LogDebug("x = 5"); //Prints nothing due to priority setting + */ + + class KLogger + { + + const DEBUG = 1; // Most Verbose + const INFO = 2; // ... + const WARN = 3; // ... + const ERROR = 4; // ... + const FATAL = 5; // Least Verbose + const OFF = 6; // Nothing at all. + + const LOG_OPEN = 1; + const OPEN_FAILED = 2; + const LOG_CLOSED = 3; + + /* Public members: Not so much of an example of encapsulation, but that's okay. */ + public $Log_Status = KLogger::LOG_CLOSED; + public $DateFormat = "Y-m-d G:i:s"; + public $MessageQueue; + + private $log_file; + private $priority = KLogger::INFO; + + private $file_handle; + + public function __construct( $filepath , $priority ) + { + if ( $priority == KLogger::OFF ) return; + + $this->log_file = $filepath; + $this->MessageQueue = array(); + $this->priority = $priority; + + if ( file_exists( $this->log_file ) ) + { + if ( !is_writable($this->log_file) ) + { + $this->Log_Status = KLogger::OPEN_FAILED; + $this->MessageQueue[] = "The file exists, but could not be opened for writing. Check that appropriate permissions have been set."; + return; + } + } + + if ( $this->file_handle = fopen( $this->log_file , "a" ) ) + { + $this->Log_Status = KLogger::LOG_OPEN; + $this->MessageQueue[] = "The log file was opened successfully."; + } + else + { + $this->Log_Status = KLogger::OPEN_FAILED; + $this->MessageQueue[] = "The file could not be opened. Check permissions."; + } + + return; + } + + public function __destruct() + { + if ( $this->file_handle ) + fclose( $this->file_handle ); + } + + public function LogInfo($line) + { + $this->Log( $line , KLogger::INFO ); + } + + public function LogDebug($line) + { + $this->Log( $line , KLogger::DEBUG ); + } + + public function LogWarn($line) + { + $this->Log( $line , KLogger::WARN ); + } + + public function LogError($line) + { + $this->Log( $line , KLogger::ERROR ); + } + + public function LogFatal($line) + { + $this->Log( $line , KLogger::FATAL ); + } + + public function Log($line, $priority) + { + if ( $this->priority <= $priority ) + { + $status = $this->getTimeLine( $priority ); + $this->WriteFreeFormLine ( "$status $line \n" ); + } + } + + public function WriteFreeFormLine( $line ) + { + if ( $this->Log_Status == KLogger::LOG_OPEN && $this->priority != KLogger::OFF ) + { + if (fwrite( $this->file_handle , $line ) === false) { + $this->MessageQueue[] = "The file could not be written to. Check that appropriate permissions have been set."; + } + } + } + + private function getTimeLine( $level ) + { + $time = date( $this->DateFormat ); + + switch( $level ) + { + case KLogger::INFO: + return "$time - INFO -->"; + case KLogger::WARN: + return "$time - WARN -->"; + case KLogger::DEBUG: + return "$time - DEBUG -->"; + case KLogger::ERROR: + return "$time - ERROR -->"; + case KLogger::FATAL: + return "$time - FATAL -->"; + default: + return "$time - LOG -->"; + } + } + + } + + +?> \ No newline at end of file From 952c8732b164d49b328ebcdbc767b67f22eadb77 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 7 Jul 2013 22:15:40 +0200 Subject: [PATCH 014/137] Properly display piechart, first attempt Should further address #312 --- public/templates/mmcFE/statistics/graphs/both.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/templates/mmcFE/statistics/graphs/both.tpl b/public/templates/mmcFE/statistics/graphs/both.tpl index ff760be1..61f796dd 100644 --- a/public/templates/mmcFE/statistics/graphs/both.tpl +++ b/public/templates/mmcFE/statistics/graphs/both.tpl @@ -30,7 +30,7 @@ {/for} {for $i=0 to date('G', time() - 60 * 60)} - + {/for} From 6193604598f084e66d53f96980bb475aa70a5c2d Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 7 Jul 2013 22:24:52 +0200 Subject: [PATCH 015/137] Fallback call if upstream share not found properly Implemented a fallback method in case no upstream share can be found for a block. This will result in same strange behaviour especially if a later block has properly added a valid share and this will be used for a previous block. At least now even the last block will be properly found and marked as discovered by a user, even though no actual upstream share was involved in this. This is a dirty workaround for pools having payout issues. After all blocks are processed and assuming upstream shares continue to work as expected, this will *skip* broken blocks/shares. Workaround fix for #392 --- public/include/classes/share.class.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index 6653814a..5e4db85e 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -191,6 +191,20 @@ class Share { if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) return true; } + // First attempt failed, we do a fallback with any share available for now + $stmt = $this->mysqli->prepare(" + SELECT + SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id + FROM $this->table + WHERE our_result = 'Y' + AND id > ? + AND UNIX_TIMESTAMP(time) >= ? + ORDER BY id ASC LIMIT 1"); + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $last, $time) && $stmt->execute() && $result = $stmt->get_result()) { + $this->oUpstream = $result->fetch_object(); + if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) + return true; + } // Catchall return false; } From 5e98496cecc0550d1a1a1cb16f426a9ae81e1b24 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 8 Jul 2013 07:22:05 +0200 Subject: [PATCH 016/137] Update proportional_payout.php Abort if no shares could be found for a block to allow user intervention to fix it. --- cronjobs/proportional_payout.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cronjobs/proportional_payout.php b/cronjobs/proportional_payout.php index c9c61539..d7ab7e95 100755 --- a/cronjobs/proportional_payout.php +++ b/cronjobs/proportional_payout.php @@ -54,7 +54,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { if (empty($aAccountShares)) { $log->logFatal('No shares found for this block, aborted: ' . $aBlock['height']); sleep(2); - continue; + exit(1); } // Loop through all accounts that have found shares for this round From 83f8438a9c5bf0bbc6bfa28a5aac2a9846fb8e91 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 8 Jul 2013 08:49:55 +0200 Subject: [PATCH 017/137] last cleanup before merge --- cronjobs/pps_payout.php | 2 +- cronjobs/proportional_payout.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php index 388f49ac..ee35e2ad 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -131,7 +131,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { } // Mark this block as accounted for if (!$block->setAccounted($aBlock['id'])) { - verbose("\nERROR : Failed to mark block as accounted! Aborting!\n"); + $log->logFatal("Failed to mark block as accounted! Aborting!"); exit(1); } } diff --git a/cronjobs/proportional_payout.php b/cronjobs/proportional_payout.php index d7ab7e95..8a7dd2cd 100755 --- a/cronjobs/proportional_payout.php +++ b/cronjobs/proportional_payout.php @@ -53,7 +53,6 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { if (empty($aAccountShares)) { $log->logFatal('No shares found for this block, aborted: ' . $aBlock['height']); - sleep(2); exit(1); } From 1c2891cc78b94e137226b8e678a3a796992d8512 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 8 Jul 2013 08:54:17 +0200 Subject: [PATCH 018/137] Removed piechart, removed no shares notification * Removed broken piechart for now * Do not display message if no shares are found for calculations to avoid cross-tabe error messages Fixes #312 --- public/templates/mmcFE/statistics/graphs/both.tpl | 6 +----- public/templates/mmcFE/statistics/graphs/mine.tpl | 2 -- public/templates/mmcFE/statistics/graphs/pool.tpl | 2 -- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/public/templates/mmcFE/statistics/graphs/both.tpl b/public/templates/mmcFE/statistics/graphs/both.tpl index 61f796dd..03832b5f 100644 --- a/public/templates/mmcFE/statistics/graphs/both.tpl +++ b/public/templates/mmcFE/statistics/graphs/both.tpl @@ -1,7 +1,6 @@ {if is_array($YOURHASHRATES) && is_array($POOLHASHRATES)}
-{foreach from=array('area','pie') item=chartType} -
  • {$GLOBAL.config.currency}/{$GLOBAL.config.price.currency}: {$GLOBAL.price|default:"n/a"|number_format:"4"}    
  • {$GLOBAL.config.currency}/{$GLOBAL.config.price.currency}: {$GLOBAL.price|default:"0"|number_format:"4"}    
  • Network Hashrate: {($GLOBAL.nethashrate / 1000 / 1000 )|default:"0"|number_format:"3"} MH/s    
  • Pool Hashrate: {($GLOBAL.hashrate / 1000)|number_format:"3"} MH/s    
  • Pool Sharerate: {$GLOBAL.sharerate|number_format:"2"} Shares/s    
  • {$POOLHASHRATES.$i|default:"0"}{$POOLHASHRATES.$i|default:"0"}{$POOLHASHRATES.$i - $YOURHASHRATES.$i|default:"0"}
    +
    @@ -36,8 +35,5 @@
    Your vs Pool Hashrate

    -{/foreach}
    -{else} -

  • No shares available to start calculations
  • {/if} diff --git a/public/templates/mmcFE/statistics/graphs/mine.tpl b/public/templates/mmcFE/statistics/graphs/mine.tpl index fe217f2f..4d4259f2 100644 --- a/public/templates/mmcFE/statistics/graphs/mine.tpl +++ b/public/templates/mmcFE/statistics/graphs/mine.tpl @@ -26,6 +26,4 @@ -{else} -

  • No shares available to start calculations
  • {/if} diff --git a/public/templates/mmcFE/statistics/graphs/pool.tpl b/public/templates/mmcFE/statistics/graphs/pool.tpl index ba2ed4e5..f8d118c7 100644 --- a/public/templates/mmcFE/statistics/graphs/pool.tpl +++ b/public/templates/mmcFE/statistics/graphs/pool.tpl @@ -26,6 +26,4 @@ -{else} -

  • No shares available to start calculations
  • {/if} From 420ae332b5985f9f3f6651309e195d78ac253f23 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 2 Jul 2013 14:08:33 +0200 Subject: [PATCH 019/137] Add detailed smarty cache documentation to config Instead of just making it availble document the smarty cache feature. It might work for users, but it's advised to rely on the memcache instead. Fixes #309 --- public/include/config/global.inc.dist.php | 26 +++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index c5a298c7..5778f2c5 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -258,8 +258,30 @@ $config['cookie']['path'] = '/'; $config['cookie']['name'] = 'POOLERCOOKIE'; $config['cookie']['domain'] = ''; -// Disable or enable smarty cache -// This is usually not required, default: 0 + +/** + * Enable or disable the Smarty cache + * + * Explanation: + * Smarty implements a file based cache for all HTML output generated + * from dynamic scripts. It can be enabled to cache the HTML data on disk, + * future request are served from those cache files. + * + * This may or may not work as expected, in general Memcache is used to cache + * all data so rendering the page should not take too long anyway. + * + * You can test this out and enable (1) this setting but it's not guaranteed to + * work with mmcfe-ng. + * + * Ensure that the folder `templates/cache` is writable by the webserver! + * + * Options: + * 0 = disabled + * 1 = enabled + * + * Default: + * 0 = disabled + **/ $config['cache'] = 0; ?> From 3449d5c29c6d05c893ac66dca2440b5aa5d2dcf0 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 2 Jul 2013 14:30:07 +0200 Subject: [PATCH 020/137] Adding cache lifetime option to smarty config * Renamed configuration array to `smarty` => `cache` * Added `smarty` => `cache_lifetime` to expire cache files properly This should be safe to use, be aware that each page request is cached! That includes any POST/GET calls to the site. It does help in speeding up the site, up to 100% on some requests. For a high traffic site it probably makes sense to enable this option with a low cache lifetime to ensure most recent data. Addresses #309 --- .gitignore | 1 + public/include/config/global.inc.dist.php | 21 ++++++++++++++------- public/include/smarty.inc.php | 3 ++- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 66b6c501..a8f1b1ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /public/include/config/global.inc.php /public/templates/compile/*.php /cronjobs/logs/*.txt +/public/templates/cache/*.php diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 5778f2c5..8e502ed1 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -275,13 +275,20 @@ $config['cookie']['domain'] = ''; * * Ensure that the folder `templates/cache` is writable by the webserver! * - * Options: - * 0 = disabled - * 1 = enabled + * cache = Enable/Disable the cache + * cache_lifetime = Time to keep files in seconds before updating them * - * Default: - * 0 = disabled + * Options: + * cache: + * 0 = disabled + * 1 = enabled + * cache_lifetime: + * time in seconds + * + * Defaults: + * cache = 0, disabled + * cache_lifetime = 30 seconds **/ -$config['cache'] = 0; - +$config['smarty']['cache'] = 1; +$config['smarty']['cache_lifetime'] = 30; ?> diff --git a/public/include/smarty.inc.php b/public/include/smarty.inc.php index 8a320581..46a38750 100644 --- a/public/include/smarty.inc.php +++ b/public/include/smarty.inc.php @@ -20,6 +20,7 @@ $smarty->template_dir = BASEPATH . 'templates/' . THEME . '/'; $smarty->compile_dir = BASEPATH . 'templates/compile/'; // Optional smarty caching, check Smarty documentation for details -$smarty->caching = $config['cache']; +$smarty->caching = $config['smarty']['cache']; +$smarty->cache_lifetime = $config['smarty']['cache_lifetime']; $smarty->cache_dir = BASEPATH . "templates/cache"; ?> From da0ab75d5de348040e79ecf1d96dca957ff7f9a3 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 4 Jul 2013 08:48:19 +0200 Subject: [PATCH 021/137] adjust smarty configurations --- public/include/smarty.inc.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/public/include/smarty.inc.php b/public/include/smarty.inc.php index 46a38750..e42e56e3 100644 --- a/public/include/smarty.inc.php +++ b/public/include/smarty.inc.php @@ -20,7 +20,10 @@ $smarty->template_dir = BASEPATH . 'templates/' . THEME . '/'; $smarty->compile_dir = BASEPATH . 'templates/compile/'; // Optional smarty caching, check Smarty documentation for details -$smarty->caching = $config['smarty']['cache']; -$smarty->cache_lifetime = $config['smarty']['cache_lifetime']; -$smarty->cache_dir = BASEPATH . "templates/cache"; +if ($config['smarty']['cache']) { + $debug->append('Enable smarty cache'); + $smarty->setCaching(Smarty::CACHING_LIFETIME_SAVED); + $smarty->cache_lifetime = $config['smarty']['cache_lifetime']; + $smarty->cache_dir = BASEPATH . "templates/cache"; +} ?> From 87ecfc55981bd783a54804179f85ac8d728a5de2 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 2 Jul 2013 14:08:33 +0200 Subject: [PATCH 022/137] Add detailed smarty cache documentation to config Instead of just making it availble document the smarty cache feature. It might work for users, but it's advised to rely on the memcache instead. Fixes #309 --- public/include/config/global.inc.dist.php | 1 - 1 file changed, 1 deletion(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 8e502ed1..e335405c 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -258,7 +258,6 @@ $config['cookie']['path'] = '/'; $config['cookie']['name'] = 'POOLERCOOKIE'; $config['cookie']['domain'] = ''; - /** * Enable or disable the Smarty cache * From 9aeb0052014cd72871b454f63995eda80e8748df Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sat, 6 Jul 2013 18:10:23 +0200 Subject: [PATCH 023/137] Adding `{nocache}` flags for dynamic content This will update content instantly once the user changes it and not load a cached version from the smarty cache. Addresses #309 --- public/templates/mmcFE/account/edit/default.tpl | 10 +++++----- .../templates/mmcFE/account/notifications/default.tpl | 8 ++++---- public/templates/mmcFE/account/workers/default.tpl | 2 ++ public/templates/mmcFE/admin/news/default.tpl | 2 ++ public/templates/mmcFE/admin/news_edit/default.tpl | 6 +++--- public/templates/mmcFE/admin/settings/default.tpl | 4 ++-- public/templates/mmcFE/admin/user/default.tpl | 2 ++ 7 files changed, 20 insertions(+), 14 deletions(-) diff --git a/public/templates/mmcFE/account/edit/default.tpl b/public/templates/mmcFE/account/edit/default.tpl index 482c3579..c15b56d4 100644 --- a/public/templates/mmcFE/account/edit/default.tpl +++ b/public/templates/mmcFE/account/edit/default.tpl @@ -7,9 +7,9 @@ Username: {$GLOBAL.userdata.username} User Id: {$GLOBAL.userdata.id} API Key: {$GLOBAL.userdata.api_key} - E-Mail: - Payment Address: - Donation %: [donation amount in percent (example: 0.5)] + E-Mail: + Payment Address: + Donation %: [donation amount in percent (example: 0.5)] Automatic Payout Threshold: [{$GLOBAL.config.ap_threshold.min}-{$GLOBAL.config.ap_threshold.max} {$GLOBAL.config.currency}. Set to '0' for no auto payout] 4 digit PIN: [The 4 digit PIN you chose when registering] @@ -23,8 +23,8 @@ - - + +
    Account Balance:    {$GLOBAL.userdata.balance.confirmed|escape} {$GLOBAL.config.currency}
    Payout to:
    {$GLOBAL.userdata.coin_address|escape}
    Account Balance:    {nocache}{$GLOBAL.userdata.balance.confirmed|escape}{/nocache} {$GLOBAL.config.currency}
    Payout to:
    {nocache}{$GLOBAL.userdata.coin_address|escape}{/nocache}
    4 digit PIN:
    diff --git a/public/templates/mmcFE/account/notifications/default.tpl b/public/templates/mmcFE/account/notifications/default.tpl index 1d54729b..52140466 100644 --- a/public/templates/mmcFE/account/notifications/default.tpl +++ b/public/templates/mmcFE/account/notifications/default.tpl @@ -12,7 +12,7 @@ IDLE Worker - + @@ -20,7 +20,7 @@ New Blocks - + @@ -28,7 +28,7 @@ Auto Payout - + @@ -36,7 +36,7 @@ Manual Payout - + diff --git a/public/templates/mmcFE/account/workers/default.tpl b/public/templates/mmcFE/account/workers/default.tpl index c69d1139..f26e3109 100644 --- a/public/templates/mmcFE/account/workers/default.tpl +++ b/public/templates/mmcFE/account/workers/default.tpl @@ -15,6 +15,7 @@     + {nocache} {section worker $WORKERS} {assign var="username" value="."|escape|explode:$WORKERS[worker].username:2} @@ -29,6 +30,7 @@ {/section} + {/nocache} diff --git a/public/templates/mmcFE/admin/news/default.tpl b/public/templates/mmcFE/admin/news/default.tpl index 7bd8c198..8c3ac5d1 100644 --- a/public/templates/mmcFE/admin/news/default.tpl +++ b/public/templates/mmcFE/admin/news/default.tpl @@ -17,6 +17,7 @@ {include file="global/block_footer.tpl"} +{nocache} {section name=news loop=$NEWS} {include file="global/block_header.tpl" @@ -35,4 +36,5 @@ {include file="global/block_footer.tpl"} {/section} +{/nocache} {include file="global/block_footer.tpl"} diff --git a/public/templates/mmcFE/admin/news_edit/default.tpl b/public/templates/mmcFE/admin/news_edit/default.tpl index 467d4a6c..22e945fb 100644 --- a/public/templates/mmcFE/admin/news_edit/default.tpl +++ b/public/templates/mmcFE/admin/news_edit/default.tpl @@ -10,7 +10,7 @@ Active - + @@ -18,13 +18,13 @@ Header - + Content - + diff --git a/public/templates/mmcFE/admin/settings/default.tpl b/public/templates/mmcFE/admin/settings/default.tpl index 3cd67f4e..a2ffbd5b 100644 --- a/public/templates/mmcFE/admin/settings/default.tpl +++ b/public/templates/mmcFE/admin/settings/default.tpl @@ -16,7 +16,7 @@ @@ -26,7 +26,7 @@ diff --git a/public/templates/mmcFE/admin/user/default.tpl b/public/templates/mmcFE/admin/user/default.tpl index 82955741..8c65c49f 100644 --- a/public/templates/mmcFE/admin/user/default.tpl +++ b/public/templates/mmcFE/admin/user/default.tpl @@ -44,6 +44,7 @@ +{nocache} {section name=user loop=$USERS|default} {$USERS[user].id} @@ -70,6 +71,7 @@ {/section} +{/nocache} From 4ea33a5e50a08bf0daa6b285bc06e37d9b87efa1 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sat, 6 Jul 2013 18:36:11 +0200 Subject: [PATCH 024/137] Adding cache detection to many pages This will allow pages to skip loading data from backends like the database or the wallet RPC server. If a cached page is detected and valid, all dynamic content generation will be skipped completely. Other pages that have not been adjusted in this commit will still fetch backend data all the time. This will ensure clients always see the most recent data, like worker information or account changes. This should fix #309 completely but needs some testing. --- .../include/pages/admin/transactions.inc.php | 16 +- public/include/pages/admin/wallet.inc.php | 17 +- public/include/pages/home.inc.php | 17 +- public/include/pages/statistics.inc.php | 26 ++- .../include/pages/statistics/blocks.inc.php | 17 +- .../include/pages/statistics/graphs.inc.php | 19 +- public/include/pages/statistics/pool.inc.php | 97 ++++---- public/include/smarty_globals.inc.php | 209 +++++++++--------- public/templates/mmcFE/master.tpl | 2 +- 9 files changed, 234 insertions(+), 186 deletions(-) diff --git a/public/include/pages/admin/transactions.inc.php b/public/include/pages/admin/transactions.inc.php index 00345903..b25d6f15 100644 --- a/public/include/pages/admin/transactions.inc.php +++ b/public/include/pages/admin/transactions.inc.php @@ -2,10 +2,20 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -if ($user->isAuthenticated()) { + +// 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', md5(serialize($_REQUEST)))) { + $debug->append('No cached version available, fetching from backend', 3); $aTransactions = $transaction->getAllTransactions(@$_REQUEST['start']); if (!$aTransactions) $_SESSION['POPUP'][] = array('CONTENT' => 'Could not find any transaction', 'TYPE' => 'errormsg'); - $smarty->assign('TRANSACTIONS', $aTransactions); - $smarty->assign('CONTENT', 'default.tpl'); +} else { + $debug->append('Using cached page', 3); } +$smarty->assign('TRANSACTIONS', $aTransactions); +$smarty->assign('CONTENT', 'default.tpl'); ?> diff --git a/public/include/pages/admin/wallet.inc.php b/public/include/pages/admin/wallet.inc.php index 479ff919..d0642b14 100644 --- a/public/include/pages/admin/wallet.inc.php +++ b/public/include/pages/admin/wallet.inc.php @@ -9,15 +9,22 @@ if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) { die("404 Page not found"); } -if ($bitcoin->can_connect() === true){ - $dBalance = $bitcoin->query('getbalance'); +if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { + $debug->append('No cached version available, fetching from backend', 3); + if ($bitcoin->can_connect() === true){ + $dBalance = $bitcoin->query('getbalance'); + } else { + $dBalance = 0; + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg'); + } + // Fetch locked balance from transactions + $dLockedBalance = $transaction->getLockedBalance(); } else { - $dBalance = 0; - $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg'); + $debug->append('Using cached page', 3); } $smarty->assign("BALANCE", $dBalance); -$smarty->assign("LOCKED", $transaction->getLockedBalance()); +$smarty->assign("LOCKED", $dLockedBalance); // Tempalte specifics $smarty->assign("CONTENT", "default.tpl"); diff --git a/public/include/pages/home.inc.php b/public/include/pages/home.inc.php index ea4bd8fb..ac7b458e 100644 --- a/public/include/pages/home.inc.php +++ b/public/include/pages/home.inc.php @@ -6,13 +6,18 @@ if (!defined('SECURITY')) die('Hacking attempt'); // Include markdown library use \Michelf\Markdown; -// Fetch active news to display -$aNews = $news->getAllActive(); -if (is_array($aNews)) { - foreach ($aNews as $key => $aData) { - // Transform Markdown content to HTML - $aNews[$key]['content'] = Markdown::defaultTransform($aData['content']); +if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { + $debug->append('No cached version available, fetching from backend', 3); + // Fetch active news to display + $aNews = $news->getAllActive(); + if (is_array($aNews)) { + foreach ($aNews as $key => $aData) { + // Transform Markdown content to HTML + $aNews[$key]['content'] = Markdown::defaultTransform($aData['content']); + } } +} else { + $debug->append('Using cached page', 3); } // Load news entries for Desktop site and unauthenticated users diff --git a/public/include/pages/statistics.inc.php b/public/include/pages/statistics.inc.php index dc27fde6..c5035f0c 100644 --- a/public/include/pages/statistics.inc.php +++ b/public/include/pages/statistics.inc.php @@ -4,17 +4,23 @@ if (!defined('SECURITY')) die('Hacking attempt'); -if ($bitcoin->can_connect() === true){ - $dDifficulty = $bitcoin->query('getdifficulty'); - if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) - $dDifficulty = $dDifficulty['proof-of-work']; - $iBlock = $bitcoin->query('getblockcount'); +if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { + $debug->append('No cached version available, fetching from backend', 3); + if ($bitcoin->can_connect() === true){ + $dDifficulty = $bitcoin->query('getdifficulty'); + if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) + $dDifficulty = $dDifficulty['proof-of-work']; + $iBlock = $bitcoin->query('getblockcount'); + } else { + $dDifficulty = 1; + $iBlock = 0; + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg'); + } + $smarty->assign("CURRENTBLOCK", $iBlock); + $smarty->assign("DIFFICULTY", $dDifficulty); } else { - $dDifficulty = 1; - $iBlock = 0; - $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg'); + $debug->append('Using cached page', 3); } -$smarty->assign("CURRENTBLOCK", $iBlock); -$smarty->assign("DIFFICULTY", $dDifficulty); $smarty->assign("CONTENT", "default.tpl"); +?> diff --git a/public/include/pages/statistics/blocks.inc.php b/public/include/pages/statistics/blocks.inc.php index 6c3b00b8..115dfb5b 100644 --- a/public/include/pages/statistics/blocks.inc.php +++ b/public/include/pages/statistics/blocks.inc.php @@ -5,12 +5,19 @@ if (!defined('SECURITY')) die('Hacking attempt'); if (!$user->isAuthenticated()) header("Location: index.php?page=home"); // Grab the last blocks found -$iLimit = 20; -$aBlocksFoundData = $statistics->getBlocksFound($iLimit); +if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { + $debug->append('No cached version available, fetching from backend', 3); + // Grab the last blocks found + $iLimit = 20; + $aBlocksFoundData = $statistics->getBlocksFound($iLimit); -// Propagate content our template -$smarty->assign("BLOCKSFOUND", $aBlocksFoundData); -$smarty->assign("BLOCKLIMIT", $iLimit); + // Propagate content our template + $smarty->assign("BLOCKSFOUND", $aBlocksFoundData); + $smarty->assign("BLOCKLIMIT", $iLimit); + +} else { + $debug->append('Using cached page', 3); +} $smarty->assign("CONTENT", "default.tpl"); ?> diff --git a/public/include/pages/statistics/graphs.inc.php b/public/include/pages/statistics/graphs.inc.php index f7016835..4d31aadb 100644 --- a/public/include/pages/statistics/graphs.inc.php +++ b/public/include/pages/statistics/graphs.inc.php @@ -1,16 +1,19 @@ isAuthenticated()) { - $aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA']['id']); - $aPoolHourlyHashRates = $statistics->getHourlyHashrateByPool(); +if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { + $debug->append('No cached version available, fetching from backend', 3); + if ($user->isAuthenticated()) { + $aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA']['id']); + $aPoolHourlyHashRates = $statistics->getHourlyHashrateByPool(); + } + $smarty->assign("YOURHASHRATES", @$aHourlyHashRates); + $smarty->assign("POOLHASHRATES", @$aPoolHourlyHashRates); +} else { + $debug->append('Using cached page', 3); } -// Propagate content our template -$smarty->assign("YOURHASHRATES", @$aHourlyHashRates); -$smarty->assign("POOLHASHRATES", @$aPoolHourlyHashRates); $smarty->assign("CONTENT", "default.tpl"); ?> diff --git a/public/include/pages/statistics/pool.inc.php b/public/include/pages/statistics/pool.inc.php index 1cab1009..1e79658c 100644 --- a/public/include/pages/statistics/pool.inc.php +++ b/public/include/pages/statistics/pool.inc.php @@ -4,55 +4,60 @@ if (!defined('SECURITY')) die('Hacking attempt'); -// Fetch data from wallet -if ($bitcoin->can_connect() === true){ - $dDifficulty = $bitcoin->getdifficulty(); - if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) - $dDifficulty = $dDifficulty['proof-of-work']; - $iBlock = $bitcoin->getblockcount(); +if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { + $debug->append('No cached version available, fetching from backend', 3); + // Fetch data from wallet + if ($bitcoin->can_connect() === true){ + $dDifficulty = $bitcoin->getdifficulty(); + if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) + $dDifficulty = $dDifficulty['proof-of-work']; + $iBlock = $bitcoin->getblockcount(); + } else { + $dDifficulty = 1; + $iBlock = 0; + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg'); + } + + // Top share contributors + $aContributorsShares = $statistics->getTopContributors('shares', 15); + + // Top hash contributors + $aContributorsHashes = $statistics->getTopContributors('hashes', 15); + + // Grab the last 10 blocks found + $iLimit = 5; + $aBlocksFoundData = $statistics->getBlocksFound($iLimit); + count($aBlocksFoundData) > 0 ? $aBlockData = $aBlocksFoundData[0] : $aBlockData = array(); + + // Estimated time to find the next block + $iCurrentPoolHashrate = $statistics->getCurrentHashrate(); + + // Time in seconds, not hours, using modifier in smarty to translate + $iCurrentPoolHashrate > 0 ? $iEstTime = $dDifficulty * pow(2,32) / ($iCurrentPoolHashrate * 1000) : $iEstTime = 0; + + // Time since last block + $now = new DateTime( "now" ); + if (!empty($aBlockData)) { + $dTimeSinceLast = ($now->getTimestamp() - $aBlockData['time']); + } else { + $dTimeSinceLast = 0; + } + + // Propagate content our template + $smarty->assign("ESTTIME", $iEstTime); + $smarty->assign("TIMESINCELAST", $dTimeSinceLast); + $smarty->assign("BLOCKSFOUND", $aBlocksFoundData); + $smarty->assign("BLOCKLIMIT", $iLimit); + $smarty->assign("CONTRIBSHARES", $aContributorsShares); + $smarty->assign("CONTRIBHASHES", $aContributorsHashes); + $smarty->assign("CURRENTBLOCK", $iBlock); + count($aBlockData) > 0 ? $smarty->assign("LASTBLOCK", $aBlockData['height']) : $smarty->assign("LASTBLOCK", 0); + $smarty->assign("DIFFICULTY", $dDifficulty); + $smarty->assign("REWARD", $config['reward']); } else { - $dDifficulty = 1; - $iBlock = 0; - $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg'); + $debug->append('Using cached page', 3); } -// Top share contributors -$aContributorsShares = $statistics->getTopContributors('shares', 15); - -// Top hash contributors -$aContributorsHashes = $statistics->getTopContributors('hashes', 15); - -// Grab the last 10 blocks found -$iLimit = 5; -$aBlocksFoundData = $statistics->getBlocksFound($iLimit); -count($aBlocksFoundData) > 0 ? $aBlockData = $aBlocksFoundData[0] : $aBlockData = array(); - -// Estimated time to find the next block -$iCurrentPoolHashrate = $statistics->getCurrentHashrate(); - -// Time in seconds, not hours, using modifier in smarty to translate -$iCurrentPoolHashrate > 0 ? $iEstTime = $dDifficulty * pow(2,32) / ($iCurrentPoolHashrate * 1000) : $iEstTime = 0; - -// Time since last block -$now = new DateTime( "now" ); -if (!empty($aBlockData)) { - $dTimeSinceLast = ($now->getTimestamp() - $aBlockData['time']); -} else { - $dTimeSinceLast = 0; -} - -// Propagate content our template -$smarty->assign("ESTTIME", $iEstTime); -$smarty->assign("TIMESINCELAST", $dTimeSinceLast); -$smarty->assign("BLOCKSFOUND", $aBlocksFoundData); -$smarty->assign("BLOCKLIMIT", $iLimit); -$smarty->assign("CONTRIBSHARES", $aContributorsShares); -$smarty->assign("CONTRIBHASHES", $aContributorsHashes); -$smarty->assign("CURRENTBLOCK", $iBlock); -count($aBlockData) > 0 ? $smarty->assign("LASTBLOCK", $aBlockData['height']) : $smarty->assign("LASTBLOCK", 0); -$smarty->assign("DIFFICULTY", $dDifficulty); -$smarty->assign("REWARD", $config['reward']); - if ($user->isAuthenticated()) { $smarty->assign("CONTENT", "authenticated.tpl"); } else { diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index 147b38e9..0f1e2896 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -7,111 +7,116 @@ if (!defined('SECURITY')) // Globally available variables $debug->append('Global smarty variables', 3); -// Defaults to get rid of PHP Notice warnings -$dDifficulty = 1; -$aRoundShares = 1; +if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { + $debug->append('No cached page detected, loading smarty globals', 3); + // Defaults to get rid of PHP Notice warnings + $dDifficulty = 1; + $aRoundShares = 1; -// Only run these if the user is logged in -if (@$_SESSION['AUTHENTICATED']) { - $aRoundShares = $statistics->getRoundShares(); - if ($bitcoin->can_connect() === true) { - $dDifficulty = $bitcoin->query('getdifficulty'); - if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) - $dDifficulty = $dDifficulty['proof-of-work']; - } -} -// Always fetch this since we need for ministats header -$bitcoin->can_connect() === true ? $dNetworkHashrate = $bitcoin->query('getnetworkhashps') : $dNetworkHashrate = 0; - -// Fetch some data -$iCurrentActiveWorkers = $worker->getCountAllActiveWorkers(); -$iCurrentPoolHashrate = $statistics->getCurrentHashrate(); -$iCurrentPoolShareRate = $statistics->getCurrentShareRate(); - -// Avoid confusion, ensure our nethash isn't higher than poolhash -if ($iCurrentPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $iCurrentPoolHashrate; - -// Global data for Smarty -$aGlobal = array( - 'slogan' => $config['website']['slogan'], - 'websitename' => $config['website']['name'], - 'hashrate' => $iCurrentPoolHashrate, - 'nethashrate' => $dNetworkHashrate, - 'sharerate' => $iCurrentPoolShareRate, - 'workers' => $iCurrentActiveWorkers, - 'roundshares' => $aRoundShares, - 'fees' => $config['fees'], - 'confirmations' => $config['confirmations'], - 'reward' => $config['reward'], - 'price' => $setting->getValue('price'), - 'blockexplorer' => $config['blockexplorer'], - 'chaininfo' => $config['chaininfo'], - 'config' => array( - 'website' => array( 'title' => $config['website']['title'] ), - 'price' => array( 'currency' => $config['price']['currency'] ), - 'targetdiff' => $config['difficulty'], - 'currency' => $config['currency'], - 'txfee' => $config['txfee'], - 'payout_system' => $config['payout_system'], - 'ap_threshold' => array( - 'min' => $config['ap_threshold']['min'], - 'max' => $config['ap_threshold']['max'] - ) - ) -); - -// Special calculations for PPS Values based on reward_type setting and/or available blocks -if ($config['reward_type'] != 'block') { - $aGlobal['ppsvalue'] = number_format(round(50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); -} else { - // Try to find the last block value and use that for future payouts, revert to fixed reward if none found - if ($aLastBlock = $block->getLast()) { - $aGlobal['ppsvalue'] = number_format(round($aLastBlock['amount'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); - } else { - $aGlobal['ppsvalue'] = number_format(round($config['reward'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); - } -} - -// We don't want these session infos cached -if (@$_SESSION['USERDATA']['id']) { - $aGlobal['userdata'] = $_SESSION['USERDATA']['id'] ? $user->getUserData($_SESSION['USERDATA']['id']) : array(); - $aGlobal['userdata']['balance'] = $transaction->getBalance($_SESSION['USERDATA']['id']); - - // Other userdata that we can cache savely - $aGlobal['userdata']['shares'] = $statistics->getUserShares($_SESSION['USERDATA']['id']); - $aGlobal['userdata']['hashrate'] = $statistics->getUserHashrate($_SESSION['USERDATA']['id']); - $aGlobal['userdata']['sharerate'] = $statistics->getUserSharerate($_SESSION['USERDATA']['id']); - - switch ($config['payout_system']) { - case 'pps': - break; - default: - // Some estimations - if (@$aRoundShares['valid'] > 0) { - $aGlobal['userdata']['est_block'] = round(( (int)$aGlobal['userdata']['shares']['valid'] / (int)$aRoundShares['valid'] ) * (float)$config['reward'], 8); - $aGlobal['userdata']['est_fee'] = round(((float)$config['fees'] / 100) * (float)$aGlobal['userdata']['est_block'], 8); - $aGlobal['userdata']['est_donation'] = round((( (float)$aGlobal['userdata']['donate_percent'] / 100) * ((float)$aGlobal['userdata']['est_block'] - (float)$aGlobal['userdata']['est_fee'])), 8); - $aGlobal['userdata']['est_payout'] = round((float)$aGlobal['userdata']['est_block'] - (float)$aGlobal['userdata']['est_donation'] - (float)$aGlobal['userdata']['est_fee'], 8); - } else { - $aGlobal['userdata']['est_block'] = 0; - $aGlobal['userdata']['est_fee'] = 0; - $aGlobal['userdata']['est_donation'] = 0; - $aGlobal['userdata']['est_payout'] = 0; + // Only run these if the user is logged in + if (@$_SESSION['AUTHENTICATED']) { + $aRoundShares = $statistics->getRoundShares(); + if ($bitcoin->can_connect() === true) { + $dDifficulty = $bitcoin->query('getdifficulty'); + if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) + $dDifficulty = $dDifficulty['proof-of-work']; + } + } + // Always fetch this since we need for ministats header + $bitcoin->can_connect() === true ? $dNetworkHashrate = $bitcoin->query('getnetworkhashps') : $dNetworkHashrate = 0; + + // Fetch some data + $iCurrentActiveWorkers = $worker->getCountAllActiveWorkers(); + $iCurrentPoolHashrate = $statistics->getCurrentHashrate(); + $iCurrentPoolShareRate = $statistics->getCurrentShareRate(); + + // Avoid confusion, ensure our nethash isn't higher than poolhash + if ($iCurrentPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $iCurrentPoolHashrate; + + // Global data for Smarty + $aGlobal = array( + 'slogan' => $config['website']['slogan'], + 'websitename' => $config['website']['name'], + 'hashrate' => $iCurrentPoolHashrate, + 'nethashrate' => $dNetworkHashrate, + 'sharerate' => $iCurrentPoolShareRate, + 'workers' => $iCurrentActiveWorkers, + 'roundshares' => $aRoundShares, + 'fees' => $config['fees'], + 'confirmations' => $config['confirmations'], + 'reward' => $config['reward'], + 'price' => $setting->getValue('price'), + 'blockexplorer' => $config['blockexplorer'], + 'chaininfo' => $config['chaininfo'], + 'config' => array( + 'website' => array( 'title' => $config['website']['title'] ), + 'price' => array( 'currency' => $config['price']['currency'] ), + 'targetdiff' => $config['difficulty'], + 'currency' => $config['currency'], + 'txfee' => $config['txfee'], + 'payout_system' => $config['payout_system'], + 'ap_threshold' => array( + 'min' => $config['ap_threshold']['min'], + 'max' => $config['ap_threshold']['max'] + ) + ) + ); + + // Special calculations for PPS Values based on reward_type setting and/or available blocks + if ($config['reward_type'] != 'block') { + $aGlobal['ppsvalue'] = number_format(round(50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); + } else { + // Try to find the last block value and use that for future payouts, revert to fixed reward if none found + if ($aLastBlock = $block->getLast()) { + $aGlobal['ppsvalue'] = number_format(round($aLastBlock['amount'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); + } else { + $aGlobal['ppsvalue'] = number_format(round($config['reward'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); } - break; } - // Site-wide notifications, based on user events - if ($aGlobal['userdata']['balance']['confirmed'] >= $config['ap_threshold']['max']) - $_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded the pools configured ' . $config['currency'] . ' warning threshold. Please initiate a transfer!', 'TYPE' => 'warning'); - if ($user->getUserFailed($_SESSION['USERDATA']['id']) > 0) - $_SESSION['POPUP'][] = array('CONTENT' => 'You have ' . $user->getUserFailed($_SESSION['USERDATA']['id']) . ' failed login attempts! Reset Counter', 'TYPE' => 'errormsg'); + // We don't want these session infos cached + if (@$_SESSION['USERDATA']['id']) { + $aGlobal['userdata'] = $_SESSION['USERDATA']['id'] ? $user->getUserData($_SESSION['USERDATA']['id']) : array(); + $aGlobal['userdata']['balance'] = $transaction->getBalance($_SESSION['USERDATA']['id']); + + // Other userdata that we can cache savely + $aGlobal['userdata']['shares'] = $statistics->getUserShares($_SESSION['USERDATA']['id']); + $aGlobal['userdata']['hashrate'] = $statistics->getUserHashrate($_SESSION['USERDATA']['id']); + $aGlobal['userdata']['sharerate'] = $statistics->getUserSharerate($_SESSION['USERDATA']['id']); + + switch ($config['payout_system']) { + case 'pps': + break; + default: + // Some estimations + if (@$aRoundShares['valid'] > 0) { + $aGlobal['userdata']['est_block'] = round(( (int)$aGlobal['userdata']['shares']['valid'] / (int)$aRoundShares['valid'] ) * (float)$config['reward'], 8); + $aGlobal['userdata']['est_fee'] = round(((float)$config['fees'] / 100) * (float)$aGlobal['userdata']['est_block'], 8); + $aGlobal['userdata']['est_donation'] = round((( (float)$aGlobal['userdata']['donate_percent'] / 100) * ((float)$aGlobal['userdata']['est_block'] - (float)$aGlobal['userdata']['est_fee'])), 8); + $aGlobal['userdata']['est_payout'] = round((float)$aGlobal['userdata']['est_block'] - (float)$aGlobal['userdata']['est_donation'] - (float)$aGlobal['userdata']['est_fee'], 8); + } else { + $aGlobal['userdata']['est_block'] = 0; + $aGlobal['userdata']['est_fee'] = 0; + $aGlobal['userdata']['est_donation'] = 0; + $aGlobal['userdata']['est_payout'] = 0; + } + break; + } + + // Site-wide notifications, based on user events + if ($aGlobal['userdata']['balance']['confirmed'] >= $config['ap_threshold']['max']) + $_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded your accounts balance. Please transfer some ' . $config['currency'] . "!", 'TYPE' => 'errormsg'); + if ($user->getUserFailed($_SESSION['USERDATA']['id']) > 0) + $_SESSION['POPUP'][] = array('CONTENT' => 'You have ' . $user->getUserFailed($_SESSION['USERDATA']['id']) . ' failed login attempts! Reset Counter', 'TYPE' => 'errormsg'); + } + + if ($setting->getValue('maintenance')) + $_SESSION['POPUP'][] = array('CONTENT' => 'This pool is currently in maintenance mode.', 'TYPE' => 'warning'); + + // Make it available in Smarty + $smarty->assign('PATH', 'site_assets/' . THEME); + $smarty->assign('GLOBAL', $aGlobal); +} else { + $debug->append('We found a cached page, not loaded smarty globals', 3); } - -if ($setting->getValue('maintenance')) - $_SESSION['POPUP'][] = array('CONTENT' => 'This pool is currently in maintenance mode.', 'TYPE' => 'warning'); - -// Make it available in Smarty -$smarty->assign('PATH', 'site_assets/' . THEME); -$smarty->assign('GLOBAL', $aGlobal); ?> diff --git a/public/templates/mmcFE/master.tpl b/public/templates/mmcFE/master.tpl index f476f747..68777d1a 100644 --- a/public/templates/mmcFE/master.tpl +++ b/public/templates/mmcFE/master.tpl @@ -69,7 +69,7 @@
    - {include file="system/debugger.tpl"} + {nocache}{include file="system/debugger.tpl"}{/nocache}
    From bb0e9dff3915c3db9f109b74304d6b5ccf234c62 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 8 Jul 2013 14:16:45 +0200 Subject: [PATCH 025/137] disable smarty cache by default in dist --- public/include/config/global.inc.dist.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index e335405c..49692646 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -288,6 +288,6 @@ $config['cookie']['domain'] = ''; * cache = 0, disabled * cache_lifetime = 30 seconds **/ -$config['smarty']['cache'] = 1; +$config['smarty']['cache'] = 0; $config['smarty']['cache_lifetime'] = 30; ?> From d25387f0b597923ff4e8b7b71943c84279535c72 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 8 Jul 2013 14:16:52 +0200 Subject: [PATCH 026/137] Disable caching check on Smarty globals This will ensure data is available for those pages relying on global data. A better step might be to load template specific data always on the pages that require the data instead of relying on global data to be available. Fixes #309 --- public/include/smarty_globals.inc.php | 216 +++++++++++++------------- 1 file changed, 106 insertions(+), 110 deletions(-) diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index 0f1e2896..8f543699 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -2,121 +2,117 @@ // Make sure we are called from index.php if (!defined('SECURITY')) - die('Hacking attempt'); + die('Hacking attempt'); // Globally available variables $debug->append('Global smarty variables', 3); -if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { - $debug->append('No cached page detected, loading smarty globals', 3); - // Defaults to get rid of PHP Notice warnings - $dDifficulty = 1; - $aRoundShares = 1; +$debug->append('No cached page detected, loading smarty globals', 3); +// Defaults to get rid of PHP Notice warnings +$dDifficulty = 1; +$aRoundShares = 1; - // Only run these if the user is logged in - if (@$_SESSION['AUTHENTICATED']) { - $aRoundShares = $statistics->getRoundShares(); - if ($bitcoin->can_connect() === true) { - $dDifficulty = $bitcoin->query('getdifficulty'); - if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) - $dDifficulty = $dDifficulty['proof-of-work']; - } +// Only run these if the user is logged in +if (@$_SESSION['AUTHENTICATED']) { + $aRoundShares = $statistics->getRoundShares(); + if ($bitcoin->can_connect() === true) { + $dDifficulty = $bitcoin->query('getdifficulty'); + if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) + $dDifficulty = $dDifficulty['proof-of-work']; } - // Always fetch this since we need for ministats header - $bitcoin->can_connect() === true ? $dNetworkHashrate = $bitcoin->query('getnetworkhashps') : $dNetworkHashrate = 0; - - // Fetch some data - $iCurrentActiveWorkers = $worker->getCountAllActiveWorkers(); - $iCurrentPoolHashrate = $statistics->getCurrentHashrate(); - $iCurrentPoolShareRate = $statistics->getCurrentShareRate(); - - // Avoid confusion, ensure our nethash isn't higher than poolhash - if ($iCurrentPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $iCurrentPoolHashrate; - - // Global data for Smarty - $aGlobal = array( - 'slogan' => $config['website']['slogan'], - 'websitename' => $config['website']['name'], - 'hashrate' => $iCurrentPoolHashrate, - 'nethashrate' => $dNetworkHashrate, - 'sharerate' => $iCurrentPoolShareRate, - 'workers' => $iCurrentActiveWorkers, - 'roundshares' => $aRoundShares, - 'fees' => $config['fees'], - 'confirmations' => $config['confirmations'], - 'reward' => $config['reward'], - 'price' => $setting->getValue('price'), - 'blockexplorer' => $config['blockexplorer'], - 'chaininfo' => $config['chaininfo'], - 'config' => array( - 'website' => array( 'title' => $config['website']['title'] ), - 'price' => array( 'currency' => $config['price']['currency'] ), - 'targetdiff' => $config['difficulty'], - 'currency' => $config['currency'], - 'txfee' => $config['txfee'], - 'payout_system' => $config['payout_system'], - 'ap_threshold' => array( - 'min' => $config['ap_threshold']['min'], - 'max' => $config['ap_threshold']['max'] - ) - ) - ); - - // Special calculations for PPS Values based on reward_type setting and/or available blocks - if ($config['reward_type'] != 'block') { - $aGlobal['ppsvalue'] = number_format(round(50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); - } else { - // Try to find the last block value and use that for future payouts, revert to fixed reward if none found - if ($aLastBlock = $block->getLast()) { - $aGlobal['ppsvalue'] = number_format(round($aLastBlock['amount'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); - } else { - $aGlobal['ppsvalue'] = number_format(round($config['reward'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); - } - } - - // We don't want these session infos cached - if (@$_SESSION['USERDATA']['id']) { - $aGlobal['userdata'] = $_SESSION['USERDATA']['id'] ? $user->getUserData($_SESSION['USERDATA']['id']) : array(); - $aGlobal['userdata']['balance'] = $transaction->getBalance($_SESSION['USERDATA']['id']); - - // Other userdata that we can cache savely - $aGlobal['userdata']['shares'] = $statistics->getUserShares($_SESSION['USERDATA']['id']); - $aGlobal['userdata']['hashrate'] = $statistics->getUserHashrate($_SESSION['USERDATA']['id']); - $aGlobal['userdata']['sharerate'] = $statistics->getUserSharerate($_SESSION['USERDATA']['id']); - - switch ($config['payout_system']) { - case 'pps': - break; - default: - // Some estimations - if (@$aRoundShares['valid'] > 0) { - $aGlobal['userdata']['est_block'] = round(( (int)$aGlobal['userdata']['shares']['valid'] / (int)$aRoundShares['valid'] ) * (float)$config['reward'], 8); - $aGlobal['userdata']['est_fee'] = round(((float)$config['fees'] / 100) * (float)$aGlobal['userdata']['est_block'], 8); - $aGlobal['userdata']['est_donation'] = round((( (float)$aGlobal['userdata']['donate_percent'] / 100) * ((float)$aGlobal['userdata']['est_block'] - (float)$aGlobal['userdata']['est_fee'])), 8); - $aGlobal['userdata']['est_payout'] = round((float)$aGlobal['userdata']['est_block'] - (float)$aGlobal['userdata']['est_donation'] - (float)$aGlobal['userdata']['est_fee'], 8); - } else { - $aGlobal['userdata']['est_block'] = 0; - $aGlobal['userdata']['est_fee'] = 0; - $aGlobal['userdata']['est_donation'] = 0; - $aGlobal['userdata']['est_payout'] = 0; - } - break; - } - - // Site-wide notifications, based on user events - if ($aGlobal['userdata']['balance']['confirmed'] >= $config['ap_threshold']['max']) - $_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded your accounts balance. Please transfer some ' . $config['currency'] . "!", 'TYPE' => 'errormsg'); - if ($user->getUserFailed($_SESSION['USERDATA']['id']) > 0) - $_SESSION['POPUP'][] = array('CONTENT' => 'You have ' . $user->getUserFailed($_SESSION['USERDATA']['id']) . ' failed login attempts! Reset Counter', 'TYPE' => 'errormsg'); - } - - if ($setting->getValue('maintenance')) - $_SESSION['POPUP'][] = array('CONTENT' => 'This pool is currently in maintenance mode.', 'TYPE' => 'warning'); - - // Make it available in Smarty - $smarty->assign('PATH', 'site_assets/' . THEME); - $smarty->assign('GLOBAL', $aGlobal); -} else { - $debug->append('We found a cached page, not loaded smarty globals', 3); } +// Always fetch this since we need for ministats header +$bitcoin->can_connect() === true ? $dNetworkHashrate = $bitcoin->query('getnetworkhashps') : $dNetworkHashrate = 0; + +// Fetch some data +$iCurrentActiveWorkers = $worker->getCountAllActiveWorkers(); +$iCurrentPoolHashrate = $statistics->getCurrentHashrate(); +$iCurrentPoolShareRate = $statistics->getCurrentShareRate(); + +// Avoid confusion, ensure our nethash isn't higher than poolhash +if ($iCurrentPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $iCurrentPoolHashrate; + +// Global data for Smarty +$aGlobal = array( + 'slogan' => $config['website']['slogan'], + 'websitename' => $config['website']['name'], + 'hashrate' => $iCurrentPoolHashrate, + 'nethashrate' => $dNetworkHashrate, + 'sharerate' => $iCurrentPoolShareRate, + 'workers' => $iCurrentActiveWorkers, + 'roundshares' => $aRoundShares, + 'fees' => $config['fees'], + 'confirmations' => $config['confirmations'], + 'reward' => $config['reward'], + 'price' => $setting->getValue('price'), + 'blockexplorer' => $config['blockexplorer'], + 'chaininfo' => $config['chaininfo'], + 'config' => array( + 'website' => array( 'title' => $config['website']['title'] ), + 'price' => array( 'currency' => $config['price']['currency'] ), + 'targetdiff' => $config['difficulty'], + 'currency' => $config['currency'], + 'txfee' => $config['txfee'], + 'payout_system' => $config['payout_system'], + 'ap_threshold' => array( + 'min' => $config['ap_threshold']['min'], + 'max' => $config['ap_threshold']['max'] + ) + ) +); + +// Special calculations for PPS Values based on reward_type setting and/or available blocks +if ($config['reward_type'] != 'block') { + $aGlobal['ppsvalue'] = number_format(round(50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); +} else { + // Try to find the last block value and use that for future payouts, revert to fixed reward if none found + if ($aLastBlock = $block->getLast()) { + $aGlobal['ppsvalue'] = number_format(round($aLastBlock['amount'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); + } else { + $aGlobal['ppsvalue'] = number_format(round($config['reward'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); + } +} + +// We don't want these session infos cached +if (@$_SESSION['USERDATA']['id']) { + $aGlobal['userdata'] = $_SESSION['USERDATA']['id'] ? $user->getUserData($_SESSION['USERDATA']['id']) : array(); + $aGlobal['userdata']['balance'] = $transaction->getBalance($_SESSION['USERDATA']['id']); + + // Other userdata that we can cache savely + $aGlobal['userdata']['shares'] = $statistics->getUserShares($_SESSION['USERDATA']['id']); + $aGlobal['userdata']['hashrate'] = $statistics->getUserHashrate($_SESSION['USERDATA']['id']); + $aGlobal['userdata']['sharerate'] = $statistics->getUserSharerate($_SESSION['USERDATA']['id']); + + switch ($config['payout_system']) { + case 'pps': + break; + default: + // Some estimations + if (@$aRoundShares['valid'] > 0) { + $aGlobal['userdata']['est_block'] = round(( (int)$aGlobal['userdata']['shares']['valid'] / (int)$aRoundShares['valid'] ) * (float)$config['reward'], 8); + $aGlobal['userdata']['est_fee'] = round(((float)$config['fees'] / 100) * (float)$aGlobal['userdata']['est_block'], 8); + $aGlobal['userdata']['est_donation'] = round((( (float)$aGlobal['userdata']['donate_percent'] / 100) * ((float)$aGlobal['userdata']['est_block'] - (float)$aGlobal['userdata']['est_fee'])), 8); + $aGlobal['userdata']['est_payout'] = round((float)$aGlobal['userdata']['est_block'] - (float)$aGlobal['userdata']['est_donation'] - (float)$aGlobal['userdata']['est_fee'], 8); + } else { + $aGlobal['userdata']['est_block'] = 0; + $aGlobal['userdata']['est_fee'] = 0; + $aGlobal['userdata']['est_donation'] = 0; + $aGlobal['userdata']['est_payout'] = 0; + } + break; + } + + // Site-wide notifications, based on user events + if ($aGlobal['userdata']['balance']['confirmed'] >= $config['ap_threshold']['max']) + $_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded your accounts balance. Please transfer some ' . $config['currency'] . "!", 'TYPE' => 'errormsg'); + if ($user->getUserFailed($_SESSION['USERDATA']['id']) > 0) + $_SESSION['POPUP'][] = array('CONTENT' => 'You have ' . $user->getUserFailed($_SESSION['USERDATA']['id']) . ' failed login attempts! Reset Counter', 'TYPE' => 'errormsg'); +} + +if ($setting->getValue('maintenance')) + $_SESSION['POPUP'][] = array('CONTENT' => 'This pool is currently in maintenance mode.', 'TYPE' => 'warning'); + +// Make it available in Smarty +$smarty->assign('PATH', 'site_assets/' . THEME); +$smarty->assign('GLOBAL', $aGlobal); ?> From 308b01c700e899d614a0d5bdea36a87872aa9310 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 8 Jul 2013 15:40:21 +0200 Subject: [PATCH 027/137] Better number format for block percentages --- public/templates/mmcFE/statistics/blocks/default.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/templates/mmcFE/statistics/blocks/default.tpl b/public/templates/mmcFE/statistics/blocks/default.tpl index 539897c7..af30f093 100644 --- a/public/templates/mmcFE/statistics/blocks/default.tpl +++ b/public/templates/mmcFE/statistics/blocks/default.tpl @@ -68,7 +68,7 @@ target and network difficulty and assuming a zero variance scenario. {$BLOCKSFOUND[block].shares|number_format} {math assign="percentage" equation="shares / estshares * 100" shares=$BLOCKSFOUND[block].shares estshares=$estshares} - {$percentage} + {$percentage|number_format:"2"} {/section} From 3da5a226e16e1f7ebd1ff3e032bc2f74db7a421d Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 8 Jul 2013 15:42:23 +0200 Subject: [PATCH 028/137] Better number format for est. shares --- public/templates/mmcFE/statistics/blocks/default.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/templates/mmcFE/statistics/blocks/default.tpl b/public/templates/mmcFE/statistics/blocks/default.tpl index af30f093..f404673c 100644 --- a/public/templates/mmcFE/statistics/blocks/default.tpl +++ b/public/templates/mmcFE/statistics/blocks/default.tpl @@ -63,7 +63,7 @@ target and network difficulty and assuming a zero variance scenario. {$BLOCKSFOUND[block].amount|number_format:"2"} {math assign="estshares" equation="(pow(2,32 - targetdiff) * blockdiff)" targetdiff=$GLOBAL.config.targetdiff blockdiff=$BLOCKSFOUND[block].difficulty} - {$estshares} + {$estshares|number_format} {$BLOCKSFOUND[block].shares|number_format} From 0f69032fd3dad9d06d226db70bd14d4d9ee56838 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 8 Jul 2013 17:10:58 +0200 Subject: [PATCH 029/137] Adding 3rd party Scrypt library This will allow us to start checking a blockhash against a solution submitted to the database. Details on this in the ticket. Just a WIP to save file states. Addresses #405 --- public/include/autoloader.inc.php | 1 + public/include/lib/scrypt.php | 526 ++++++++++++++++++++++++++++++ 2 files changed, 527 insertions(+) create mode 100644 public/include/lib/scrypt.php diff --git a/public/include/autoloader.inc.php b/public/include/autoloader.inc.php index c74fa62b..68109049 100644 --- a/public/include/autoloader.inc.php +++ b/public/include/autoloader.inc.php @@ -33,3 +33,4 @@ require_once(CLASS_DIR . '/mail.class.php'); require_once(CLASS_DIR . '/notification.class.php'); require_once(CLASS_DIR . '/news.class.php'); require_once(INCLUDE_DIR . '/lib/Michelf/Markdown.php'); +require_once(INCLUDE_DIR . '/lib/scrypt.php'); diff --git a/public/include/lib/scrypt.php b/public/include/lib/scrypt.php new file mode 100644 index 00000000..87ddff2e --- /dev/null +++ b/public/include/lib/scrypt.php @@ -0,0 +1,526 @@ + 0 and a power of 2"); + } + if ($n > PHP_INT_MAX / 128 / $r) { + throw new Exception\InvalidArgumentException("Parameter n is too large"); + } + if ($r > PHP_INT_MAX / 128 / $p) { + throw new Exception\InvalidArgumentException("Parameter r is too large"); + } + + if (extension_loaded('Scrypt')) { + if ($length < 16) { + throw new Exception\InvalidArgumentException("Key length is too low, must be greater or equal to 16"); + } + return self::hex2bin(scrypt($password, $salt, $n, $r, $p, $length)); + } + + $b = Pbkdf2::calc('sha256', $password, $salt, 1, $p * 128 * $r); + + $s = ''; + for ($i = 0; $i < $p; $i++) { + $s .= self::scryptROMix(substr($b, $i * 128 * $r, 128 * $r), $n, $r); + } + + return Pbkdf2::calc('sha256', $password, $s, 1, $length); + } + + /** +* scryptROMix +* +* @param string $b +* @param integer $n +* @param integer $r +* @return string +* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-4 +*/ + protected static function scryptROMix($b, $n, $r) + { + $x = $b; + $v = array(); + for ($i = 0; $i < $n; $i++) { + $v[$i] = $x; + $x = self::scryptBlockMix($x, $r); + } + for ($i = 0; $i < $n; $i++) { + $j = self::integerify($x) % $n; + $t = $x ^ $v[$j]; + $x = self::scryptBlockMix($t, $r); + } + return $x; + } + + /** +* scryptBlockMix +* +* @param string $b +* @param integer $r +* @return string +* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-3 +*/ + protected static function scryptBlockMix($b, $r) + { + $x = substr($b, -64); + $even = ''; + $odd = ''; + $len = 2 * $r; + + for ($i = 0; $i < $len; $i++) { + if (PHP_INT_SIZE === 4) { + $x = self::salsa208Core32($x ^ substr($b, 64 * $i, 64)); + } else { + $x = self::salsa208Core64($x ^ substr($b, 64 * $i, 64)); + } + if ($i % 2 == 0) { + $even .= $x; + } else { + $odd .= $x; + } + } + return $even . $odd; + } + + /** +* Salsa 20/8 core (32 bit version) +* +* @param string $b +* @return string +* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-2 +* @see http://cr.yp.to/salsa20.html +*/ + protected static function salsa208Core32($b) + { + $b32 = array(); + for ($i = 0; $i < 16; $i++) { + list(, $b32[$i]) = unpack("V", substr($b, $i * 4, 4)); + } + + $x = $b32; + for ($i = 0; $i < 8; $i += 2) { + $a = ($x[ 0] + $x[12]); + $x[ 4] ^= ($a << 7) | ($a >> 25) & 0x7f; + $a = ($x[ 4] + $x[ 0]); + $x[ 8] ^= ($a << 9) | ($a >> 23) & 0x1ff; + $a = ($x[ 8] + $x[ 4]); + $x[12] ^= ($a << 13) | ($a >> 19) & 0x1fff; + $a = ($x[12] + $x[ 8]); + $x[ 0] ^= ($a << 18) | ($a >> 14) & 0x3ffff; + $a = ($x[ 5] + $x[ 1]); + $x[ 9] ^= ($a << 7) | ($a >> 25) & 0x7f; + $a = ($x[ 9] + $x[ 5]); + $x[13] ^= ($a << 9) | ($a >> 23) & 0x1ff; + $a = ($x[13] + $x[ 9]); + $x[ 1] ^= ($a << 13) | ($a >> 19) & 0x1fff; + $a = ($x[ 1] + $x[13]); + $x[ 5] ^= ($a << 18) | ($a >> 14) & 0x3ffff; + $a = ($x[10] + $x[ 6]); + $x[14] ^= ($a << 7) | ($a >> 25) & 0x7f; + $a = ($x[14] + $x[10]); + $x[ 2] ^= ($a << 9) | ($a >> 23) & 0x1ff; + $a = ($x[ 2] + $x[14]); + $x[ 6] ^= ($a << 13) | ($a >> 19) & 0x1fff; + $a = ($x[ 6] + $x[ 2]); + $x[10] ^= ($a << 18) | ($a >> 14) & 0x3ffff; + $a = ($x[15] + $x[11]); + $x[ 3] ^= ($a << 7) | ($a >> 25) & 0x7f; + $a = ($x[ 3] + $x[15]); + $x[ 7] ^= ($a << 9) | ($a >> 23) & 0x1ff; + $a = ($x[ 7] + $x[ 3]); + $x[11] ^= ($a << 13) | ($a >> 19) & 0x1fff; + $a = ($x[11] + $x[ 7]); + $x[15] ^= ($a << 18) | ($a >> 14) & 0x3ffff; + $a = ($x[ 0] + $x[ 3]); + $x[ 1] ^= ($a << 7) | ($a >> 25) & 0x7f; + $a = ($x[ 1] + $x[ 0]); + $x[ 2] ^= ($a << 9) | ($a >> 23) & 0x1ff; + $a = ($x[ 2] + $x[ 1]); + $x[ 3] ^= ($a << 13) | ($a >> 19) & 0x1fff; + $a = ($x[ 3] + $x[ 2]); + $x[ 0] ^= ($a << 18) | ($a >> 14) & 0x3ffff; + $a = ($x[ 5] + $x[ 4]); + $x[ 6] ^= ($a << 7) | ($a >> 25) & 0x7f; + $a = ($x[ 6] + $x[ 5]); + $x[ 7] ^= ($a << 9) | ($a >> 23) & 0x1ff; + $a = ($x[ 7] + $x[ 6]); + $x[ 4] ^= ($a << 13) | ($a >> 19) & 0x1fff; + $a = ($x[ 4] + $x[ 7]); + $x[ 5] ^= ($a << 18) | ($a >> 14) & 0x3ffff; + $a = ($x[10] + $x[ 9]); + $x[11] ^= ($a << 7) | ($a >> 25) & 0x7f; + $a = ($x[11] + $x[10]); + $x[ 8] ^= ($a << 9) | ($a >> 23) & 0x1ff; + $a = ($x[ 8] + $x[11]); + $x[ 9] ^= ($a << 13) | ($a >> 19) & 0x1fff; + $a = ($x[ 9] + $x[ 8]); + $x[10] ^= ($a << 18) | ($a >> 14) & 0x3ffff; + $a = ($x[15] + $x[14]); + $x[12] ^= ($a << 7) | ($a >> 25) & 0x7f; + $a = ($x[12] + $x[15]); + $x[13] ^= ($a << 9) | ($a >> 23) & 0x1ff; + $a = ($x[13] + $x[12]); + $x[14] ^= ($a << 13) | ($a >> 19) & 0x1fff; + $a = ($x[14] + $x[13]); + $x[15] ^= ($a << 18) | ($a >> 14) & 0x3ffff; + } + for ($i = 0; $i < 16; $i++) { + $b32[$i] = $b32[$i] + $x[$i]; + } + $result = ''; + for ($i = 0; $i < 16; $i++) { + $result .= pack("V", $b32[$i]); + } + + return $result; + } + + /** +* Salsa 20/8 core (64 bit version) +* +* @param string $b +* @return string +* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-2 +* @see http://cr.yp.to/salsa20.html +*/ + protected static function salsa208Core64($b) + { + $b32 = array(); + for ($i = 0; $i < 16; $i++) { + list(, $b32[$i]) = unpack("V", substr($b, $i * 4, 4)); + } + + $x = $b32; + for ($i = 0; $i < 8; $i += 2) { + $a = ($x[ 0] + $x[12]) & 0xffffffff; + $x[ 4] ^= ($a << 7) | ($a >> 25); + $a = ($x[ 4] + $x[ 0]) & 0xffffffff; + $x[ 8] ^= ($a << 9) | ($a >> 23); + $a = ($x[ 8] + $x[ 4]) & 0xffffffff; + $x[12] ^= ($a << 13) | ($a >> 19); + $a = ($x[12] + $x[ 8]) & 0xffffffff; + $x[ 0] ^= ($a << 18) | ($a >> 14); + $a = ($x[ 5] + $x[ 1]) & 0xffffffff; + $x[ 9] ^= ($a << 7) | ($a >> 25); + $a = ($x[ 9] + $x[ 5]) & 0xffffffff; + $x[13] ^= ($a << 9) | ($a >> 23); + $a = ($x[13] + $x[ 9]) & 0xffffffff; + $x[ 1] ^= ($a << 13) | ($a >> 19); + $a = ($x[ 1] + $x[13]) & 0xffffffff; + $x[ 5] ^= ($a << 18) | ($a >> 14); + $a = ($x[10] + $x[ 6]) & 0xffffffff; + $x[14] ^= ($a << 7) | ($a >> 25); + $a = ($x[14] + $x[10]) & 0xffffffff; + $x[ 2] ^= ($a << 9) | ($a >> 23); + $a = ($x[ 2] + $x[14]) & 0xffffffff; + $x[ 6] ^= ($a << 13) | ($a >> 19); + $a = ($x[ 6] + $x[ 2]) & 0xffffffff; + $x[10] ^= ($a << 18) | ($a >> 14); + $a = ($x[15] + $x[11]) & 0xffffffff; + $x[ 3] ^= ($a << 7) | ($a >> 25); + $a = ($x[ 3] + $x[15]) & 0xffffffff; + $x[ 7] ^= ($a << 9) | ($a >> 23); + $a = ($x[ 7] + $x[ 3]) & 0xffffffff; + $x[11] ^= ($a << 13) | ($a >> 19); + $a = ($x[11] + $x[ 7]) & 0xffffffff; + $x[15] ^= ($a << 18) | ($a >> 14); + $a = ($x[ 0] + $x[ 3]) & 0xffffffff; + $x[ 1] ^= ($a << 7) | ($a >> 25); + $a = ($x[ 1] + $x[ 0]) & 0xffffffff; + $x[ 2] ^= ($a << 9) | ($a >> 23); + $a = ($x[ 2] + $x[ 1]) & 0xffffffff; + $x[ 3] ^= ($a << 13) | ($a >> 19); + $a = ($x[ 3] + $x[ 2]) & 0xffffffff; + $x[ 0] ^= ($a << 18) | ($a >> 14); + $a = ($x[ 5] + $x[ 4]) & 0xffffffff; + $x[ 6] ^= ($a << 7) | ($a >> 25); + $a = ($x[ 6] + $x[ 5]) & 0xffffffff; + $x[ 7] ^= ($a << 9) | ($a >> 23); + $a = ($x[ 7] + $x[ 6]) & 0xffffffff; + $x[ 4] ^= ($a << 13) | ($a >> 19); + $a = ($x[ 4] + $x[ 7]) & 0xffffffff; + $x[ 5] ^= ($a << 18) | ($a >> 14); + $a = ($x[10] + $x[ 9]) & 0xffffffff; + $x[11] ^= ($a << 7) | ($a >> 25); + $a = ($x[11] + $x[10]) & 0xffffffff; + $x[ 8] ^= ($a << 9) | ($a >> 23); + $a = ($x[ 8] + $x[11]) & 0xffffffff; + $x[ 9] ^= ($a << 13) | ($a >> 19); + $a = ($x[ 9] + $x[ 8]) & 0xffffffff; + $x[10] ^= ($a << 18) | ($a >> 14); + $a = ($x[15] + $x[14]) & 0xffffffff; + $x[12] ^= ($a << 7) | ($a >> 25); + $a = ($x[12] + $x[15]) & 0xffffffff; + $x[13] ^= ($a << 9) | ($a >> 23); + $a = ($x[13] + $x[12]) & 0xffffffff; + $x[14] ^= ($a << 13) | ($a >> 19); + $a = ($x[14] + $x[13]) & 0xffffffff; + $x[15] ^= ($a << 18) | ($a >> 14); + } + for ($i = 0; $i < 16; $i++) { + $b32[$i] = ($b32[$i] + $x[$i]) & 0xffffffff; + } + $result = ''; + for ($i = 0; $i < 16; $i++) { + $result .= pack("V", $b32[$i]); + } + + return $result; + } + + /** +* Integerify +* +* Integerify (B[0] ... B[2 * r - 1]) is defined as the result +* of interpreting B[2 * r - 1] as a little-endian integer. +* Each block B is a string of 64 bytes. +* +* @param string $b +* @return integer +* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-4 +*/ + protected static function integerify($b) + { + $v = 'v'; + if (PHP_INT_SIZE === 8) { + $v = 'V'; + } + list(,$n) = unpack($v, substr($b, -64)); + return $n; + } + + /** +* Convert hex string in a binary string +* +* @param string $hex +* @return string +*/ + protected static function hex2bin($hex) + { + if (version_compare(PHP_VERSION, '5.4') >= 0) { + return hex2bin($hex); + } + $len = strlen($hex); + $result = ''; + for ($i = 0; $i < $len; $i+=2) { + $result .= chr(hexdec($hex[$i] . $hex[$i+1])); + } + return $result; + } +} + + + +/** +* Zend Framework (http://framework.zend.com/) +* +* @link http://github.com/zendframework/zf2 for the canonical source repository +* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) +* @license http://framework.zend.com/license/new-bsd New BSD License +*/ + + + + + +/** +* PKCS #5 v2.0 standard RFC 2898 +*/ +class Pbkdf2 +{ + /** +* Generate the new key +* +* @param string $hash The hash algorithm to be used by HMAC +* @param string $password The source password/key +* @param string $salt +* @param integer $iterations The number of iterations +* @param integer $length The output size +* @throws Exception\InvalidArgumentException +* @return string +*/ + public static function calc($hash, $password, $salt, $iterations, $length) + { + if (!Hmac::isSupported($hash)) { + throw new Exception\InvalidArgumentException("The hash algorithm $hash is not supported by " . __CLASS__); + } + + $num = ceil($length / Hmac::getOutputSize($hash, Hmac::OUTPUT_BINARY)); + $result = ''; + for ($block = 1; $block <= $num; $block++) { + $hmac = hash_hmac($hash, $salt . pack('N', $block), $password, Hmac::OUTPUT_BINARY); + $mix = $hmac; + for ($i = 1; $i < $iterations; $i++) { + $hmac = hash_hmac($hash, $hmac, $password, Hmac::OUTPUT_BINARY); + $mix ^= $hmac; + } + $result .= $mix; + } + return substr($result, 0, $length); + } +} + + + +/** +* Zend Framework (http://framework.zend.com/) +* +* @link http://github.com/zendframework/zf2 for the canonical source repository +* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) +* @license http://framework.zend.com/license/new-bsd New BSD License +*/ + + + +/** +* PHP implementation of the RFC 2104 Hash based Message Authentication Code +*/ +class Hmac +{ + const OUTPUT_STRING = false; + const OUTPUT_BINARY = true; + + /** +* Last algorithm supported +* +* @var string|null +*/ + protected static $lastAlgorithmSupported; + + /** +* Performs a HMAC computation given relevant details such as Key, Hashing +* algorithm, the data to compute MAC of, and an output format of String, +* or Binary. +* +* @param string $key +* @param string $hash +* @param string $data +* @param bool $output +* @throws Exception\InvalidArgumentException +* @return string +*/ + public static function compute($key, $hash, $data, $output = self::OUTPUT_STRING) + { + + if (empty($key)) { + throw new Exception\InvalidArgumentException('Provided key is null or empty'); + } + + if (!$hash || ($hash !== static::$lastAlgorithmSupported && !static::isSupported($hash))) { + throw new Exception\InvalidArgumentException( + "Hash algorithm is not supported on this PHP installation; provided '{$hash}'" + ); + } + + return hash_hmac($hash, $data, $key, $output); + } + + /** +* Get the output size according to the hash algorithm and the output format +* +* @param string $hash +* @param bool $output +* @return integer +*/ + public static function getOutputSize($hash, $output = self::OUTPUT_STRING) + { + return strlen(static::compute('key', $hash, 'data', $output)); + } + + /** +* Get the supported algorithm +* +* @return array +*/ + public static function getSupportedAlgorithms() + { + return hash_algos(); + } + + /** +* Is the hash algorithm supported? +* +* @param string $algorithm +* @return bool +*/ + public static function isSupported($algorithm) + { + if ($algorithm === static::$lastAlgorithmSupported) { + return true; + } + + if (in_array(strtolower($algorithm), hash_algos(), true)) { + static::$lastAlgorithmSupported = $algorithm; + return true; + } + + return false; + } + + /** +* Clear the cache of last algorithm supported +*/ + public static function clearLastAlgorithmCache() + { + static::$lastAlgorithmSupported = null; + } +} + function swapEndian($input){ + $output = ""; + for($i=0;$i< strlen($input);$i+=2){ + $output .= substr($input, -($i+2), 2); + + + } + return $output; + + + } + + +/*for($i=0;$i < 200;$i++){ + $value = Scrypt::calc($i, $i, 1024, 1, 1, 32); + echo "scrypt ".$i." hash:". bin2hex($value)."
    "; +}*/ +/* +$i = pack("H*", "01000000f615f7ce3b4fc6b8f61e8f89aedb1d0852507650533a9e3b10b9bbcc30639f279fcaa86746e1ef52d3edb3c4ad8259920d509bd073605c9bf1d59983752a6b06b817bb4ea78e011d012d59d4"); + +$value = Scrypt::calc($i, $i, 1024, 1, 1, 32); + echo "scrypt ".$i." hash:". bin2hex($value)."
    "; + print_r( swapEndian(bin2hex($value))); + */ + + +?> From 4c531360c1e011e4f745feb1c8852d854c6c437e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 08:54:20 +0200 Subject: [PATCH 030/137] fixing empty variable when using cache --- public/include/pages/home.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/pages/home.inc.php b/public/include/pages/home.inc.php index ac7b458e..9431e219 100644 --- a/public/include/pages/home.inc.php +++ b/public/include/pages/home.inc.php @@ -16,11 +16,11 @@ if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { $aNews[$key]['content'] = Markdown::defaultTransform($aData['content']); } } + $smarty->assign("NEWS", $aNews); } else { $debug->append('Using cached page', 3); } // Load news entries for Desktop site and unauthenticated users -$smarty->assign("NEWS", $aNews); $smarty->assign("CONTENT", "default.tpl"); ?> From 7f32bbb7bac256137d76814dbe9c01c40e2875dd Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 08:58:46 +0200 Subject: [PATCH 031/137] Adding more descriptive message for txfee Fixes #400 --- public/include/config/global.inc.dist.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index c5a298c7..9390b534 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -133,7 +133,19 @@ $config['recaptcha']['private_key'] = 'YOUR_PRIVATE_RECAPTCHA_KEY'; // Currency system used in this pool, default: `LTC` $config['currency'] = 'LTC'; -// Default transaction fee, added by RPC server, default: 0.1 +/** + * Default transaction fee to apply to user transactions + * + * Explanation + * The coin daemon applies transcation fees to young coins. + * Since we are unable to find out what the exact fee was we set + * a default value here which is applied to both manual and auto payouts. + * If this is not set, no fee is applied in the transactions history but + * the user might still see them when the coins arrive. + * + * Default: + * txfee = 0.1 + **/ $config['txfee'] = 0.1; // Payout a block bonus to block finders, default: 0 (disabled) From f6b350370d9488314b4d3033844af36a4f64e92b Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 11:27:20 +0200 Subject: [PATCH 032/137] Adding solution detections for blocks This will finally fix all block finding issues with a 4 way detection. The find upstream method will continue to try other ways to find a proper share until they are all exhausted or a match was found. * Use stratum solution, create scrypt hash from block header * Use pushpoold solution, create solution string from block header * Use first available upstream share in timerange of block time * Use *any* first available valid share older than time of block This will fix #405 - no more unknown blocks. Ever. --- cronjobs/findblock.php | 3 +- public/include/classes/share.class.php | 44 +++++++++++++++++++++++--- public/include/lib/scrypt.php | 10 ++++++ 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/cronjobs/findblock.php b/cronjobs/findblock.php index 28dddb4b..5c168448 100755 --- a/cronjobs/findblock.php +++ b/cronjobs/findblock.php @@ -72,7 +72,8 @@ if (empty($aAllBlocks)) { foreach ($aAllBlocks as $iIndex => $aBlock) { if (empty($aBlock['share_id'])) { // Fetch this blocks upstream ID - if ($share->setUpstream($block->getLastUpstreamId(), $aBlock['time'])) { + $aBlockInfo = $bitcoin->query('getblock', $aBlock['blockhash']); + if ($share->setUpstream($aBlockInfo, $block->getLastUpstreamId())) { $iCurrentUpstreamId = $share->getUpstreamId(); $iAccountId = $user->getUserId($share->getUpstreamFinder()); } else { diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index 5e4db85e..ca5b1478 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -177,7 +177,41 @@ class Share { * @param last int Skips all shares up to last to find new share * @return bool **/ - public function setUpstream($last=0, $time=0) { + public function setUpstream($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'])); + $merkleroot = pack("H*", swapEndian($aBlock['merkleroot']) ); + $time = pack("I*", $aBlock['time']); + $bits = pack("H*", swapEndian($aBlock['bits'])); + $nonce = pack("I*", $aBlock['nonce']); + $header_bin = $version . $previousblockhash . $merkleroot . $time . $bits . $nonce; + $header_hex = implode(unpack("H*", $header_bin)); + $scrypt_hash = swapEndian(bin2hex(Scrypt::calc($header_bin, $header_bin, 1024, 1, 1, 32))); + + + // Fallback to pushpoold solution type + $ppheader = sprintf('%08d', $aBlock['version']) . word_reverse($aBlock['previousblockhash']) . word_reverse($aBlock['merkleroot']) . dechex($aBlock['time']) . $aBlock['bits'] . dechex($aBlock['nonce']); + echo "ppheader : $ppheader \n"; + echo "header : $header_hex \n"; + echo "Scrypt hash : $scrypt_hash \n"; + + $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution = ? LIMIT 1"); + if ($this->checkStmt($stmt) && $stmt->bind_param('s', $scrypt_hash) && $stmt->execute() && $result = $stmt->get_result()->num_rows > 0) { + $this->oUpstream = $result->fetch_object(); + if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) + return true; + } + + // Failed to fetch via startum solution, try pushpoold + $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution LIKE CONCAT(?, '%') LIMIT 1"); + if ($this->checkStmt($stmt) && $stmt->bind_param('s', $ppheader) && $stmt->execute() && $result = $stmt->get_result()) { + $this->oUpstream = $result->fetch_object(); + if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) + return true; + } + + // Still no match, try upstream result with timerange $stmt = $this->mysqli->prepare(" SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id @@ -185,13 +219,15 @@ class Share { WHERE upstream_result = 'Y' AND id > ? AND UNIX_TIMESTAMP(time) >= ? + AND UNIX_TIMESTAMP(time) <= ? + 60 ORDER BY id ASC LIMIT 1"); - if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $last, $time) && $stmt->execute() && $result = $stmt->get_result()) { + if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $last, $aBlock['time'], $Block['time']) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) return true; } - // First attempt failed, we do a fallback with any share available for now + + // We failed again, now we take ANY result matching the timestamp $stmt = $this->mysqli->prepare(" SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id @@ -200,7 +236,7 @@ class Share { AND id > ? AND UNIX_TIMESTAMP(time) >= ? ORDER BY id ASC LIMIT 1"); - if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $last, $time) && $stmt->execute() && $result = $stmt->get_result()) { + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $last, $aBlock['time']) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) return true; diff --git a/public/include/lib/scrypt.php b/public/include/lib/scrypt.php index 87ddff2e..3d501b7d 100644 --- a/public/include/lib/scrypt.php +++ b/public/include/lib/scrypt.php @@ -523,4 +523,14 @@ $value = Scrypt::calc($i, $i, 1024, 1, 1, 32); */ +// Function used for pushpoold solution checks +function word_reverse($str) { + $ret = ''; + while (strlen($str) > 0) { + $ret .= substr($str, -8, 8); + $str = substr($str, 0, -8); + } + return $ret; +} + ?> From e4f853716533af078ee8be8c7480a909791203f8 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 11:34:49 +0200 Subject: [PATCH 033/137] removing debug output --- public/include/classes/share.class.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index ca5b1478..4fea43c5 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -189,12 +189,8 @@ class Share { $header_hex = implode(unpack("H*", $header_bin)); $scrypt_hash = swapEndian(bin2hex(Scrypt::calc($header_bin, $header_bin, 1024, 1, 1, 32))); - // Fallback to pushpoold solution type $ppheader = sprintf('%08d', $aBlock['version']) . word_reverse($aBlock['previousblockhash']) . word_reverse($aBlock['merkleroot']) . dechex($aBlock['time']) . $aBlock['bits'] . dechex($aBlock['nonce']); - echo "ppheader : $ppheader \n"; - echo "header : $header_hex \n"; - echo "Scrypt hash : $scrypt_hash \n"; $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution = ? LIMIT 1"); if ($this->checkStmt($stmt) && $stmt->bind_param('s', $scrypt_hash) && $stmt->execute() && $result = $stmt->get_result()->num_rows > 0) { From 7f759708c8b77814823007ba2bcec680eab8359c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 11:43:12 +0200 Subject: [PATCH 034/137] Adding share type to log output This will display which detection mechanism caught the share. Fixed stratum detection Fixes #405 --- cronjobs/findblock.php | 5 +++-- public/include/classes/share.class.php | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/cronjobs/findblock.php b/cronjobs/findblock.php index 5c168448..b98bbc3a 100755 --- a/cronjobs/findblock.php +++ b/cronjobs/findblock.php @@ -68,7 +68,7 @@ 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"); + $log->logInfo("Block ID\t\tHeight\tAmount\tShare ID\tShares\tFinder\tType"); foreach ($aAllBlocks as $iIndex => $aBlock) { if (empty($aBlock['share_id'])) { // Fetch this blocks upstream ID @@ -105,7 +105,8 @@ if (empty($aAllBlocks)) { . $aBlock['amount'] . "\t" . $iCurrentUpstreamId . "\t\t" . $iRoundShares . "\t" - . "[$iAccountId] " . $user->getUserName($iAccountId) + . "[$iAccountId] " . $user->getUserName($iAccountId) . "\t" + . $share->share_type ); // Notify users diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index 4fea43c5..e3f6e33d 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -193,8 +193,9 @@ class Share { $ppheader = sprintf('%08d', $aBlock['version']) . word_reverse($aBlock['previousblockhash']) . word_reverse($aBlock['merkleroot']) . dechex($aBlock['time']) . $aBlock['bits'] . dechex($aBlock['nonce']); $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution = ? LIMIT 1"); - if ($this->checkStmt($stmt) && $stmt->bind_param('s', $scrypt_hash) && $stmt->execute() && $result = $stmt->get_result()->num_rows > 0) { + if ($this->checkStmt($stmt) && $stmt->bind_param('s', $scrypt_hash) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); + $this->share_type = 'startum_solution'; if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) return true; } @@ -203,6 +204,7 @@ class Share { $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution LIKE CONCAT(?, '%') LIMIT 1"); if ($this->checkStmt($stmt) && $stmt->bind_param('s', $ppheader) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); + $this->share_type = 'pp_solution'; if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) return true; } @@ -219,6 +221,7 @@ class Share { ORDER BY id ASC LIMIT 1"); if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $last, $aBlock['time'], $Block['time']) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); + $this->share_type = 'upstream_share'; if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) return true; } @@ -234,6 +237,7 @@ class Share { ORDER BY id ASC LIMIT 1"); if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $last, $aBlock['time']) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); + $this->share_type = 'any_share'; if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) return true; } From abef09b08c8465fd03ad864439387392aca51580 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 14:50:23 +0200 Subject: [PATCH 035/137] Added blockhash solution checking Stratum of @moopless supports blockhash as a solution. Check for that one first. Addresses #405 --- public/include/classes/share.class.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index e3f6e33d..c2ce65e2 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -187,11 +187,18 @@ class Share { $nonce = pack("I*", $aBlock['nonce']); $header_bin = $version . $previousblockhash . $merkleroot . $time . $bits . $nonce; $header_hex = implode(unpack("H*", $header_bin)); + + // Stratum supported blockhash solution entry + $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution = ? LIMIT 1"); + if ($this->checkStmt($stmt) && $stmt->bind_param('s', $aBlock['hash']) && $stmt->execute() && $result = $stmt->get_result()) { + $this->oUpstream = $result->fetch_object(); + $this->share_type = 'startum_blockhash'; + if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) + return true; + } + + // Stratum scrypt hash check $scrypt_hash = swapEndian(bin2hex(Scrypt::calc($header_bin, $header_bin, 1024, 1, 1, 32))); - - // Fallback to pushpoold solution type - $ppheader = sprintf('%08d', $aBlock['version']) . word_reverse($aBlock['previousblockhash']) . word_reverse($aBlock['merkleroot']) . dechex($aBlock['time']) . $aBlock['bits'] . dechex($aBlock['nonce']); - $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution = ? LIMIT 1"); if ($this->checkStmt($stmt) && $stmt->bind_param('s', $scrypt_hash) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); @@ -201,6 +208,8 @@ class Share { } // Failed to fetch via startum solution, try pushpoold + // Fallback to pushpoold solution type + $ppheader = sprintf('%08d', $aBlock['version']) . word_reverse($aBlock['previousblockhash']) . word_reverse($aBlock['merkleroot']) . dechex($aBlock['time']) . $aBlock['bits'] . dechex($aBlock['nonce']); $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution LIKE CONCAT(?, '%') LIMIT 1"); if ($this->checkStmt($stmt) && $stmt->bind_param('s', $ppheader) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); From 52e02a545b41fb50780e8b7e367da1a24ae7f81e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 15:43:42 +0200 Subject: [PATCH 036/137] fixing issue with upstream shares not being found --- public/include/classes/share.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index c2ce65e2..496652f0 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -226,9 +226,9 @@ class Share { WHERE upstream_result = 'Y' AND id > ? AND UNIX_TIMESTAMP(time) >= ? - AND UNIX_TIMESTAMP(time) <= ? + 60 + AND UNIX_TIMESTAMP(time) <= ( ? + 60 ) ORDER BY id ASC LIMIT 1"); - if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $last, $aBlock['time'], $Block['time']) && $stmt->execute() && $result = $stmt->get_result()) { + if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $last, $aBlock['time'], $aBlock['time']) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); $this->share_type = 'upstream_share'; if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) From b33f572774814964ba2d231212a381e4061b71f7 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 16:13:30 +0200 Subject: [PATCH 037/137] adding missing PHP closing tag --- public/include/pages/statistics.inc.php | 1 + 1 file changed, 1 insertion(+) diff --git a/public/include/pages/statistics.inc.php b/public/include/pages/statistics.inc.php index dc27fde6..3307dd21 100644 --- a/public/include/pages/statistics.inc.php +++ b/public/include/pages/statistics.inc.php @@ -18,3 +18,4 @@ if ($bitcoin->can_connect() === true){ $smarty->assign("CURRENTBLOCK", $iBlock); $smarty->assign("DIFFICULTY", $dDifficulty); $smarty->assign("CONTENT", "default.tpl"); +?> From 5acebc37d3c1f0439c4ed0bb9ae8035305c360e4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 16:13:43 +0200 Subject: [PATCH 038/137] Allow guest access to pages with config options This will allow pool owners to make certain pages public accessible without user registration or login. Please check the config dist file for details. Fixes #408 --- public/include/config/global.inc.dist.php | 16 ++++++++++++++++ public/include/pages/statistics/blocks.inc.php | 7 +++++-- public/include/pages/statistics/pool.inc.php | 4 +++- public/include/smarty_globals.inc.php | 2 +- public/templates/mmcFE/global/navigation.tpl | 8 ++++++++ .../statistics/pool/contributors_hashrate.tpl | 2 +- .../statistics/pool/contributors_shares.tpl | 2 +- 7 files changed, 35 insertions(+), 6 deletions(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 9390b534..162bba00 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -121,6 +121,22 @@ $config['website']['theme'] = 'mmcFE'; $config['website']['mobile'] = true; $config['website']['mobile_theme'] = 'mobile'; +/** + * Some basic access restrictions on some pages + * + * Explanation: + * Some pools would like to run a few pages for public access instead + * of enforcing a login. You can change visibility of some pages here. + * + * Options: + * 'public' : Allow guest access and authenticated user to view page + * 'private' : Only allow logged in users access to view page + * + * Defaults: + * 'private' for every page + **/ +$config['website']['acl']['statistics']['pool'] = 'private'; +$config['website']['acl']['statistics']['blocks'] = 'private'; /** * Re-Captcha settings diff --git a/public/include/pages/statistics/blocks.inc.php b/public/include/pages/statistics/blocks.inc.php index 6c3b00b8..22eeadff 100644 --- a/public/include/pages/statistics/blocks.inc.php +++ b/public/include/pages/statistics/blocks.inc.php @@ -2,7 +2,6 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -if (!$user->isAuthenticated()) header("Location: index.php?page=home"); // Grab the last blocks found $iLimit = 20; @@ -12,5 +11,9 @@ $aBlocksFoundData = $statistics->getBlocksFound($iLimit); $smarty->assign("BLOCKSFOUND", $aBlocksFoundData); $smarty->assign("BLOCKLIMIT", $iLimit); -$smarty->assign("CONTENT", "default.tpl"); +if ($config['website']['acl']['statistics']['blocks'] == 'public') { + $smarty->assign("CONTENT", "default.tpl"); +} else if ($user->isAuthenticated()) { + $smarty->assign("CONTENT", "default.tpl"); +} ?> diff --git a/public/include/pages/statistics/pool.inc.php b/public/include/pages/statistics/pool.inc.php index 1cab1009..27395cc1 100644 --- a/public/include/pages/statistics/pool.inc.php +++ b/public/include/pages/statistics/pool.inc.php @@ -53,7 +53,9 @@ count($aBlockData) > 0 ? $smarty->assign("LASTBLOCK", $aBlockData['height']) : $ $smarty->assign("DIFFICULTY", $dDifficulty); $smarty->assign("REWARD", $config['reward']); -if ($user->isAuthenticated()) { +if ($config['website']['acl']['statistics']['pool'] == 'public') { + $smarty->assign("CONTENT", "authenticated.tpl"); +} else if ($user->isAuthenticated() && $config['website']['acl']['statistics']['pool'] == 'private') { $smarty->assign("CONTENT", "authenticated.tpl"); } else { $smarty->assign("CONTENT", "../default.tpl"); diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index 147b38e9..be65e39e 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -47,7 +47,7 @@ $aGlobal = array( 'blockexplorer' => $config['blockexplorer'], 'chaininfo' => $config['chaininfo'], 'config' => array( - 'website' => array( 'title' => $config['website']['title'] ), + 'website' => array( 'title' => $config['website']['title'], 'acl' => $config['website']['acl'] ), 'price' => array( 'currency' => $config['price']['currency'] ), 'targetdiff' => $config['difficulty'], 'currency' => $config['currency'], diff --git a/public/templates/mmcFE/global/navigation.tpl b/public/templates/mmcFE/global/navigation.tpl index 70619098..6cafd1f6 100644 --- a/public/templates/mmcFE/global/navigation.tpl +++ b/public/templates/mmcFE/global/navigation.tpl @@ -31,6 +31,14 @@ {else}
  • Statistics +
      + {if $GLOBAL.config.website.acl.statistics.pool == 'public'} +
    • Pool Stats
    • + {/if} + {if $GLOBAL.config.website.acl.statistics.blocks == 'public'} +
    • Block Stats
    • + {/if} +
    {/if}
  • Getting Started
  • Support
  • diff --git a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl index 7b294265..a2a6ed58 100644 --- a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl +++ b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl @@ -23,7 +23,7 @@ {if $GLOBAL.config.price.currency}{($estday * $GLOBAL.price)|default:"n/a"|number_format:"2"}{/if} {/section} -{if $listed != 1} +{if $listed != 1 && $GLOBAL.userdata.username|default:""} {if $GLOBAL.userdata.hashrate > 0}{math assign="myestday" equation="round(reward / ( diff * pow(2,32) / ( hashrate * 1000 ) / 3600 / 24), 3)" diff=$DIFFICULTY reward=$REWARD hashrate=$GLOBAL.userdata.hashrate}{/if} n/a diff --git a/public/templates/mmcFE/statistics/pool/contributors_shares.tpl b/public/templates/mmcFE/statistics/pool/contributors_shares.tpl index 444effa4..2a482209 100644 --- a/public/templates/mmcFE/statistics/pool/contributors_shares.tpl +++ b/public/templates/mmcFE/statistics/pool/contributors_shares.tpl @@ -18,7 +18,7 @@ {$CONTRIBSHARES[hashrate].shares|number_format} {/section} -{if $listed != 1} +{if $listed != 1 && $GLOBAL.userdata.username|default:""} n/a {$GLOBAL.userdata.username} From 9e878b06d5aebe9986e839a000a14f46fb54f402 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 16:29:00 +0200 Subject: [PATCH 039/137] Ensure people know what the memcache setting does Even though memcache can be disabled, the memcache library is a requirement as listed in the README.md. I clarified that in the configuration file itself to ensure people install it even though they might be disabling it for debugging purpose. Fixes #409 --- public/include/config/global.inc.dist.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 9390b534..b939a0e4 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -229,11 +229,15 @@ $config['confirmations'] = 120; /** * Memcache configuration * - * Please note that a memcache is greatly increasing performance - * when combined with the `statistics.php` cronjob. Disabling this - * is not recommended in a live environment! + * Even though you can disable the memcache for debugging purposes, the memcache + * library is still required for mmcfe-ng to work. You should not disable this in + * a live environment since a lot of data is cached for the website to increase load + * times! * * Explanations + * enabled : Disable (false) memcache for debugging or enable (true) it + * host : Host IP or hostname + * port : memcache port * keyprefix : Must be changed for multiple mmcfe-ng instances on one host * expiration : Default expiration time in seconds of all cached keys. * Increase if caches expire too fast. From dc51d874a74fd33231b30167ccd14bf6491c2fca Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 21:26:06 +0200 Subject: [PATCH 040/137] Adding block height to blockupdate output This should make tracing block updates easier. --- cronjobs/blockupdate.php | 4 ++-- public/include/classes/block.class.php | 9 ++------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/cronjobs/blockupdate.php b/cronjobs/blockupdate.php index 89a27be1..d257a69d 100755 --- a/cronjobs/blockupdate.php +++ b/cronjobs/blockupdate.php @@ -30,12 +30,12 @@ if ( $bitcoin->can_connect() !== true ) { // Fetch all unconfirmed blocks $aAllBlocks = $block->getAllUnconfirmed($config['confirmations']); -$log->logInfo("ID\tBlockhash\tConfirmations"); +$log->logInfo("ID\tHeight\tBlockhash\tConfirmations"); foreach ($aAllBlocks as $iIndex => $aBlock) { $aBlockInfo = $bitcoin->query('getblock', $aBlock['blockhash']); // Fetch this blocks transaction details to find orphan blocks $aTxDetails = $bitcoin->query('gettransaction', $aBlockInfo['tx'][0]); - $log->logInfo($aBlock['id'] . "\t" . $aBlock['blockhash'] . "\t" . $aBlock['confirmations'] . " -> " . $aBlockInfo['confirmations']); + $log->logInfo($aBlock['id'] . "\t" . $aBlock['height'] . "\t" . $aBlock['blockhash'] . "\t" . $aBlock['confirmations'] . " -> " . $aBlockInfo['confirmations']); if ($aTxDetails['details'][0]['category'] == 'orphan') { // We have an orphaned block, we need to invalidate all transactions for this one if ($transaction->setOrphan($aBlock['id']) && $block->setConfirmations($aBlock['id'], -1)) { diff --git a/public/include/classes/block.class.php b/public/include/classes/block.class.php index 2fcbad09..81d1806a 100644 --- a/public/include/classes/block.class.php +++ b/public/include/classes/block.class.php @@ -85,14 +85,9 @@ class Block { * @return data array Array with database fields as keys **/ public function getAllUnconfirmed($confirmations='120') { - $stmt = $this->mysqli->prepare("SELECT id, blockhash, confirmations FROM $this->table WHERE confirmations < ? AND confirmations > -1"); - if ($this->checkStmt($stmt)) { - $stmt->bind_param("i", $confirmations); - $stmt->execute(); - $result = $stmt->get_result(); - $stmt->close(); + $stmt = $this->mysqli->prepare("SELECT id, height, blockhash, confirmations FROM $this->table WHERE confirmations < ? AND confirmations > -1"); + if ($this->checkStmt($stmt) && $stmt->bind_param("i", $confirmations) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - } return false; } From 2f2acdad6dc4597ddfb84690ee8813883a600601 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 3 Jul 2013 18:57:36 +0200 Subject: [PATCH 041/137] First working version of PPLNS payouts * Based PPLNS on Prop Payout script * Using defaults from prop payout, no class adjustments * Added more methods required for PPLNS * Added block methods for dynamic payout calculations * Added PPLNS Sidebar that also displays the PPLNS Target * Shares beyond this target will not be included in payouts * Shares missing to this target will be added from archives * Enabled archiving by default for PPLNS * Added configuration options for PPLNS * Documented the usage for PPLNS, defaults are sane * Added pplns_payout to run-crons Addresses #143 and if accepted will fix it --- README.md | 4 +- cronjobs/pplns_payout.php | 142 ++++++++++++++++++ cronjobs/run-crons.sh | 2 +- public/include/classes/block.class.php | 24 +++ public/include/classes/share.class.php | 102 ++++++++----- public/include/config/global.inc.dist.php | 24 +++ public/include/smarty_globals.inc.php | 7 + .../templates/mmcFE/global/sidebar_pplns.tpl | 74 +++++++++ .../templates/mobile/global/sidebar_pplns.tpl | 63 ++++++++ 9 files changed, 399 insertions(+), 43 deletions(-) create mode 100755 cronjobs/pplns_payout.php create mode 100644 public/templates/mmcFE/global/sidebar_pplns.tpl create mode 100644 public/templates/mobile/global/sidebar_pplns.tpl diff --git a/README.md b/README.md index 14526524..885c5573 100644 --- a/README.md +++ b/README.md @@ -68,11 +68,11 @@ Features The following feature have been implemented so far: -* Mobile WebUI **NEW** +* Mobile WebUI * Reward Systems * Propotional * PPS - * (Planned) PPLNS + * PPLNS **NEW** * Use of memcache for statistics instead of a cronjob * Web User accounts * Re-Captcha protected registration form diff --git a/cronjobs/pplns_payout.php b/cronjobs/pplns_payout.php new file mode 100755 index 00000000..295702c9 --- /dev/null +++ b/cronjobs/pplns_payout.php @@ -0,0 +1,142 @@ +#!/usr/bin/php +getAllUnaccounted('ASC'); +if (empty($aAllBlocks)) { + verbose("No new unaccounted blocks found\n"); + exit(0); +} + +// We support some dynamic share targets but fall back to our fixed value +if ($config['pplns']['shares']['type'] == 'blockavg' && $block->getBlockCount() > 0) { + $pplns_target = round($block->getAvgBlockShares($config['pplns']['type']['blockavg']['blockcount'])); +} else { + $pplns_target = $config['pplns']['shares']['default'] ; +} + +$count = 0; +foreach ($aAllBlocks as $iIndex => $aBlock) { + if (!$aBlock['accounted']) { + $iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0; + $iCurrentUpstreamId = $aBlock['share_id']; + $iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']); + $config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward']; + $aRoundAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']); + + if ($iRoundShares >= $pplns_target) { + verbose("Matching or exceeding PPLNS target of $pplns_target\n"); + $aAccountShares = $share->getSharesForAccounts($aBlock['share_id'] - $pplns_target + 1, $aBlock['share_id']); + } else { + verbose("Not able to match PPLNS target of $pplns_target\n"); + // We need to fill up with archived shares + // Grab the full current round shares since we didn't match target + $aAccountShares = $aRoundAccountShares; + // Grab only the most recent shares from Archive that fill the missing shares + $aArchiveShares = $share->getArchiveShares($share->getMaxArchiveShareId() - ($pplns_target- $iRoundShares) + 1, $share->getMaxArchiveShareId()); + // Add archived shares to users current shares, if we have any in archive + if (is_array($aArchiveShares)) { + foreach($aAccountShares as $key => $aData) { + if (array_key_exists($aData['username'], $aArchiveShares)) { + $aAccountShares[$key]['valid'] += $aArchiveShares[$aData['username']]['valid']; + $aAccountShares[$key]['invalid'] += $aArchiveShares[$aData['username']]['invalid']; + } + } + } + } + if (empty($aAccountShares)) { + verbose("\nNo shares found for this block\n\n"); + sleep(2); + continue; + } + + // Table header for account shares + verbose("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee\t\tStatus\n"); + + // Loop through all accounts that have found shares for this round + foreach ($aAccountShares as $key => $aData) { + // Payout based on PPLNS target shares, proportional payout for all users + $aData['percentage'] = number_format(round(( 100 / $pplns_target) * $aData['valid'], 8), 8); + $aData['payout'] = number_format(round(( $aData['percentage'] / 100 ) * $dReward, 8), 8); + // Defaults + $aData['fee' ] = 0; + $aData['donation'] = 0; + + if ($config['fees'] > 0) + $aData['fee'] = number_format(round($config['fees'] / 100 * $aData['payout'], 8), 8); + // Calculate donation amount, fees not included + $aData['donation'] = number_format(round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 8), 8); + + // Verbose output of this users calculations + verbose($aData['id'] . "\t" . + $aData['username'] . "\t" . + $aData['valid'] . "\t" . + $aData['invalid'] . "\t" . + $aData['percentage'] . "\t" . + $aData['payout'] . "\t" . + $aData['donation'] . "\t" . + $aData['fee'] . "\t"); + + $strStatus = "OK"; + // Add full round share statistics, not just PPLNS + foreach ($aRoundAccountShares as $key => $aRoundData) { + if ($aRoundData['username'] == $aData['username']) + if (!$statistics->updateShareStatistics($aRoundData, $aBlock['id'])) + $strStatus = "Stats Failed"; + } + // Add new credit transaction + if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit', $aBlock['id'])) + $strStatus = "Transaction Failed"; + // Add new fee debit for this block + if ($aData['fee'] > 0 && $config['fees'] > 0) + if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee', $aBlock['id'])) + $strStatus = "Fee Failed"; + // Add new donation debit + if ($aData['donation'] > 0) + if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation', $aBlock['id'])) + $strStatus = "Donation Failed"; + verbose("\t$strStatus\n"); + } + + // Move counted shares to archive before this blockhash upstream share + $share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId); + // Delete all accounted shares + if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) { + verbose("\nERROR : Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting!\n"); + exit(1); + } + // Mark this block as accounted for + if (!$block->setAccounted($aBlock['id'])) { + verbose("\nERROR : Failed to mark block as accounted! Aborting!\n"); + } + + verbose("------------------------------------------------------------------------\n\n"); + } +} diff --git a/cronjobs/run-crons.sh b/cronjobs/run-crons.sh index c9dfd949..b6b241b4 100755 --- a/cronjobs/run-crons.sh +++ b/cronjobs/run-crons.sh @@ -16,7 +16,7 @@ PIDFILE='/tmp/mmcfe-ng-cron.pid' CRONHOME='.' # List of cruns to execute -CRONS="findblock.php proportional_payout.php pps_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php statistics.php" +CRONS="findblock.php proportional_payout.php pplns_payout.php pps_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php statistics.php" # Additional arguments to pass to cronjobs CRONARGS="-v" diff --git a/public/include/classes/block.class.php b/public/include/classes/block.class.php index 81d1806a..8f52efd9 100644 --- a/public/include/classes/block.class.php +++ b/public/include/classes/block.class.php @@ -79,6 +79,30 @@ class Block { return false; } + /** + * Get total amount of blocks in our table + * @param noone + * @return data int Count of rows + **/ + public function getBlockCount() { + $stmt = $this->mysqli->prepare("SELECT COUNT(id) AS blocks FROM $this->table"); + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) + return (int)$result->fetch_object()->blocks; + return false; + } + + /** + * Fetch our average share count for the past N blocks + * @param limit int Maximum blocks to check + * @return data float Float value of average shares + **/ + public function getAvgBlockShares($limit=10) { + $stmt = $this->mysqli->prepare("SELECT AVG(shares) AS average FROM $this->table LIMIT ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $limit) && $stmt->execute() && $result = $stmt->get_result()) + return (float)$result->fetch_object()->average; + return false; + } + /** * Fetch all unconfirmed blocks from table * @param confirmations int Required confirmations to consider block confirmed diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index 496652f0..ed6078ea 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -13,9 +13,10 @@ class Share { // This defines each share public $rem_host, $username, $our_result, $upstream_result, $reason, $solution, $time; - public function __construct($debug, $mysqli, $salt) { + public function __construct($debug, $mysqli, $user) { $this->debug = $debug; $this->mysqli = $mysqli; + $this->user = $user; $this->debug->append("Instantiated Share class", 2); } @@ -86,47 +87,67 @@ class Share { * Fetch all shares grouped by accounts to count share per account * @param previous_upstream int Previous found share accepted by upstream to limit results * @param current_upstream int Current upstream accepted share + * @param limit int Limit to this amount of shares for PPLNS * @return data array username, valid and invalid shares from account **/ public function getSharesForAccounts($previous_upstream=0, $current_upstream) { - $stmt = $this->mysqli->prepare("SELECT - a.id, - validT.account AS username, - sum(validT.valid) as valid, - IFNULL(sum(invalidT.invalid),0) as invalid - FROM - ( - SELECT DISTINCT - SUBSTRING_INDEX( `username` , '.', 1 ) as account, - COUNT(id) AS valid - FROM $this->table - WHERE id BETWEEN ? AND ? - AND our_result = 'Y' - GROUP BY account - ) validT - LEFT JOIN - ( - SELECT DISTINCT - SUBSTRING_INDEX( `username` , '.', 1 ) as account, - COUNT(id) AS invalid - FROM $this->table - WHERE id BETWEEN ? AND ? - AND our_result = 'N' - GROUP BY account - ) invalidT - ON validT.account = invalidT.account - INNER JOIN accounts a ON a.username = validT.account - GROUP BY a.username DESC"); - if ($this->checkStmt($stmt)) { - $stmt->bind_param('iiii', $previous_upstream, $current_upstream, $previous_upstream, $current_upstream); - $stmt->execute(); - $result = $stmt->get_result(); - $stmt->close(); + $stmt = $this->mysqli->prepare(" + SELECT + a.id, + SUBSTRING_INDEX( s.username , '.', 1 ) as username, + SUM(IF(our_result='Y', 1, 0)) AS valid, + SUM(IF(our_result='N', 1, 0)) AS invalid + FROM $this->table AS s + LEFT JOIN " . $this->user->getTableName() . " AS a + ON a.username = SUBSTRING_INDEX( s.username , '.', 1 ) + WHERE s.id BETWEEN ? AND ? + GROUP BY username DESC + "); + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - } return false; } + /** + * Fetch the highest available share ID from archive + **/ + function getMaxArchiveShareId() { + $stmt = $this->mysqli->prepare(" + SELECT MAX(share_id) AS share_id FROM $this->tableArchive + "); + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_object()->share_id; + return false; + } + + /** + * We need a certain amount of valid archived shares + * param left int Left/lowest share ID + * param right int Right/highest share ID + * return array data Returns an array with usernames as keys for easy access + **/ + function getArchiveShares($left, $right) { + $stmt = $this->mysqli->prepare(" + SELECT + a.id, + SUBSTRING_INDEX( s.username , '.', 1 ) as username, + SUM(IF(our_result='Y', 1, 0)) AS valid, + SUM(IF(our_result='N', 1, 0)) AS invalid + FROM $this->tableArchive AS s + LEFT JOIN " . $this->user->getTableName() . " AS a + ON a.username = SUBSTRING_INDEX( s.username , '.', 1 ) + WHERE s.id BETWEEN ? AND ? + GROUP BY username DESC + "); + if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $left, $right) && $stmt->execute() && $result = $stmt->get_result()) { + $aData = NULL; + while ($row = $result->fetch_assoc()) { + $aData[$row['username']] = $row; + } + if (is_array($aData)) return $aData; + } + return false; + } /** * Move accounted shares to archive table, this step is optional * @param previous_upstream int Previous found share accepted by upstream to limit results @@ -135,10 +156,11 @@ class Share { * @return bool **/ public function moveArchive($current_upstream, $block_id, $previous_upstream=0) { - $archive_stmt = $this->mysqli->prepare("INSERT INTO $this->tableArchive (share_id, username, our_result, upstream_result, block_id, time) - SELECT id, username, our_result, upstream_result, ?, time - FROM $this->table - WHERE id BETWEEN ? AND ?"); + $archive_stmt = $this->mysqli->prepare(" + INSERT INTO $this->tableArchive (share_id, username, our_result, upstream_result, block_id, time) + SELECT id, username, our_result, upstream_result, ?, time + FROM $this->table + WHERE id BETWEEN ? AND ?"); if ($this->checkStmt($archive_stmt) && $archive_stmt->bind_param('iii', $block_id, $previous_upstream, $current_upstream) && $archive_stmt->execute()) { $archive_stmt->close(); return true; @@ -267,4 +289,4 @@ class Share { } } -$share = new Share($debug, $mysqli, SALT); +$share = new Share($debug, $mysqli, $user); diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 9390b534..995fc0b2 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -183,6 +183,30 @@ $config['chaininfo'] = 'http://allchains.info'; // Pool fees applied to users in percent, default: 0 (disabled) $config['fees'] = 0; +/** + * PPLNS requires some settings to run properly. First we need to define + * a default shares count that is applied if we don't have a proper type set. + * Different dynamic types can be applied, or you can run a fixed scheme. + * + * Explanation + * default : Default target shares for PPLNS + * type : Payout type used in PPLNS + * blockcount : Amount of blocks to check for avg shares + * + * Available Options: + * default : amount of shares, integeger + * type : blockavg or fixed + * blockcount : amount of blocks, any integer + * + * Defaults: + * default = 4000000 + * type = `blockavg` + * blockcount = 10 + **/ +$config['pplns']['shares']['default'] = 4000000; +$config['pplns']['shares']['type'] = 'blockavg'; +$config['pplns']['blockavg']['blockcount'] = 10; + // Pool target difficulty as set in pushpoold configuration file // Please also read this for stratum: https://github.com/TheSerapher/php-mmcfe-ng/wiki/FAQ $config['difficulty'] = 20; diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index 147b38e9..fd5083d6 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -83,6 +83,13 @@ if (@$_SESSION['USERDATA']['id']) { $aGlobal['userdata']['sharerate'] = $statistics->getUserSharerate($_SESSION['USERDATA']['id']); switch ($config['payout_system']) { + case 'pplns': + if ($iAvgBlockShares = round($block->getAvgBlockShares($config['pplns']['type']['blockavg']['blockcount']))) { + $aGlobal['pplns']['target'] = $iAvgBlockShares; + } else { + $aGlobal['pplns']['target'] = $config['pplns']['shares']['default']; + } + break; case 'pps': break; default: diff --git a/public/templates/mmcFE/global/sidebar_pplns.tpl b/public/templates/mmcFE/global/sidebar_pplns.tpl new file mode 100644 index 00000000..deb0c1cc --- /dev/null +++ b/public/templates/mmcFE/global/sidebar_pplns.tpl @@ -0,0 +1,74 @@ +
    +
    +
    +
    +

    Dashboard

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    diff --git a/public/templates/mobile/global/sidebar_pplns.tpl b/public/templates/mobile/global/sidebar_pplns.tpl new file mode 100644 index 00000000..95dcf93c --- /dev/null +++ b/public/templates/mobile/global/sidebar_pplns.tpl @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PPLNS Target{$GLOBAL.pplns.target|number_format}
     
    Your Stats
    Hashrate{$GLOBAL.userdata.hashrate|number_format} KH/s
    Unpaid Shares
    Your Valid{$GLOBAL.userdata.shares.valid|number_format}
    Pool Valid{$GLOBAL.roundshares.valid|number_format}
    Round Shares
    Pool Valid{$GLOBAL.roundshares.valid|number_format}
    Pool Invalid{$GLOBAL.roundshares.invalid|number_format}{if $GLOBAL.roundshares.valid > 0} ({(100 / $GLOBAL.roundshares.valid * $GLOBAL.roundshares.invalid)|number_format:"2"}%){/if}
    Your Invalid{$GLOBAL.userdata.shares.invalid|number_format}{if $GLOBAL.roundshares.valid > 0} ({(100 / $GLOBAL.roundshares.valid * $GLOBAL.userdata.shares.invalid)|number_format:"2"}%){/if}
    {$GLOBAL.config.currency} Round Estimate
    Block{$GLOBAL.userdata.est_block|number_format:"3"}
    Fees{$GLOBAL.userdata.est_fee|number_format:"3"}
    Donation{$GLOBAL.userdata.est_donation|number_format:"3"}
    Payout{$GLOBAL.userdata.est_payout|number_format:"3"}
     
    {$GLOBAL.config.currency} Account Balance
    Confirmed{$GLOBAL.userdata.balance.confirmed|default:"0"}
    Unconfirmed{$GLOBAL.userdata.balance.unconfirmed|default:"0"}
    From 394d8b52a94d22a1be835407345d8c44952d1fd4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 7 Jul 2013 00:24:21 +0200 Subject: [PATCH 042/137] Abort payout process if missing share_id As discussed in #392 we should abort any payouts for rounds until the issue is fixed. --- cronjobs/pplns_payout.php | 1 + 1 file changed, 1 insertion(+) diff --git a/cronjobs/pplns_payout.php b/cronjobs/pplns_payout.php index 295702c9..7249586d 100755 --- a/cronjobs/pplns_payout.php +++ b/cronjobs/pplns_payout.php @@ -47,6 +47,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { if (!$aBlock['accounted']) { $iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0; $iCurrentUpstreamId = $aBlock['share_id']; + if (!is_number($iCurrentUpstreamId)) die("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue\n"); $iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']); $config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward']; $aRoundAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']); From ba240000ab3cf1e9bc3dbdb96ca9788d3d73608e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 8 Jul 2013 13:25:34 +0200 Subject: [PATCH 043/137] Latest version for PPLNS payouts * Rebased with next to add logging functions * Added block method to fetch specific block * Modified getArchive shares method to be more constistent * Added new global configuration * Adjusted smarty globals with new configuration * More verbose pplns cron with logging * Re-target the round in case of PPLNS not being matched by archive Fixes #143 --- cronjobs/pplns_payout.php | 116 ++++++++++++++-------- cronjobs/proportional_payout.php | 3 +- public/include/classes/block.class.php | 12 +++ public/include/classes/share.class.php | 51 ++++++++-- public/include/config/global.inc.dist.php | 31 +++++- public/include/smarty_globals.inc.php | 2 +- 6 files changed, 157 insertions(+), 58 deletions(-) diff --git a/cronjobs/pplns_payout.php b/cronjobs/pplns_payout.php index 7249586d..827b598c 100755 --- a/cronjobs/pplns_payout.php +++ b/cronjobs/pplns_payout.php @@ -24,62 +24,90 @@ require_once('shared.inc.php'); // Check if we are set as the payout system if ($config['payout_system'] != 'pplns') { - verbose("Please activate this cron in configuration via payout_system = pplns\n"); + $log->logInfo("Please activate this cron in configuration via payout_system = pplns"); exit(0); } // Fetch all unaccounted blocks $aAllBlocks = $block->getAllUnaccounted('ASC'); if (empty($aAllBlocks)) { - verbose("No new unaccounted blocks found\n"); + $log->logDebug("No new unaccounted blocks found"); exit(0); } -// We support some dynamic share targets but fall back to our fixed value -if ($config['pplns']['shares']['type'] == 'blockavg' && $block->getBlockCount() > 0) { - $pplns_target = round($block->getAvgBlockShares($config['pplns']['type']['blockavg']['blockcount'])); -} else { - $pplns_target = $config['pplns']['shares']['default'] ; -} - $count = 0; foreach ($aAllBlocks as $iIndex => $aBlock) { + // We support some dynamic share targets but fall back to our fixed value + // Re-calculate after each run due to re-targets in this loop + if ($config['pplns']['shares']['type'] == 'blockavg' && $block->getBlockCount() > 0) { + $pplns_target = round($block->getAvgBlockShares($config['pplns']['blockavg']['blockcount'])); + } else { + $pplns_target = $config['pplns']['shares']['default'] ; + } + if (!$aBlock['accounted']) { $iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0; $iCurrentUpstreamId = $aBlock['share_id']; - if (!is_number($iCurrentUpstreamId)) die("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue\n"); + if (!is_numeric($iCurrentUpstreamId)) { + $log->logFatal("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue"); + exit(1); + } $iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']); + $iNewRoundShares = 0; $config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward']; $aRoundAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']); if ($iRoundShares >= $pplns_target) { - verbose("Matching or exceeding PPLNS target of $pplns_target\n"); + $log->logDebug("Matching or exceeding PPLNS target of $pplns_target with $iRoundShares"); $aAccountShares = $share->getSharesForAccounts($aBlock['share_id'] - $pplns_target + 1, $aBlock['share_id']); + if (empty($aAccountShares)) { + $log->logFatal("No shares found for this block, aborted!"); + exit(1); + } } else { - verbose("Not able to match PPLNS target of $pplns_target\n"); + $log->logDebug("Not able to match PPLNS target of $pplns_target with $iRoundShares"); // We need to fill up with archived shares // Grab the full current round shares since we didn't match target $aAccountShares = $aRoundAccountShares; + if (empty($aAccountShares)) { + $log->logFatal("No shares found for this block, aborted!"); + exit(1); + } + // Grab only the most recent shares from Archive that fill the missing shares - $aArchiveShares = $share->getArchiveShares($share->getMaxArchiveShareId() - ($pplns_target- $iRoundShares) + 1, $share->getMaxArchiveShareId()); - // Add archived shares to users current shares, if we have any in archive - if (is_array($aArchiveShares)) { - foreach($aAccountShares as $key => $aData) { - if (array_key_exists($aData['username'], $aArchiveShares)) { - $aAccountShares[$key]['valid'] += $aArchiveShares[$aData['username']]['valid']; - $aAccountShares[$key]['invalid'] += $aArchiveShares[$aData['username']]['invalid']; + $log->logInfo('Fetching ' . ($pplns_target - $iRoundShares) . ' additional shares from archive'); + if (!$aArchiveShares = $share->getArchiveShares($pplns_target - $iRoundShares)) { + $log->logError('Failed to fetch shares from archive, setting target to round total'); + $pplns_target = $iRoundShares; + } else { + // Add archived shares to users current shares, if we have any in archive + if (is_array($aArchiveShares)) { + $log->logDebug('Found shares in archive to match PPLNS target, calculating per-user shares'); + foreach($aAccountShares as $key => $aData) { + if (array_key_exists($aData['username'], $aArchiveShares)) { + $log->logDebug('Found user ' . $aData['username'] . ' in archived shares'); + $log->logDebug(' valid : ' . $aAccountShares[$key]['valid'] . ' + ' . $aArchiveShares[$aData['username']]['valid'] . ' = ' . ($aAccountShares[$key]['valid'] + $aArchiveShares[$aData['username']]['valid']) ); + $log->logDebug(' invalid : ' . $aAccountShares[$key]['invalid'] . ' + ' . $aArchiveShares[$aData['username']]['invalid'] . ' = ' . ($aAccountShares[$key]['invalid'] + $aArchiveShares[$aData['username']]['invalid']) ); + $aAccountShares[$key]['valid'] += $aArchiveShares[$aData['username']]['valid']; + $aAccountShares[$key]['invalid'] += $aArchiveShares[$aData['username']]['invalid']; + } } } + // We tried to fill up to PPLNS target, now we need to check the actual shares to properly payout users + foreach($aAccountShares as $key => $aData) { + $iNewRoundShares += $aData['valid']; + } } } - if (empty($aAccountShares)) { - verbose("\nNo shares found for this block\n\n"); - sleep(2); - continue; + + // We filled from archive but still are not able to match PPLNS target, re-adjust + if ($iRoundShares < $iNewRoundShares) { + $log->logInfo('Adjusting round target to ' . $iNewRoundShares); + $iRoundShares = $iNewRoundShares; } // Table header for account shares - verbose("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee\t\tStatus\n"); + $log->logInfo("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee"); // Loop through all accounts that have found shares for this round foreach ($aAccountShares as $key => $aData) { @@ -96,48 +124,50 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $aData['donation'] = number_format(round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 8), 8); // Verbose output of this users calculations - verbose($aData['id'] . "\t" . - $aData['username'] . "\t" . - $aData['valid'] . "\t" . - $aData['invalid'] . "\t" . - $aData['percentage'] . "\t" . - $aData['payout'] . "\t" . - $aData['donation'] . "\t" . - $aData['fee'] . "\t"); + $log->logInfo($aData['id'] . "\t" . + $aData['username'] . "\t" . + $aData['valid'] . "\t" . + $aData['invalid'] . "\t" . + $aData['percentage'] . "\t" . + $aData['payout'] . "\t" . + $aData['donation'] . "\t" . + $aData['fee']); - $strStatus = "OK"; // Add full round share statistics, not just PPLNS foreach ($aRoundAccountShares as $key => $aRoundData) { if ($aRoundData['username'] == $aData['username']) if (!$statistics->updateShareStatistics($aRoundData, $aBlock['id'])) - $strStatus = "Stats Failed"; + $log->logError('Failed to update share statistics for ' . $aData['username']); } // Add new credit transaction if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit', $aBlock['id'])) - $strStatus = "Transaction Failed"; + $log->logFatal('Failed to insert new Credit transaction to database for ' . $aData['username']); // Add new fee debit for this block if ($aData['fee'] > 0 && $config['fees'] > 0) if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee', $aBlock['id'])) - $strStatus = "Fee Failed"; + $log->logFatal('Failed to insert new Fee transaction to database for ' . $aData['username']); // Add new donation debit if ($aData['donation'] > 0) if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation', $aBlock['id'])) - $strStatus = "Donation Failed"; - verbose("\t$strStatus\n"); + $log->logFatal('Failed to insert new Donation transaction to database for ' . $aData['username']); } // Move counted shares to archive before this blockhash upstream share - $share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId); + if (!$share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId)) + $log->logError('Failed to copy shares to archive table'); // Delete all accounted shares if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) { - verbose("\nERROR : Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting!\n"); + $log->logFatal("Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting!"); exit(1); } + // If we don't keep archives, delete some now to release disk space + if (!$share->purgeArchive()) { + $log->logError("Failed to delete archived shares, not critical but should be checked!"); + } // Mark this block as accounted for if (!$block->setAccounted($aBlock['id'])) { - verbose("\nERROR : Failed to mark block as accounted! Aborting!\n"); + $log->logFatal("Failed to mark block as accounted! Aborting!"); + exit(1); } - - verbose("------------------------------------------------------------------------\n\n"); } } diff --git a/cronjobs/proportional_payout.php b/cronjobs/proportional_payout.php index 8a7dd2cd..faca41ca 100755 --- a/cronjobs/proportional_payout.php +++ b/cronjobs/proportional_payout.php @@ -97,7 +97,8 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { } // Move counted shares to archive before this blockhash upstream share - if ($config['archive_shares']) $share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId); + if (!$share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId)) + $log->logError('Failed to copy shares to archive'); // Delete all accounted shares if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) { $log->logFatal('Failed to delete accounted shares from ' . $iPreviousShareId . ' to ' . $iCurrentUpstreamId . ', aborted'); diff --git a/public/include/classes/block.class.php b/public/include/classes/block.class.php index 8f52efd9..711ed0b5 100644 --- a/public/include/classes/block.class.php +++ b/public/include/classes/block.class.php @@ -43,6 +43,18 @@ class Block { return false; } + /** + * Get a specific block, by block height + * @param height int Block Height + * @return data array Block information from DB + **/ + public function getBlock($height) { + $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE height = ? LIMIT 1"); + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $height) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_assoc(); + return false; + } + /** * Get our last, highest share ID inserted for a block * @param none diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index ed6078ea..fc59f0f4 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -13,10 +13,12 @@ class Share { // This defines each share public $rem_host, $username, $our_result, $upstream_result, $reason, $solution, $time; - public function __construct($debug, $mysqli, $user) { + public function __construct($debug, $mysqli, $user, $block, $config) { $this->debug = $debug; $this->mysqli = $mysqli; $this->user = $user; + $this->config = $config; + $this->block = $block; $this->debug->append("Instantiated Share class", 2); } @@ -126,28 +128,55 @@ class Share { * param right int Right/highest share ID * return array data Returns an array with usernames as keys for easy access **/ - function getArchiveShares($left, $right) { + function getArchiveShares($iCount) { + $iMinId = $this->getMaxArchiveShareId() - $iCount; + $iMaxId = $this->getMaxArchiveShareId(); $stmt = $this->mysqli->prepare(" SELECT a.id, - SUBSTRING_INDEX( s.username , '.', 1 ) as username, - SUM(IF(our_result='Y', 1, 0)) AS valid, - SUM(IF(our_result='N', 1, 0)) AS invalid + SUBSTRING_INDEX( s.username , '.', 1 ) as account, + IFNULL(SUM(IF(our_result='Y', 1, 0)), 0) AS valid, + IFNULL(SUM(IF(our_result='N', 1, 0)), 0) AS invalid FROM $this->tableArchive AS s LEFT JOIN " . $this->user->getTableName() . " AS a ON a.username = SUBSTRING_INDEX( s.username , '.', 1 ) - WHERE s.id BETWEEN ? AND ? - GROUP BY username DESC - "); - if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $left, $right) && $stmt->execute() && $result = $stmt->get_result()) { + WHERE s.share_id > ? AND s.share_id <= ? + GROUP BY account DESC"); + if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $iMinId, $iMaxId) && $stmt->execute() && $result = $stmt->get_result()) { $aData = NULL; while ($row = $result->fetch_assoc()) { - $aData[$row['username']] = $row; + $aData[$row['account']] = $row; } if (is_array($aData)) return $aData; } return false; } + + /** + * We keep shares only up to a certain point + * This can be configured by the user. + * @return return bool true or false + **/ + public function purgeArchive() { + if ($this->config['payout_system'] == 'pplns') { + // Fetch our last block so we can go back configured rounds + $aLastBlock = $this->block->getLast(); + // Fetch the block we need to find the share_id + $aBlock = $this->block->getBlock($aLastBlock['height'] - $this->config['archive']['maxrounds']); + // Now that we know our block, remove those shares + $stmt = $this->mysqli->prepare("DELETE FROM $this->tableArchive WHERE block_id < ? AND time < DATE_SUB(now(), INTERVAL ? MINUTE)"); + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $aBlock['id'], $config['archive']['maxage']) && $stmt->execute()) + return true; + } else { + // We are not running pplns, so we just need to keep shares of the past minutes + $stmt = $this->mysqli->prepare("DELETE FROM $this->tableArchive WHERE time < DATE_SUB(now(), INTERVAL ? MINUTE)"); + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $config['archive']['maxage']) && $stmt->execute()) + return true; + } + // Catchall + return false; + } + /** * Move accounted shares to archive table, this step is optional * @param previous_upstream int Previous found share accepted by upstream to limit results @@ -289,4 +318,4 @@ class Share { } } -$share = new Share($debug, $mysqli, $user); +$share = new Share($debug, $mysqli, $user, $block, $config); diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 995fc0b2..cee6d5de 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -169,8 +169,35 @@ $config['block_bonus'] = 0; **/ $config['payout_system'] = 'prop'; -// For debugging purposes you can archive shares in the archive_shares table, default: true -$config['archive_shares'] = true; +/** + * Archiving configuration for debugging + * + * Explanation: + * By default, we don't need to archive for a long time. PPLNS and Hashrate + * calculations rely on this archive, but all shares past a certain point can + * safely be deleted. + * + * To ensure we have enough shares on stack for PPLNS, this + * is set to the past 10 rounds. Even with lucky ones in between those should + * fit the PPLNS target. On top of that, even if we have more than 10 rounds, + * we still keep the last maxage shares to ensure we can calculate hashrates. + * Both conditions need to be met in order for shares to be purged from archive. + * + * Proportional mode will only keep the past 24 hours. These are required for + * hashrate calculations to work past a round, hence 24 hours was selected as + * the default. You may want to increase the time for debugging, then add any + * integer reflecting minutes of shares to keep. + * + * Availabe Options: + * maxrounds : PPLNS, keep shares for maxrounds + * maxage : PROP and PPLNS, delete shares older than maxage minutes + * + * Default: + * maxrounds = 10 + * maxage = 60 * 60 * 24 (24h) + **/ +$config['archive']['maxrounds'] = 10; +$config['archive']['maxage'] = 60 * 60 * 24; // URL prefix for block searches, used for block links, default: `http://explorer.litecoin.net/search?q=` // If empty, the block link to the block information page will be removed diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index fd5083d6..de5a2694 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -84,7 +84,7 @@ if (@$_SESSION['USERDATA']['id']) { switch ($config['payout_system']) { case 'pplns': - if ($iAvgBlockShares = round($block->getAvgBlockShares($config['pplns']['type']['blockavg']['blockcount']))) { + if ($iAvgBlockShares = round($block->getAvgBlockShares($config['pplns']['blockavg']['blockcount']))) { $aGlobal['pplns']['target'] = $iAvgBlockShares; } else { $aGlobal['pplns']['target'] = $config['pplns']['shares']['default']; From 00b3c45d2a8e2a983943de3d45d193b7d8f47eb0 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 14:12:54 +0200 Subject: [PATCH 044/137] Adding archive cleanup cron Since this needs to be done for all payouts, moved into it's own cron. * Adjusted run-crons.sh to trigger archive cleanup * Adjusted payouts to remove purgeArchive calls * Adjusted pps payout, removed old configuration variable Further addresses #143 --- cronjobs/archive_cleanup.php | 29 +++++++++++++++++++++++++++++ cronjobs/pplns_payout.php | 4 ---- cronjobs/pps_payout.php | 2 +- cronjobs/run-crons.sh | 2 +- 4 files changed, 31 insertions(+), 6 deletions(-) create mode 100755 cronjobs/archive_cleanup.php diff --git a/cronjobs/archive_cleanup.php b/cronjobs/archive_cleanup.php new file mode 100755 index 00000000..6f008d69 --- /dev/null +++ b/cronjobs/archive_cleanup.php @@ -0,0 +1,29 @@ +#!/usr/bin/php +purgeArchive()) { + $log->logError("Failed to delete archived shares, not critical but should be checked!"); +} +?> diff --git a/cronjobs/pplns_payout.php b/cronjobs/pplns_payout.php index 827b598c..54c229bd 100755 --- a/cronjobs/pplns_payout.php +++ b/cronjobs/pplns_payout.php @@ -160,10 +160,6 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $log->logFatal("Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting!"); exit(1); } - // If we don't keep archives, delete some now to release disk space - if (!$share->purgeArchive()) { - $log->logError("Failed to delete archived shares, not critical but should be checked!"); - } // Mark this block as accounted for if (!$block->setAccounted($aBlock['id'])) { $log->logFatal("Failed to mark block as accounted! Aborting!"); diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php index ee35e2ad..58b8dc83 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -120,7 +120,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $log->logError("Failed to update stats for this block on : " . $aData['username']); } // Move shares to archive - if ($config['archive_shares'] && $aBlock['share_id'] < $iLastShareId) { + if ($aBlock['share_id'] < $iLastShareId) { if (!$share->moveArchive($aBlock['share_id'], $aBlock['id'], @$iLastBlockShare)) $log->logError("Archving failed"); } diff --git a/cronjobs/run-crons.sh b/cronjobs/run-crons.sh index b6b241b4..e797cb44 100755 --- a/cronjobs/run-crons.sh +++ b/cronjobs/run-crons.sh @@ -16,7 +16,7 @@ PIDFILE='/tmp/mmcfe-ng-cron.pid' CRONHOME='.' # List of cruns to execute -CRONS="findblock.php proportional_payout.php pplns_payout.php pps_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php statistics.php" +CRONS="findblock.php proportional_payout.php pplns_payout.php pps_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php statistics.php archive_cleanup.php" # Additional arguments to pass to cronjobs CRONARGS="-v" From 8808913ca5a25ec909b9ce8f259f55146062ed1d Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 22:02:12 +0200 Subject: [PATCH 045/137] Fixing Sidebar estimations on PPLNS This will address a new issue in #143, estimates where not displayed properly when pplns was active. Fixes #143 --- public/include/smarty_globals.inc.php | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index de5a2694..cc162501 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -83,16 +83,7 @@ if (@$_SESSION['USERDATA']['id']) { $aGlobal['userdata']['sharerate'] = $statistics->getUserSharerate($_SESSION['USERDATA']['id']); switch ($config['payout_system']) { - case 'pplns': - if ($iAvgBlockShares = round($block->getAvgBlockShares($config['pplns']['blockavg']['blockcount']))) { - $aGlobal['pplns']['target'] = $iAvgBlockShares; - } else { - $aGlobal['pplns']['target'] = $config['pplns']['shares']['default']; - } - break; - case 'pps': - break; - default: + case 'prop' || 'pplns': // Some estimations if (@$aRoundShares['valid'] > 0) { $aGlobal['userdata']['est_block'] = round(( (int)$aGlobal['userdata']['shares']['valid'] / (int)$aRoundShares['valid'] ) * (float)$config['reward'], 8); @@ -105,6 +96,14 @@ if (@$_SESSION['USERDATA']['id']) { $aGlobal['userdata']['est_donation'] = 0; $aGlobal['userdata']['est_payout'] = 0; } + case 'pplns': + if ($iAvgBlockShares = round($block->getAvgBlockShares($config['pplns']['blockavg']['blockcount']))) { + $aGlobal['pplns']['target'] = $iAvgBlockShares; + } else { + $aGlobal['pplns']['target'] = $config['pplns']['shares']['default']; + } + break; + case 'pps': break; } From 85c1a8eaf55a963789e52e3ea16ae69111414ba3 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 22:41:07 +0200 Subject: [PATCH 046/137] more debug information on non-existing block shares --- cronjobs/pplns_payout.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cronjobs/pplns_payout.php b/cronjobs/pplns_payout.php index 54c229bd..9657b394 100755 --- a/cronjobs/pplns_payout.php +++ b/cronjobs/pplns_payout.php @@ -61,7 +61,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $log->logDebug("Matching or exceeding PPLNS target of $pplns_target with $iRoundShares"); $aAccountShares = $share->getSharesForAccounts($aBlock['share_id'] - $pplns_target + 1, $aBlock['share_id']); if (empty($aAccountShares)) { - $log->logFatal("No shares found for this block, aborted!"); + $log->logFatal("No shares found for this block, aborted! Block Height : " . $aBlock['height']); exit(1); } } else { @@ -70,7 +70,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { // Grab the full current round shares since we didn't match target $aAccountShares = $aRoundAccountShares; if (empty($aAccountShares)) { - $log->logFatal("No shares found for this block, aborted!"); + $log->logFatal("No shares found for this block, aborted! Block height: " . $aBlock['height']); exit(1); } From ecf3db3a5b456470d245051d13bfdab8f2eef42e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 22:41:21 +0200 Subject: [PATCH 047/137] Removed BETWEEN call for round shares Now shares are calculated proper with `id > ? AND id <= ?` as boundaries. This will ensure the upstream result is also calculated for the round. Addresses #143 --- public/include/classes/share.class.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index fc59f0f4..7cf1ef3d 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -73,7 +73,7 @@ class Share { count(id) as total FROM $this->table WHERE our_result = 'Y' - AND id BETWEEN ? AND ? + AND id > ? AND id <= ? "); if ($this->checkStmt($stmt)) { $stmt->bind_param('ii', $previous_upstream, $current_upstream); @@ -102,7 +102,7 @@ class Share { FROM $this->table AS s LEFT JOIN " . $this->user->getTableName() . " AS a ON a.username = SUBSTRING_INDEX( s.username , '.', 1 ) - WHERE s.id BETWEEN ? AND ? + WHERE s.id > ? AND s.id <= ? GROUP BY username DESC "); if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute() && $result = $stmt->get_result()) @@ -189,7 +189,7 @@ class Share { INSERT INTO $this->tableArchive (share_id, username, our_result, upstream_result, block_id, time) SELECT id, username, our_result, upstream_result, ?, time FROM $this->table - WHERE id BETWEEN ? AND ?"); + WHERE id > ? AND id <= ?"); if ($this->checkStmt($archive_stmt) && $archive_stmt->bind_param('iii', $block_id, $previous_upstream, $current_upstream) && $archive_stmt->execute()) { $archive_stmt->close(); return true; @@ -199,7 +199,7 @@ class Share { } public function deleteAccountedShares($current_upstream, $previous_upstream=0) { - $stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id BETWEEN ? AND ?"); + $stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id > ? AND id <= ?"); if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute()) return true; // Catchall From b4b6b118bf06603c82d6f37ab2bd7474d608d2fe Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 23:19:36 +0200 Subject: [PATCH 048/137] Ensure to return 0 if no valid/invalid shares are found --- public/include/classes/share.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index 7cf1ef3d..dddabf33 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -97,8 +97,8 @@ class Share { SELECT a.id, SUBSTRING_INDEX( s.username , '.', 1 ) as username, - SUM(IF(our_result='Y', 1, 0)) AS valid, - SUM(IF(our_result='N', 1, 0)) AS invalid + IFNULL(SUM(IF(our_result='Y', 1, 0)), 0) AS valid, + IFNULL(SUM(IF(our_result='N', 1, 0)), 0) AS invalid FROM $this->table AS s LEFT JOIN " . $this->user->getTableName() . " AS a ON a.username = SUBSTRING_INDEX( s.username , '.', 1 ) From adceaf6913e1de4f08e5055068cb5e24bdbeda91 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 23:28:59 +0200 Subject: [PATCH 049/137] Update README.md Added new donor --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 885c5573..3b3902f6 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ These people have supported this project with a donation: * [vias](https://github.com/vias79) * [WKNiGHT](https://github.com/WKNiGHT-) * [ZC](https://github.com/zccopwrx) +* Nutnut Pools running mmcfe-ng ====================== From 94d9c1eb4c67c4f3da524c8adc5500269df8364b Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 10 Jul 2013 10:40:11 +0200 Subject: [PATCH 050/137] Added cronjob monitoring to admin panel * Added monitoring class to deal with monitoring events * Added event calls to all important cronjobs * Added cron_end include file for monitoring cleanups on successful runs * Added Monitoring to autoloader * Modified account page to check for running auto_payout in monitoring * Added monitoring to Navigation bar * Added monitoring controller page Fixes #415 --- cronjobs/archive_cleanup.php | 7 ++ cronjobs/auto_payout.php | 11 ++- cronjobs/blockupdate.php | 6 ++ cronjobs/cron_end.inc.php | 28 ++++++++ cronjobs/findblock.php | 9 ++- cronjobs/pplns_payout.php | 18 +++++ cronjobs/pps_payout.php | 16 ++++- cronjobs/proportional_payout.php | 38 ++++++---- cronjobs/shared.inc.php | 18 ++++- public/include/autoloader.inc.php | 1 + public/include/classes/monitoring.class.php | 49 +++++++++++++ public/include/pages/account/edit.inc.php | 3 +- public/include/pages/admin/monitoring.inc.php | 70 +++++++++++++++++++ .../mmcFE/admin/monitoring/default.tpl | 39 +++++++++++ public/templates/mmcFE/global/navigation.tpl | 1 + 15 files changed, 290 insertions(+), 24 deletions(-) create mode 100644 cronjobs/cron_end.inc.php create mode 100644 public/include/classes/monitoring.class.php create mode 100644 public/include/pages/admin/monitoring.inc.php create mode 100644 public/templates/mmcFE/admin/monitoring/default.tpl diff --git a/cronjobs/archive_cleanup.php b/cronjobs/archive_cleanup.php index 6f008d69..ebdd5b7e 100755 --- a/cronjobs/archive_cleanup.php +++ b/cronjobs/archive_cleanup.php @@ -25,5 +25,12 @@ require_once('shared.inc.php'); // If we don't keep archives, delete some now to release disk space if (!$share->purgeArchive()) { $log->logError("Failed to delete archived shares, not critical but should be checked!"); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "Failed to delete archived shares"); + $monitoring->setStatus($cron_name . "_status", "okerror", 1); + exit(1); } + +// Cron cleanup and monitoring +require_once('cron_end.inc.php'); ?> diff --git a/cronjobs/auto_payout.php b/cronjobs/auto_payout.php index 943a6cd7..c55cc913 100755 --- a/cronjobs/auto_payout.php +++ b/cronjobs/auto_payout.php @@ -24,12 +24,12 @@ require_once('shared.inc.php'); if ($bitcoin->can_connect() !== true) { $log->logFatal(" unable to connect to RPC server, exiting\n"); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server"); + $monitoring->setStatus($cron_name . "_status", "okerror", 1); exit(1); } -// Mark this job as active -$setting->setValue('auto_payout_active', 1); - // Fetch all users with setup AP $users = $user->getAllAutoPayout(); @@ -80,7 +80,6 @@ if (! empty($users)) { $log->logDebug(" no user has configured their AP > 0\n"); } -// Mark this job as inactive -$setting->setValue('auto_payout_active', 0); - +// Cron cleanup and monitoring +require_once('cron_end.inc.php'); ?> diff --git a/cronjobs/blockupdate.php b/cronjobs/blockupdate.php index d257a69d..67d106b7 100755 --- a/cronjobs/blockupdate.php +++ b/cronjobs/blockupdate.php @@ -24,6 +24,9 @@ require_once('shared.inc.php'); if ( $bitcoin->can_connect() !== true ) { $log->logFatal("Failed to connect to RPC server\n"); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server"); + $monitoring->setStatus($cron_name . "_status", "okerror", 1); exit(1); } @@ -51,3 +54,6 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $log->logError(' Failed to update block confirmations'); } } + +require_once('cron_end.inc.php'); +?> diff --git a/cronjobs/cron_end.inc.php b/cronjobs/cron_end.inc.php new file mode 100644 index 00000000..177e5296 --- /dev/null +++ b/cronjobs/cron_end.inc.php @@ -0,0 +1,28 @@ +setStatus($cron_name . "_message", "message", "OK"); +$monitoring->setStatus($cron_name . "_status", "okerror", 0); +$monitoring->setStatus($cron_name . "_runtime", "time", microtime(true) - $cron_start[$cron_name]); + +// Mark cron as running for monitoring +$monitoring->setStatus($cron_name . '_active', "yesno", 0); +?> diff --git a/cronjobs/findblock.php b/cronjobs/findblock.php index b98bbc3a..0336deea 100755 --- a/cronjobs/findblock.php +++ b/cronjobs/findblock.php @@ -32,6 +32,9 @@ if ( $bitcoin->can_connect() === true ){ $aTransactions = $bitcoin->query('listsinceblock', $strLastBlockHash); } else { $log->logFatal('Unable to conenct to RPC server backend'); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server"); + $monitoring->setStatus($cron_name . "_status", "okerror", 1); exit(1); } @@ -78,6 +81,9 @@ if (empty($aAllBlocks)) { $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; } @@ -124,5 +130,6 @@ if (empty($aAllBlocks)) { } } } -?> +require_once('cron_end.inc.php'); +?> diff --git a/cronjobs/pplns_payout.php b/cronjobs/pplns_payout.php index 9657b394..6bb4234a 100755 --- a/cronjobs/pplns_payout.php +++ b/cronjobs/pplns_payout.php @@ -32,6 +32,9 @@ if ($config['payout_system'] != 'pplns') { $aAllBlocks = $block->getAllUnaccounted('ASC'); if (empty($aAllBlocks)) { $log->logDebug("No new unaccounted blocks found"); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "No new unaccounted blocks"); + $monitoring->setStatus($cron_name . "_status", "okerror", 0); exit(0); } @@ -50,6 +53,9 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $iCurrentUpstreamId = $aBlock['share_id']; if (!is_numeric($iCurrentUpstreamId)) { $log->logFatal("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue"); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "Block " . $aBlock['height'] . " has no share_id associated with it"); + $monitoring->setStatus($cron_name . "_status", "okerror", 1); exit(1); } $iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']); @@ -62,6 +68,9 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $aAccountShares = $share->getSharesForAccounts($aBlock['share_id'] - $pplns_target + 1, $aBlock['share_id']); if (empty($aAccountShares)) { $log->logFatal("No shares found for this block, aborted! Block Height : " . $aBlock['height']); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "No shares found for this block: " . $aBlock['height']); + $monitoring->setStatus($cron_name . "_status", "okerror", 1); exit(1); } } else { @@ -71,6 +80,9 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $aAccountShares = $aRoundAccountShares; if (empty($aAccountShares)) { $log->logFatal("No shares found for this block, aborted! Block height: " . $aBlock['height']); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "No shares found for this block: " . $aBlock['height']); + $monitoring->setStatus($cron_name . "_status", "okerror", 1); exit(1); } @@ -163,7 +175,13 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { // Mark this block as accounted for if (!$block->setAccounted($aBlock['id'])) { $log->logFatal("Failed to mark block as accounted! Aborting!"); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "Failed to mark block " . $aBlock['height'] . " as accounted"); + $monitoring->setStatus($cron_name . "_status", "okerror", 1); exit(1); } } } + +require_once('cron_end.inc.php'); +?> diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php index 58b8dc83..bb48e62e 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -36,12 +36,15 @@ if ( $bitcoin->can_connect() === true ){ $dDifficulty = $dDifficulty['proof-of-work']; } else { $log->logFatal("Aborted: " . $bitcoin->can_connect() . "\n"); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server"); + $monitoring->setStatus($cron_name . "_status", "okerror", 1); exit(1); } // Value per share calculation if ($config['reward_type'] != 'block') { -$pps_value = number_format(round($config['reward'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); + $pps_value = number_format(round($config['reward'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); } else { // Try to find the last block value and use that for future payouts, revert to fixed reward if none found if ($aLastBlock = $block->getLast()) { @@ -111,6 +114,9 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $iLastBlockShare = @$aAllBlocks[$iIndex - 1]['share_id'] ? @$aAllBlocks[$iIndex - 1]['share_id'] : 0; if (!is_numeric($aBlock['share_id'])) { $log->logFatal("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue"); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "Block " . $aBlock['height'] . " has no share_id associated with it"); + $monitoring->setStatus($cron_name . "_status", "okerror", 1); exit(1); } // Per account statistics @@ -127,12 +133,20 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { // Delete shares if ($aBlock['share_id'] < $iLastShareId && !$share->deleteAccountedShares($aBlock['share_id'], $iLastBlockShare)) { $log->logFatal("Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . $iLastBlockShare . ", aborting!"); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . $iLastBlockShare); + $monitoring->setStatus($cron_name . "_status", "okerror", 1); exit(1); } // Mark this block as accounted for if (!$block->setAccounted($aBlock['id'])) { $log->logFatal("Failed to mark block as accounted! Aborting!"); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "Failed to mark block " . $aBlock['height'] . " as accounted"); + $monitoring->setStatus($cron_name . "_status", "okerror", 1); exit(1); } } + +require_once('cron.inc.php'); ?> diff --git a/cronjobs/proportional_payout.php b/cronjobs/proportional_payout.php index faca41ca..36a9a1fe 100755 --- a/cronjobs/proportional_payout.php +++ b/cronjobs/proportional_payout.php @@ -32,6 +32,9 @@ if ($config['payout_system'] != 'prop') { $aAllBlocks = $block->getAllUnaccounted('ASC'); if (empty($aAllBlocks)) { $log->logDebug('No new unaccounted blocks found in database'); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "No new unaccounted blocks"); + $monitoring->setStatus($cron_name . "_status", "okerror", 0); exit(0); } @@ -42,17 +45,15 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { if (!$aBlock['accounted']) { $iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0; $iCurrentUpstreamId = $aBlock['share_id']; - if (!is_numeric($iCurrentUpstreamId)) { - $log->logFatal("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue."); - $log->logFatal("Please assign a valid share ID to this block to continue the payout process."); - exit(1); - } $aAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']); $iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']); $config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward']; if (empty($aAccountShares)) { $log->logFatal('No shares found for this block, aborted: ' . $aBlock['height']); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "No shares found for this block, aborted: " . $aBlock['height']); + $monitoring->setStatus($cron_name . "_status", "okerror", 1); exit(1); } @@ -72,13 +73,13 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { // Verbose output of this users calculations $log->logInfo($aData['id'] . "\t" . - $aData['username'] . "\t" . - $aData['valid'] . "\t" . - $aData['invalid'] . "\t" . - $aData['percentage'] . "\t" . - $aData['payout'] . "\t" . - $aData['donation'] . "\t" . - $aData['fee']); + $aData['username'] . "\t" . + $aData['valid'] . "\t" . + $aData['invalid'] . "\t" . + $aData['percentage'] . "\t" . + $aData['payout'] . "\t" . + $aData['donation'] . "\t" . + $aData['fee']); // Update user share statistics if (!$statistics->updateShareStatistics($aData, $aBlock['id'])) @@ -102,10 +103,21 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { // Delete all accounted shares if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) { $log->logFatal('Failed to delete accounted shares from ' . $iPreviousShareId . ' to ' . $iCurrentUpstreamId . ', aborted'); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "Failed to delete accounted shares from " . $iPreviousShareId . " to " . $iCurrentUpstreamId); + $monitoring->setStatus($cron_name . "_status", "okerror", 1); exit(1); } // Mark this block as accounted for - if (!$block->setAccounted($aBlock['id'])) + if (!$block->setAccounted($aBlock['id'])) { $log->logFatal('Failed to mark block as accounted! Aborted.'); + $monitoring->setStatus($cron_name . "_active", "yesno", 0); + $monitoring->setStatus($cron_name . "_message", "message", "Failed to mark block " . $aBlock['height'] . " as accounted"); + $monitoring->setStatus($cron_name . "_status", "okerror", 1); + exit(1); + } } } + +require_once('cron_end.inc.php'); +?> diff --git a/cronjobs/shared.inc.php b/cronjobs/shared.inc.php index 9d3ae322..14bed27e 100644 --- a/cronjobs/shared.inc.php +++ b/cronjobs/shared.inc.php @@ -22,6 +22,13 @@ limitations under the License. // We need to find our include files so set this properly define("BASEPATH", "../public/"); + +/***************************************************** + * No need to change beyond this point * + *****************************************************/ +// Our cron name +$cron_name = basename($_SERVER['PHP_SELF'], '.php'); + // Our security check define("SECURITY", 1); @@ -32,6 +39,13 @@ require_once(BASEPATH . 'include/config/global.inc.php'); require_once(INCLUDE_DIR . '/autoloader.inc.php'); // Load 3rd party logging library for running crons -$log = new KLogger ( 'logs/' . basename($_SERVER['PHP_SELF'], '.php') . '.txt' , KLogger::DEBUG ); -$log->LogDebug('Starting ' . basename($_SERVER['PHP_SELF'], '.php')); +$log = new KLogger ( 'logs/' . $cron_name . '.txt' , KLogger::DEBUG ); +$log->LogDebug('Starting ' . $cron_name); + +// Load the start time for later runtime calculations for monitoring +$cron_start[$cron_name] = microtime(true); + +// Mark cron as running for monitoring +$log->logDebug('Marking cronjob as running for monitoring'); +$monitoring->setStatus($cron_name . '_active', 'yesno', 1); ?> diff --git a/public/include/autoloader.inc.php b/public/include/autoloader.inc.php index 68109049..281b39c6 100644 --- a/public/include/autoloader.inc.php +++ b/public/include/autoloader.inc.php @@ -24,6 +24,7 @@ require_once(INCLUDE_DIR . '/smarty.inc.php'); require_once(CLASS_DIR . '/base.class.php'); require_once(CLASS_DIR . '/block.class.php'); require_once(CLASS_DIR . '/setting.class.php'); +require_once(CLASS_DIR . '/monitoring.class.php'); require_once(CLASS_DIR . '/user.class.php'); require_once(CLASS_DIR . '/share.class.php'); require_once(CLASS_DIR . '/worker.class.php'); diff --git a/public/include/classes/monitoring.class.php b/public/include/classes/monitoring.class.php new file mode 100644 index 00000000..ff69007f --- /dev/null +++ b/public/include/classes/monitoring.class.php @@ -0,0 +1,49 @@ +debug = $debug; + $this->mysqli = $mysqli; + $this->table = 'monitoring'; + } + + /** + * Fetch a value from our table + * @param name string Setting name + * @return value string Value + **/ + public function getStatus($name) { + $query = $this->mysqli->prepare("SELECT * FROM $this->table WHERE name = ? LIMIT 1"); + if ($query && $query->bind_param('s', $name) && $query->execute() && $result = $query->get_result()) { + return $result->fetch_assoc(); + } else { + $this->debug->append("Failed to fetch variable $name from $this->table"); + return false; + } + return $value; + } + + /** + * Insert or update a setting + * @param name string Name of the variable + * @param value string Variable value + * @return bool + **/ + public function setStatus($name, $type, $value) { + $stmt = $this->mysqli->prepare(" + INSERT INTO $this->table (name, type, value) + VALUES (?, ?, ?) + ON DUPLICATE KEY UPDATE value = ? + "); + if ($stmt && $stmt->bind_param('ssss', $name, $type, $value, $value) && $stmt->execute()) + return true; + $this->debug->append("Failed to set $name to $value"); + return false; + } +} + +$monitoring = new Monitoring($debug, $mysqli); diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php index 5548dd82..96fb432d 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -30,7 +30,8 @@ if ($user->isAuthenticated()) { if ($continue == true) { // Send balance to address, mind fee for transaction! try { - if ($setting->getValue('auto_payout_active') == 0) { + $auto_payout = $monitoring->getStatus('auto_payout_active'); + if ($auto_payout['value'] == 0) { $bitcoin->sendtoaddress($sCoinAddress, $dBalance); } else { $_SESSION['POPUP'][] = array('CONTENT' => 'Auto-payout active, please contact site support immidiately to revoke invalid transactions.', 'TYPE' => 'errormsg'); diff --git a/public/include/pages/admin/monitoring.inc.php b/public/include/pages/admin/monitoring.inc.php new file mode 100644 index 00000000..18db3949 --- /dev/null +++ b/public/include/pages/admin/monitoring.inc.php @@ -0,0 +1,70 @@ +isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) { + header("HTTP/1.1 404 Page not found"); + die("404 Page not found"); +} + +// Fetch settings to propagate to template +$aCronStatus = array( + 'auto_payout' => array ( + array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('auto_payout_status') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('auto_payout_message') ), + array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('auto_payout_active') ), + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('auto_payout_runtime') ) + ), + 'archive_cleanup' => array ( + array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('archive_cleanup_status') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('archive_cleanup_message') ), + array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('archive_cleanup_active') ), + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('archive_cleanup_runtime') ) + ), + 'blockupdate' => array ( + array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('blockupdate_status') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('blockupdate_message') ), + array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('blockupdate_active') ), + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('blockupdate_runtime') ) + ), + 'findblock' => array ( + array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('findblock_status') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('findblock_message') ), + array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('findblock_active') ), + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('findblock_runtime') ) + ) +); +// Payout system specifics +switch ($config['payout_system']) { +case 'pplns': + $aCronStatus['pplns_payout'] = array ( + array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('pplns_payout_status') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('pplns_payout_message') ), + array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('pplns_payout_active') ), + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('pplns_payout_runtime') ) + ); + break; +case 'pps': + $aCronStatus['pps_payout'] = array( + array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('pps_payout_status') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('pps_payout_message') ), + array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('pps_payout_active') ), + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('pps_payout_runtime') ) + ); + break; +case 'prop': + $aCronStatus['proportional_payout'] = array( + array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('proportional_payout_status') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('proportional_payout_message') ), + array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('proportional_payout_active') ), + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('proportional_payout_runtime') ) + ); + break; +} +$smarty->assign("CRONSTATUS", $aCronStatus); + +// Tempalte specifics +$smarty->assign("CONTENT", "default.tpl"); +?> diff --git a/public/templates/mmcFE/admin/monitoring/default.tpl b/public/templates/mmcFE/admin/monitoring/default.tpl new file mode 100644 index 00000000..416d5fcf --- /dev/null +++ b/public/templates/mmcFE/admin/monitoring/default.tpl @@ -0,0 +1,39 @@ +{foreach $CRONSTATUS as $k=>$v} + {include file="global/block_header.tpl" BLOCK_HEADER="$k"} + + + + + + + {foreach $v as $event} + + + + + {/foreach} + +
    Event NameStatus
    {$event.NAME} + {if $event.STATUS.type == 'okerror'} + {if $event.STATUS.value == 0} + OK + {else} + ERROR + {/if} + {else if $event.STATUS.type == 'message'} + {$event.STATUS.value} + {else if $event.STATUS.type == 'yesno'} + {if $event.STATUS.value == 1} + Yes + {else} + No + {/if} + {else if $event.STATUS.type == 'time'} + {$event.STATUS.value|number_format:"2"} seconds + {else} + {$event.STATUS.value} + {/if} +
    + + {include file="global/block_footer.tpl"} +{/foreach} diff --git a/public/templates/mmcFE/global/navigation.tpl b/public/templates/mmcFE/global/navigation.tpl index 6cafd1f6..42c89e9a 100644 --- a/public/templates/mmcFE/global/navigation.tpl +++ b/public/templates/mmcFE/global/navigation.tpl @@ -13,6 +13,7 @@ {if $smarty.session.AUTHENTICATED|default:"0" == 1 && $GLOBAL.userdata.is_admin == 1}
  • Admin Panel
      +
    • Monitoring
    • User Info
    • Wallet Info
    • Transactions
    • From 0c014da726fc44f42bbd2f7772ce31fb53bfd325 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 10 Jul 2013 10:44:10 +0200 Subject: [PATCH 051/137] unknown index PHP warning fix --- public/include/pages/account/edit.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php index 96fb432d..1ced549b 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -54,7 +54,7 @@ if ($user->isAuthenticated()) { $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service', 'TYPE' => 'errormsg'); } } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than ' . $config['txfee'] . ' ' . $conifg['currency'] . ' to cover transaction fees', 'TYPE' => 'errormsg'); + $_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than ' . $config['txfee'] . ' ' . $config['currency'] . ' to cover transaction fees', 'TYPE' => 'errormsg'); } $setting->setValue('manual_payout_active', 0); } From a6d752a09699d31c37fa5a965828486fcb4a65f5 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 10 Jul 2013 10:45:10 +0200 Subject: [PATCH 052/137] adding some defaults on template --- public/templates/mmcFE/admin/monitoring/default.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/templates/mmcFE/admin/monitoring/default.tpl b/public/templates/mmcFE/admin/monitoring/default.tpl index 416d5fcf..4a929e02 100644 --- a/public/templates/mmcFE/admin/monitoring/default.tpl +++ b/public/templates/mmcFE/admin/monitoring/default.tpl @@ -25,9 +25,9 @@ No {/if} {else if $event.STATUS.type == 'time'} - {$event.STATUS.value|number_format:"2"} seconds + {$event.STATUS.value|default:"0"|number_format:"2"} seconds {else} - {$event.STATUS.value} + {$event.STATUS.value|default:""} {/if} From 0e98d81af070f33fcd31977d57fca0bd2845bdb7 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 10 Jul 2013 11:22:57 +0200 Subject: [PATCH 053/137] adding new monitoring table to SQL imports --- sql/002_monitoring.sql | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 sql/002_monitoring.sql diff --git a/sql/002_monitoring.sql b/sql/002_monitoring.sql new file mode 100644 index 00000000..e5a21682 --- /dev/null +++ b/sql/002_monitoring.sql @@ -0,0 +1,21 @@ +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; + + +CREATE TABLE IF NOT EXISTS `monitoring` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(30) NOT NULL, + `type` varchar(15) NOT NULL, + `value` varchar(25) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf32 COMMENT='Monitoring events from cronjobs'; + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; From ceeed45bb39ac76282c224db9111b0e936af4038 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 10 Jul 2013 11:26:06 +0200 Subject: [PATCH 054/137] Adding statistics cron to monitoring Useful to trace cron runtimes in order to adjust caching times for long rounds. Addresses #145 and adds a new cron to monitoring page --- cronjobs/statistics.php | 2 ++ public/include/pages/admin/monitoring.inc.php | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/cronjobs/statistics.php b/cronjobs/statistics.php index 9ce96bde..2a145692 100755 --- a/cronjobs/statistics.php +++ b/cronjobs/statistics.php @@ -55,4 +55,6 @@ $log->logInfo("getAllUserShares " . number_format(microtime(true) - $start, 2) . foreach ($aUserShares as $aShares) { $memcache->setCache('getUserShares'. $aShares['id'], $aShares); } + +require_once('cron_end.inc.php'); ?> diff --git a/public/include/pages/admin/monitoring.inc.php b/public/include/pages/admin/monitoring.inc.php index 18db3949..c891c201 100644 --- a/public/include/pages/admin/monitoring.inc.php +++ b/public/include/pages/admin/monitoring.inc.php @@ -11,6 +11,12 @@ if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) { // Fetch settings to propagate to template $aCronStatus = array( + 'statistics' => array ( + array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('statistics_status') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('statistics_message') ), + array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('statistics_active') ), + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('statistics_runtime') ) + ), 'auto_payout' => array ( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('auto_payout_status') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('auto_payout_message') ), From 652bf8ed4b23c9a8b93aec965e8367e8e4405792 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 10 Jul 2013 11:30:41 +0200 Subject: [PATCH 055/137] Update README.md Updated features --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3b3902f6..a7787bb2 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ The following feature have been implemented so far: * Propotional * PPS * PPLNS **NEW** -* Use of memcache for statistics instead of a cronjob +* Statistics are cached in Memcache by Cronjob for quick data access * Web User accounts * Re-Captcha protected registration form * Worker accounts @@ -88,9 +88,11 @@ The following feature have been implemented so far: * Auto payout * Transaction list (confirmed and unconfirmed) * Admin Panel + * Cron Monitoring Overview **NEW** * User Listing including statistics * Wallet information - * News Posts **NEW** + * User Transactions **NEW** + * News Posts * Notification system * IDLE Workers * New blocks found in pool From 271f7f8381be3dedace988a61c5a6a4b8d39eaf3 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 10 Jul 2013 11:39:16 +0200 Subject: [PATCH 056/137] Better monitoring layout --- public/include/pages/admin/monitoring.inc.php | 32 +++++++++---------- .../mmcFE/admin/monitoring/default.tpl | 28 ++++++++-------- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/public/include/pages/admin/monitoring.inc.php b/public/include/pages/admin/monitoring.inc.php index c891c201..efc20a6c 100644 --- a/public/include/pages/admin/monitoring.inc.php +++ b/public/include/pages/admin/monitoring.inc.php @@ -13,33 +13,33 @@ if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) { $aCronStatus = array( 'statistics' => array ( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('statistics_status') ), - array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('statistics_message') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('statistics_active') ), - array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('statistics_runtime') ) + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('statistics_runtime') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('statistics_message') ), ), 'auto_payout' => array ( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('auto_payout_status') ), - array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('auto_payout_message') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('auto_payout_active') ), - array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('auto_payout_runtime') ) + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('auto_payout_runtime') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('auto_payout_message') ), ), 'archive_cleanup' => array ( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('archive_cleanup_status') ), - array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('archive_cleanup_message') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('archive_cleanup_active') ), - array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('archive_cleanup_runtime') ) + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('archive_cleanup_runtime') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('archive_cleanup_message') ), ), 'blockupdate' => array ( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('blockupdate_status') ), - array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('blockupdate_message') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('blockupdate_active') ), - array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('blockupdate_runtime') ) + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('blockupdate_runtime') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('blockupdate_message') ), ), 'findblock' => array ( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('findblock_status') ), - array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('findblock_message') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('findblock_active') ), - array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('findblock_runtime') ) + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('findblock_runtime') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('findblock_message') ), ) ); // Payout system specifics @@ -47,25 +47,25 @@ switch ($config['payout_system']) { case 'pplns': $aCronStatus['pplns_payout'] = array ( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('pplns_payout_status') ), - array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('pplns_payout_message') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('pplns_payout_active') ), - array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('pplns_payout_runtime') ) + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('pplns_payout_runtime') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('pplns_payout_message') ), ); break; case 'pps': $aCronStatus['pps_payout'] = array( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('pps_payout_status') ), - array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('pps_payout_message') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('pps_payout_active') ), - array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('pps_payout_runtime') ) + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('pps_payout_runtime') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('pps_payout_message') ), ); break; case 'prop': $aCronStatus['proportional_payout'] = array( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('proportional_payout_status') ), - array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('proportional_payout_message') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('proportional_payout_active') ), - array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('proportional_payout_runtime') ) + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('proportional_payout_runtime') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('proportional_payout_message') ), ); break; } diff --git a/public/templates/mmcFE/admin/monitoring/default.tpl b/public/templates/mmcFE/admin/monitoring/default.tpl index 4a929e02..6213a164 100644 --- a/public/templates/mmcFE/admin/monitoring/default.tpl +++ b/public/templates/mmcFE/admin/monitoring/default.tpl @@ -1,14 +1,17 @@ -{foreach $CRONSTATUS as $k=>$v} - {include file="global/block_header.tpl" BLOCK_HEADER="$k"} - +{include file="global/block_header.tpl" BLOCK_HEADER="Monitoring"} +
      - - + + + + + - {foreach $v as $event} +{foreach $CRONSTATUS as $cron=>$v} - + + {foreach $v as $event} + {/foreach} - {/foreach} +{/foreach}
      Event NameStatusCronjobExit CodeActiveRuntimeMessage
      {$event.NAME}{$cron} {if $event.STATUS.type == 'okerror'} {if $event.STATUS.value == 0} @@ -20,9 +23,9 @@ {$event.STATUS.value} {else if $event.STATUS.type == 'yesno'} {if $event.STATUS.value == 1} - Yes + Yes {else} - No + No {/if} {else if $event.STATUS.type == 'time'} {$event.STATUS.value|default:"0"|number_format:"2"} seconds @@ -30,10 +33,9 @@ {$event.STATUS.value|default:""} {/if}
      - - {include file="global/block_footer.tpl"} -{/foreach} +{include file="global/block_footer.tpl"} From 993ddaf3bc316854bd01dee0ce9ea575c0d77a44 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 10 Jul 2013 14:17:20 +0200 Subject: [PATCH 057/137] fixing 24h shares retention in dist config --- public/include/config/global.inc.dist.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 896d5daf..a57c1f22 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -210,10 +210,10 @@ $config['payout_system'] = 'prop'; * * Default: * maxrounds = 10 - * maxage = 60 * 60 * 24 (24h) + * maxage = 60 * 24 (24h) **/ $config['archive']['maxrounds'] = 10; -$config['archive']['maxage'] = 60 * 60 * 24; +$config['archive']['maxage'] = 60 * 24; // URL prefix for block searches, used for block links, default: `http://explorer.litecoin.net/search?q=` // If empty, the block link to the block information page will be removed From fdf97c88325135dd0b2c963414e69574f8b96588 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 10 Jul 2013 14:24:15 +0200 Subject: [PATCH 058/137] fixing config access in share class for archive purge --- public/include/classes/share.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index dddabf33..7bcc6d14 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -165,12 +165,12 @@ class Share { $aBlock = $this->block->getBlock($aLastBlock['height'] - $this->config['archive']['maxrounds']); // Now that we know our block, remove those shares $stmt = $this->mysqli->prepare("DELETE FROM $this->tableArchive WHERE block_id < ? AND time < DATE_SUB(now(), INTERVAL ? MINUTE)"); - if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $aBlock['id'], $config['archive']['maxage']) && $stmt->execute()) + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $aBlock['id'], $this->config['archive']['maxage']) && $stmt->execute()) return true; } else { // We are not running pplns, so we just need to keep shares of the past minutes $stmt = $this->mysqli->prepare("DELETE FROM $this->tableArchive WHERE time < DATE_SUB(now(), INTERVAL ? MINUTE)"); - if ($this->checkStmt($stmt) && $stmt->bind_param('i', $config['archive']['maxage']) && $stmt->execute()) + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $this->config['archive']['maxage']) && $stmt->execute()) return true; } // Catchall From 39d1193e6a59358843847185fc104cf347a9227b Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 08:51:40 +0200 Subject: [PATCH 059/137] Add default value for active pool workers Fixes #418 --- public/templates/mmcFE/global/header.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/templates/mmcFE/global/header.tpl b/public/templates/mmcFE/global/header.tpl index b3d150f6..4ee9d9b5 100644 --- a/public/templates/mmcFE/global/header.tpl +++ b/public/templates/mmcFE/global/header.tpl @@ -8,7 +8,7 @@
    • Network Hashrate: {($GLOBAL.nethashrate / 1000 / 1000 )|default:"0"|number_format:"3"} MH/s    
    • Pool Hashrate: {($GLOBAL.hashrate / 1000)|number_format:"3"} MH/s    
    • Pool Sharerate: {$GLOBAL.sharerate|number_format:"2"} Shares/s    
    • -
    • Pool Workers: {$GLOBAL.workers}    
    • +
    • Pool Workers: {$GLOBAL.workers|default:"0"}    
    • From aace0dd014ededaad206a7985f7e243fb62044d1 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 08:57:11 +0200 Subject: [PATCH 060/137] More descriptive PPLNS options Fixes #421 and #422 --- public/include/config/global.inc.dist.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index a57c1f22..73325a5c 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -179,6 +179,7 @@ $config['block_bonus'] = 0; * Available options: * prop: Proportional payout system * pps : Pay Per Share payout system + * pplns : Pay Per Last N Shares payout system * * Default: * prop @@ -232,6 +233,19 @@ $config['fees'] = 0; * Different dynamic types can be applied, or you can run a fixed scheme. * * Explanation + * + * PPLNS can run on two different payouts: fixed and blockavg. Each one + * defines a different PPLNS target. + * + * Fixed means we will be looking at the shares setup in the default + * setting. There is no automatic adjustments to the PPLNS target, + * all users will be paid out proportionally to that target. + * + * Blockavg will look at the last blockcount blocks shares and take + * the average as the PPLNS target. This will be automatically adjusted + * when difficulty changes and more blocks are available. This keeps the + * target dynamic but still traceable. + * * default : Default target shares for PPLNS * type : Payout type used in PPLNS * blockcount : Amount of blocks to check for avg shares @@ -260,7 +274,7 @@ $config['difficulty'] = 20; * * Explanation: * - * Proportional Payout System + * Proportional + PPLNS Payout System * When running a pool on fixed mode, each block will be paid * out as defined in `reward`. If you wish to pass transaction * fees inside discovered blocks on to user, set this to `block`. From 6632920fa1ba4c0e37c2a50b98f5f1d9104ff2ed Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 2 Jul 2013 14:08:33 +0200 Subject: [PATCH 061/137] Add detailed smarty cache documentation to config Instead of just making it availble document the smarty cache feature. It might work for users, but it's advised to rely on the memcache instead. Fixes #309 --- public/include/config/global.inc.dist.php | 26 +++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index a57c1f22..8bff1114 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -341,8 +341,30 @@ $config['cookie']['path'] = '/'; $config['cookie']['name'] = 'POOLERCOOKIE'; $config['cookie']['domain'] = ''; -// Disable or enable smarty cache -// This is usually not required, default: 0 + +/** + * Enable or disable the Smarty cache + * + * Explanation: + * Smarty implements a file based cache for all HTML output generated + * from dynamic scripts. It can be enabled to cache the HTML data on disk, + * future request are served from those cache files. + * + * This may or may not work as expected, in general Memcache is used to cache + * all data so rendering the page should not take too long anyway. + * + * You can test this out and enable (1) this setting but it's not guaranteed to + * work with mmcfe-ng. + * + * Ensure that the folder `templates/cache` is writable by the webserver! + * + * Options: + * 0 = disabled + * 1 = enabled + * + * Default: + * 0 = disabled + **/ $config['cache'] = 0; ?> From e9311f08a53912ea8b2a491124345b5fa11ed1b1 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 2 Jul 2013 14:30:07 +0200 Subject: [PATCH 062/137] Adding cache lifetime option to smarty config * Renamed configuration array to `smarty` => `cache` * Added `smarty` => `cache_lifetime` to expire cache files properly This should be safe to use, be aware that each page request is cached! That includes any POST/GET calls to the site. It does help in speeding up the site, up to 100% on some requests. For a high traffic site it probably makes sense to enable this option with a low cache lifetime to ensure most recent data. Addresses #309 --- .gitignore | 1 + public/include/config/global.inc.dist.php | 21 ++++++++++++++------- public/include/smarty.inc.php | 3 ++- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 66b6c501..a8f1b1ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /public/include/config/global.inc.php /public/templates/compile/*.php /cronjobs/logs/*.txt +/public/templates/cache/*.php diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 8bff1114..da41eb90 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -358,13 +358,20 @@ $config['cookie']['domain'] = ''; * * Ensure that the folder `templates/cache` is writable by the webserver! * - * Options: - * 0 = disabled - * 1 = enabled + * cache = Enable/Disable the cache + * cache_lifetime = Time to keep files in seconds before updating them * - * Default: - * 0 = disabled + * Options: + * cache: + * 0 = disabled + * 1 = enabled + * cache_lifetime: + * time in seconds + * + * Defaults: + * cache = 0, disabled + * cache_lifetime = 30 seconds **/ -$config['cache'] = 0; - +$config['smarty']['cache'] = 1; +$config['smarty']['cache_lifetime'] = 30; ?> diff --git a/public/include/smarty.inc.php b/public/include/smarty.inc.php index 8a320581..46a38750 100644 --- a/public/include/smarty.inc.php +++ b/public/include/smarty.inc.php @@ -20,6 +20,7 @@ $smarty->template_dir = BASEPATH . 'templates/' . THEME . '/'; $smarty->compile_dir = BASEPATH . 'templates/compile/'; // Optional smarty caching, check Smarty documentation for details -$smarty->caching = $config['cache']; +$smarty->caching = $config['smarty']['cache']; +$smarty->cache_lifetime = $config['smarty']['cache_lifetime']; $smarty->cache_dir = BASEPATH . "templates/cache"; ?> From 426268f71db7c5a10d685859592931c859cc5db6 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 4 Jul 2013 08:48:19 +0200 Subject: [PATCH 063/137] adjust smarty configurations --- public/include/smarty.inc.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/public/include/smarty.inc.php b/public/include/smarty.inc.php index 46a38750..e42e56e3 100644 --- a/public/include/smarty.inc.php +++ b/public/include/smarty.inc.php @@ -20,7 +20,10 @@ $smarty->template_dir = BASEPATH . 'templates/' . THEME . '/'; $smarty->compile_dir = BASEPATH . 'templates/compile/'; // Optional smarty caching, check Smarty documentation for details -$smarty->caching = $config['smarty']['cache']; -$smarty->cache_lifetime = $config['smarty']['cache_lifetime']; -$smarty->cache_dir = BASEPATH . "templates/cache"; +if ($config['smarty']['cache']) { + $debug->append('Enable smarty cache'); + $smarty->setCaching(Smarty::CACHING_LIFETIME_SAVED); + $smarty->cache_lifetime = $config['smarty']['cache_lifetime']; + $smarty->cache_dir = BASEPATH . "templates/cache"; +} ?> From 0e6edc562c89361ed6348cf23f91b7e7aedb51a4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 2 Jul 2013 14:08:33 +0200 Subject: [PATCH 064/137] Add detailed smarty cache documentation to config Instead of just making it availble document the smarty cache feature. It might work for users, but it's advised to rely on the memcache instead. Fixes #309 --- public/include/config/global.inc.dist.php | 1 - 1 file changed, 1 deletion(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index da41eb90..b0aa2f21 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -341,7 +341,6 @@ $config['cookie']['path'] = '/'; $config['cookie']['name'] = 'POOLERCOOKIE'; $config['cookie']['domain'] = ''; - /** * Enable or disable the Smarty cache * From 3c426e913b609b927c1d79cdf03941f24e19ca45 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sat, 6 Jul 2013 18:10:23 +0200 Subject: [PATCH 065/137] Adding `{nocache}` flags for dynamic content This will update content instantly once the user changes it and not load a cached version from the smarty cache. Addresses #309 --- public/templates/mmcFE/account/edit/default.tpl | 10 +++++----- .../templates/mmcFE/account/notifications/default.tpl | 8 ++++---- public/templates/mmcFE/account/workers/default.tpl | 2 ++ public/templates/mmcFE/admin/news/default.tpl | 2 ++ public/templates/mmcFE/admin/news_edit/default.tpl | 6 +++--- public/templates/mmcFE/admin/settings/default.tpl | 4 ++-- public/templates/mmcFE/admin/user/default.tpl | 2 ++ 7 files changed, 20 insertions(+), 14 deletions(-) diff --git a/public/templates/mmcFE/account/edit/default.tpl b/public/templates/mmcFE/account/edit/default.tpl index 482c3579..c15b56d4 100644 --- a/public/templates/mmcFE/account/edit/default.tpl +++ b/public/templates/mmcFE/account/edit/default.tpl @@ -7,9 +7,9 @@ Username: {$GLOBAL.userdata.username} User Id: {$GLOBAL.userdata.id} API Key: {$GLOBAL.userdata.api_key} - E-Mail: - Payment Address: - Donation %: [donation amount in percent (example: 0.5)] + E-Mail: + Payment Address: + Donation %: [donation amount in percent (example: 0.5)] Automatic Payout Threshold: [{$GLOBAL.config.ap_threshold.min}-{$GLOBAL.config.ap_threshold.max} {$GLOBAL.config.currency}. Set to '0' for no auto payout] 4 digit PIN: [The 4 digit PIN you chose when registering] @@ -23,8 +23,8 @@ - - + +
      Account Balance:    {$GLOBAL.userdata.balance.confirmed|escape} {$GLOBAL.config.currency}
      Payout to:
      {$GLOBAL.userdata.coin_address|escape}
      Account Balance:    {nocache}{$GLOBAL.userdata.balance.confirmed|escape}{/nocache} {$GLOBAL.config.currency}
      Payout to:
      {nocache}{$GLOBAL.userdata.coin_address|escape}{/nocache}
      4 digit PIN:
      diff --git a/public/templates/mmcFE/account/notifications/default.tpl b/public/templates/mmcFE/account/notifications/default.tpl index 1d54729b..52140466 100644 --- a/public/templates/mmcFE/account/notifications/default.tpl +++ b/public/templates/mmcFE/account/notifications/default.tpl @@ -12,7 +12,7 @@ IDLE Worker - + @@ -20,7 +20,7 @@ New Blocks - + @@ -28,7 +28,7 @@ Auto Payout - + @@ -36,7 +36,7 @@ Manual Payout - + diff --git a/public/templates/mmcFE/account/workers/default.tpl b/public/templates/mmcFE/account/workers/default.tpl index c69d1139..f26e3109 100644 --- a/public/templates/mmcFE/account/workers/default.tpl +++ b/public/templates/mmcFE/account/workers/default.tpl @@ -15,6 +15,7 @@     + {nocache} {section worker $WORKERS} {assign var="username" value="."|escape|explode:$WORKERS[worker].username:2} @@ -29,6 +30,7 @@ {/section} + {/nocache} diff --git a/public/templates/mmcFE/admin/news/default.tpl b/public/templates/mmcFE/admin/news/default.tpl index 7bd8c198..8c3ac5d1 100644 --- a/public/templates/mmcFE/admin/news/default.tpl +++ b/public/templates/mmcFE/admin/news/default.tpl @@ -17,6 +17,7 @@ {include file="global/block_footer.tpl"} +{nocache} {section name=news loop=$NEWS} {include file="global/block_header.tpl" @@ -35,4 +36,5 @@ {include file="global/block_footer.tpl"} {/section} +{/nocache} {include file="global/block_footer.tpl"} diff --git a/public/templates/mmcFE/admin/news_edit/default.tpl b/public/templates/mmcFE/admin/news_edit/default.tpl index 467d4a6c..22e945fb 100644 --- a/public/templates/mmcFE/admin/news_edit/default.tpl +++ b/public/templates/mmcFE/admin/news_edit/default.tpl @@ -10,7 +10,7 @@ Active - + @@ -18,13 +18,13 @@ Header - + Content - + diff --git a/public/templates/mmcFE/admin/settings/default.tpl b/public/templates/mmcFE/admin/settings/default.tpl index 3cd67f4e..a2ffbd5b 100644 --- a/public/templates/mmcFE/admin/settings/default.tpl +++ b/public/templates/mmcFE/admin/settings/default.tpl @@ -16,7 +16,7 @@ @@ -26,7 +26,7 @@ diff --git a/public/templates/mmcFE/admin/user/default.tpl b/public/templates/mmcFE/admin/user/default.tpl index 82955741..8c65c49f 100644 --- a/public/templates/mmcFE/admin/user/default.tpl +++ b/public/templates/mmcFE/admin/user/default.tpl @@ -44,6 +44,7 @@ +{nocache} {section name=user loop=$USERS|default} {$USERS[user].id} @@ -70,6 +71,7 @@ {/section} +{/nocache} From bffeea07c93ced2830ed4cc1ed0e89b5de947352 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sat, 6 Jul 2013 18:36:11 +0200 Subject: [PATCH 066/137] Adding cache detection to many pages This will allow pages to skip loading data from backends like the database or the wallet RPC server. If a cached page is detected and valid, all dynamic content generation will be skipped completely. Other pages that have not been adjusted in this commit will still fetch backend data all the time. This will ensure clients always see the most recent data, like worker information or account changes. This should fix #309 completely but needs some testing. --- .../include/pages/admin/transactions.inc.php | 16 ++- public/include/pages/admin/wallet.inc.php | 17 ++- public/include/pages/home.inc.php | 17 +-- public/include/pages/statistics.inc.php | 25 +++-- .../include/pages/statistics/blocks.inc.php | 17 ++- .../include/pages/statistics/graphs.inc.php | 19 ++-- public/include/pages/statistics/pool.inc.php | 101 +++++++++--------- public/include/smarty_globals.inc.php | 4 +- public/templates/mmcFE/master.tpl | 2 +- 9 files changed, 130 insertions(+), 88 deletions(-) diff --git a/public/include/pages/admin/transactions.inc.php b/public/include/pages/admin/transactions.inc.php index 00345903..b25d6f15 100644 --- a/public/include/pages/admin/transactions.inc.php +++ b/public/include/pages/admin/transactions.inc.php @@ -2,10 +2,20 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -if ($user->isAuthenticated()) { + +// 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', md5(serialize($_REQUEST)))) { + $debug->append('No cached version available, fetching from backend', 3); $aTransactions = $transaction->getAllTransactions(@$_REQUEST['start']); if (!$aTransactions) $_SESSION['POPUP'][] = array('CONTENT' => 'Could not find any transaction', 'TYPE' => 'errormsg'); - $smarty->assign('TRANSACTIONS', $aTransactions); - $smarty->assign('CONTENT', 'default.tpl'); +} else { + $debug->append('Using cached page', 3); } +$smarty->assign('TRANSACTIONS', $aTransactions); +$smarty->assign('CONTENT', 'default.tpl'); ?> diff --git a/public/include/pages/admin/wallet.inc.php b/public/include/pages/admin/wallet.inc.php index 479ff919..d0642b14 100644 --- a/public/include/pages/admin/wallet.inc.php +++ b/public/include/pages/admin/wallet.inc.php @@ -9,15 +9,22 @@ if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) { die("404 Page not found"); } -if ($bitcoin->can_connect() === true){ - $dBalance = $bitcoin->query('getbalance'); +if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { + $debug->append('No cached version available, fetching from backend', 3); + if ($bitcoin->can_connect() === true){ + $dBalance = $bitcoin->query('getbalance'); + } else { + $dBalance = 0; + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg'); + } + // Fetch locked balance from transactions + $dLockedBalance = $transaction->getLockedBalance(); } else { - $dBalance = 0; - $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg'); + $debug->append('Using cached page', 3); } $smarty->assign("BALANCE", $dBalance); -$smarty->assign("LOCKED", $transaction->getLockedBalance()); +$smarty->assign("LOCKED", $dLockedBalance); // Tempalte specifics $smarty->assign("CONTENT", "default.tpl"); diff --git a/public/include/pages/home.inc.php b/public/include/pages/home.inc.php index ea4bd8fb..ac7b458e 100644 --- a/public/include/pages/home.inc.php +++ b/public/include/pages/home.inc.php @@ -6,13 +6,18 @@ if (!defined('SECURITY')) die('Hacking attempt'); // Include markdown library use \Michelf\Markdown; -// Fetch active news to display -$aNews = $news->getAllActive(); -if (is_array($aNews)) { - foreach ($aNews as $key => $aData) { - // Transform Markdown content to HTML - $aNews[$key]['content'] = Markdown::defaultTransform($aData['content']); +if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { + $debug->append('No cached version available, fetching from backend', 3); + // Fetch active news to display + $aNews = $news->getAllActive(); + if (is_array($aNews)) { + foreach ($aNews as $key => $aData) { + // Transform Markdown content to HTML + $aNews[$key]['content'] = Markdown::defaultTransform($aData['content']); + } } +} else { + $debug->append('Using cached page', 3); } // Load news entries for Desktop site and unauthenticated users diff --git a/public/include/pages/statistics.inc.php b/public/include/pages/statistics.inc.php index 3307dd21..c5035f0c 100644 --- a/public/include/pages/statistics.inc.php +++ b/public/include/pages/statistics.inc.php @@ -4,18 +4,23 @@ if (!defined('SECURITY')) die('Hacking attempt'); -if ($bitcoin->can_connect() === true){ - $dDifficulty = $bitcoin->query('getdifficulty'); - if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) - $dDifficulty = $dDifficulty['proof-of-work']; - $iBlock = $bitcoin->query('getblockcount'); +if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { + $debug->append('No cached version available, fetching from backend', 3); + if ($bitcoin->can_connect() === true){ + $dDifficulty = $bitcoin->query('getdifficulty'); + if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) + $dDifficulty = $dDifficulty['proof-of-work']; + $iBlock = $bitcoin->query('getblockcount'); + } else { + $dDifficulty = 1; + $iBlock = 0; + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg'); + } + $smarty->assign("CURRENTBLOCK", $iBlock); + $smarty->assign("DIFFICULTY", $dDifficulty); } else { - $dDifficulty = 1; - $iBlock = 0; - $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg'); + $debug->append('Using cached page', 3); } -$smarty->assign("CURRENTBLOCK", $iBlock); -$smarty->assign("DIFFICULTY", $dDifficulty); $smarty->assign("CONTENT", "default.tpl"); ?> diff --git a/public/include/pages/statistics/blocks.inc.php b/public/include/pages/statistics/blocks.inc.php index 22eeadff..41e209cb 100644 --- a/public/include/pages/statistics/blocks.inc.php +++ b/public/include/pages/statistics/blocks.inc.php @@ -4,12 +4,19 @@ if (!defined('SECURITY')) die('Hacking attempt'); // Grab the last blocks found -$iLimit = 20; -$aBlocksFoundData = $statistics->getBlocksFound($iLimit); +if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { + $debug->append('No cached version available, fetching from backend', 3); + // Grab the last blocks found + $iLimit = 20; + $aBlocksFoundData = $statistics->getBlocksFound($iLimit); -// Propagate content our template -$smarty->assign("BLOCKSFOUND", $aBlocksFoundData); -$smarty->assign("BLOCKLIMIT", $iLimit); + // Propagate content our template + $smarty->assign("BLOCKSFOUND", $aBlocksFoundData); + $smarty->assign("BLOCKLIMIT", $iLimit); + +} else { + $debug->append('Using cached page', 3); +} if ($config['website']['acl']['statistics']['blocks'] == 'public') { $smarty->assign("CONTENT", "default.tpl"); diff --git a/public/include/pages/statistics/graphs.inc.php b/public/include/pages/statistics/graphs.inc.php index f7016835..4d31aadb 100644 --- a/public/include/pages/statistics/graphs.inc.php +++ b/public/include/pages/statistics/graphs.inc.php @@ -1,16 +1,19 @@ isAuthenticated()) { - $aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA']['id']); - $aPoolHourlyHashRates = $statistics->getHourlyHashrateByPool(); +if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { + $debug->append('No cached version available, fetching from backend', 3); + if ($user->isAuthenticated()) { + $aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA']['id']); + $aPoolHourlyHashRates = $statistics->getHourlyHashrateByPool(); + } + $smarty->assign("YOURHASHRATES", @$aHourlyHashRates); + $smarty->assign("POOLHASHRATES", @$aPoolHourlyHashRates); +} else { + $debug->append('Using cached page', 3); } -// Propagate content our template -$smarty->assign("YOURHASHRATES", @$aHourlyHashRates); -$smarty->assign("POOLHASHRATES", @$aPoolHourlyHashRates); $smarty->assign("CONTENT", "default.tpl"); ?> diff --git a/public/include/pages/statistics/pool.inc.php b/public/include/pages/statistics/pool.inc.php index 27395cc1..8386cb07 100644 --- a/public/include/pages/statistics/pool.inc.php +++ b/public/include/pages/statistics/pool.inc.php @@ -1,58 +1,63 @@ can_connect() === true){ - $dDifficulty = $bitcoin->getdifficulty(); - if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) - $dDifficulty = $dDifficulty['proof-of-work']; - $iBlock = $bitcoin->getblockcount(); +if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { + $debug->append('No cached version available, fetching from backend', 3); + // Fetch data from wallet + if ($bitcoin->can_connect() === true){ + $dDifficulty = $bitcoin->getdifficulty(); + if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) + $dDifficulty = $dDifficulty['proof-of-work']; + $iBlock = $bitcoin->getblockcount(); + } else { + $dDifficulty = 1; + $iBlock = 0; + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg'); + } + + // Top share contributors + $aContributorsShares = $statistics->getTopContributors('shares', 15); + + // Top hash contributors + $aContributorsHashes = $statistics->getTopContributors('hashes', 15); + + // Grab the last 10 blocks found + $iLimit = 5; + $aBlocksFoundData = $statistics->getBlocksFound($iLimit); + count($aBlocksFoundData) > 0 ? $aBlockData = $aBlocksFoundData[0] : $aBlockData = array(); + + // Estimated time to find the next block + $iCurrentPoolHashrate = $statistics->getCurrentHashrate(); + + // Time in seconds, not hours, using modifier in smarty to translate + $iCurrentPoolHashrate > 0 ? $iEstTime = $dDifficulty * pow(2,32) / ($iCurrentPoolHashrate * 1000) : $iEstTime = 0; + + // Time since last block + $now = new DateTime( "now" ); + if (!empty($aBlockData)) { + $dTimeSinceLast = ($now->getTimestamp() - $aBlockData['time']); + } else { + $dTimeSinceLast = 0; + } + + // Propagate content our template + $smarty->assign("ESTTIME", $iEstTime); + $smarty->assign("TIMESINCELAST", $dTimeSinceLast); + $smarty->assign("BLOCKSFOUND", $aBlocksFoundData); + $smarty->assign("BLOCKLIMIT", $iLimit); + $smarty->assign("CONTRIBSHARES", $aContributorsShares); + $smarty->assign("CONTRIBHASHES", $aContributorsHashes); + $smarty->assign("CURRENTBLOCK", $iBlock); + count($aBlockData) > 0 ? $smarty->assign("LASTBLOCK", $aBlockData['height']) : $smarty->assign("LASTBLOCK", 0); + $smarty->assign("DIFFICULTY", $dDifficulty); + $smarty->assign("REWARD", $config['reward']); } else { - $dDifficulty = 1; - $iBlock = 0; - $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg'); + $debug->append('Using cached page', 3); } -// Top share contributors -$aContributorsShares = $statistics->getTopContributors('shares', 15); - -// Top hash contributors -$aContributorsHashes = $statistics->getTopContributors('hashes', 15); - -// Grab the last 10 blocks found -$iLimit = 5; -$aBlocksFoundData = $statistics->getBlocksFound($iLimit); -count($aBlocksFoundData) > 0 ? $aBlockData = $aBlocksFoundData[0] : $aBlockData = array(); - -// Estimated time to find the next block -$iCurrentPoolHashrate = $statistics->getCurrentHashrate(); - -// Time in seconds, not hours, using modifier in smarty to translate -$iCurrentPoolHashrate > 0 ? $iEstTime = $dDifficulty * pow(2,32) / ($iCurrentPoolHashrate * 1000) : $iEstTime = 0; - -// Time since last block -$now = new DateTime( "now" ); -if (!empty($aBlockData)) { - $dTimeSinceLast = ($now->getTimestamp() - $aBlockData['time']); -} else { - $dTimeSinceLast = 0; -} - -// Propagate content our template -$smarty->assign("ESTTIME", $iEstTime); -$smarty->assign("TIMESINCELAST", $dTimeSinceLast); -$smarty->assign("BLOCKSFOUND", $aBlocksFoundData); -$smarty->assign("BLOCKLIMIT", $iLimit); -$smarty->assign("CONTRIBSHARES", $aContributorsShares); -$smarty->assign("CONTRIBHASHES", $aContributorsHashes); -$smarty->assign("CURRENTBLOCK", $iBlock); -count($aBlockData) > 0 ? $smarty->assign("LASTBLOCK", $aBlockData['height']) : $smarty->assign("LASTBLOCK", 0); -$smarty->assign("DIFFICULTY", $dDifficulty); -$smarty->assign("REWARD", $config['reward']); - +// Public / private page detection if ($config['website']['acl']['statistics']['pool'] == 'public') { $smarty->assign("CONTENT", "authenticated.tpl"); } else if ($user->isAuthenticated() && $config['website']['acl']['statistics']['pool'] == 'private') { diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index e83b1ca3..cbd369d3 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -2,7 +2,7 @@ // Make sure we are called from index.php if (!defined('SECURITY')) - die('Hacking attempt'); + die('Hacking attempt'); // Globally available variables $debug->append('Global smarty variables', 3); @@ -109,7 +109,7 @@ if (@$_SESSION['USERDATA']['id']) { // Site-wide notifications, based on user events if ($aGlobal['userdata']['balance']['confirmed'] >= $config['ap_threshold']['max']) - $_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded the pools configured ' . $config['currency'] . ' warning threshold. Please initiate a transfer!', 'TYPE' => 'warning'); + $_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded your accounts balance. Please transfer some ' . $config['currency'] . "!", 'TYPE' => 'errormsg'); if ($user->getUserFailed($_SESSION['USERDATA']['id']) > 0) $_SESSION['POPUP'][] = array('CONTENT' => 'You have ' . $user->getUserFailed($_SESSION['USERDATA']['id']) . ' failed login attempts! Reset Counter', 'TYPE' => 'errormsg'); } diff --git a/public/templates/mmcFE/master.tpl b/public/templates/mmcFE/master.tpl index f476f747..68777d1a 100644 --- a/public/templates/mmcFE/master.tpl +++ b/public/templates/mmcFE/master.tpl @@ -69,7 +69,7 @@
      - {include file="system/debugger.tpl"} + {nocache}{include file="system/debugger.tpl"}{/nocache}
      From e0275566ae3f8c2b83de98980ada6a5d5fb0442c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 8 Jul 2013 14:16:45 +0200 Subject: [PATCH 067/137] disable smarty cache by default in dist --- public/include/config/global.inc.dist.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index b0aa2f21..238654e7 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -371,6 +371,6 @@ $config['cookie']['domain'] = ''; * cache = 0, disabled * cache_lifetime = 30 seconds **/ -$config['smarty']['cache'] = 1; +$config['smarty']['cache'] = 0; $config['smarty']['cache_lifetime'] = 30; ?> From 65c6318b026a11845cc5b308dafd48f6fed83c41 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 9 Jul 2013 08:54:20 +0200 Subject: [PATCH 068/137] fixing empty variable when using cache --- public/include/pages/home.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/pages/home.inc.php b/public/include/pages/home.inc.php index ac7b458e..9431e219 100644 --- a/public/include/pages/home.inc.php +++ b/public/include/pages/home.inc.php @@ -16,11 +16,11 @@ if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { $aNews[$key]['content'] = Markdown::defaultTransform($aData['content']); } } + $smarty->assign("NEWS", $aNews); } else { $debug->append('Using cached page', 3); } // Load news entries for Desktop site and unauthenticated users -$smarty->assign("NEWS", $aNews); $smarty->assign("CONTENT", "default.tpl"); ?> From 17829cfd4ab146e34371b4a946092b201a6acfb9 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 09:13:50 +0200 Subject: [PATCH 069/137] always assign default content --- public/include/pages/account/transactions.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/pages/account/transactions.inc.php b/public/include/pages/account/transactions.inc.php index db927ca7..7bcfb4f2 100644 --- a/public/include/pages/account/transactions.inc.php +++ b/public/include/pages/account/transactions.inc.php @@ -6,6 +6,6 @@ if ($user->isAuthenticated()) { $aTransactions = $transaction->getTransactions($_SESSION['USERDATA']['id']); if (!$aTransactions) $_SESSION['POPUP'][] = array('CONTENT' => 'Could not find any transaction', 'TYPE' => 'errormsg'); $smarty->assign('TRANSACTIONS', $aTransactions); - $smarty->assign('CONTENT', 'default.tpl'); } +$smarty->assign('CONTENT', 'default.tpl'); ?> From 39cfdc78e03c541bf3c3fab089708dacbbb3aaa7 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 09:15:36 +0200 Subject: [PATCH 070/137] onliner security check --- public/include/smarty_globals.inc.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index 82938ee2..a1e0eb19 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -1,8 +1,7 @@ append('Global smarty variables', 3); From 72d91ff6c3fb4ffc8c184ab83e9f6de2b99ace06 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 09:16:31 +0200 Subject: [PATCH 071/137] adding proper account balance warning back in --- public/include/smarty_globals.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index a1e0eb19..7219778c 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -109,7 +109,7 @@ if (@$_SESSION['USERDATA']['id']) { // Site-wide notifications, based on user events if ($aGlobal['userdata']['balance']['confirmed'] >= $config['ap_threshold']['max']) - $_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded your accounts balance. Please transfer some ' . $config['currency'] . "!", 'TYPE' => 'errormsg'); + $_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded the pools configured ' . $config['currency'] . ' warning threshold. Please initiate a transfer!', 'TYPE' => 'errormsg'); if ($user->getUserFailed($_SESSION['USERDATA']['id']) > 0) $_SESSION['POPUP'][] = array('CONTENT' => 'You have ' . $user->getUserFailed($_SESSION['USERDATA']['id']) . ' failed login attempts! Reset Counter', 'TYPE' => 'errormsg'); } From 25b12d204a4ec48503f20608f4886f380fa63668 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 09:19:30 +0200 Subject: [PATCH 072/137] Better description what type uses what for PPLNS Fixes #422 --- public/include/config/global.inc.dist.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 73325a5c..c8f31d6e 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -246,6 +246,11 @@ $config['fees'] = 0; * when difficulty changes and more blocks are available. This keeps the * target dynamic but still traceable. * + * If you use the fixed type it will use $config['pplns']['shares']['default'] + * for target calculations, if you use blockavg type it will use + * $config['pplns']['blockavg']['blockcount'] blocks average for target + * calculations. + * * default : Default target shares for PPLNS * type : Payout type used in PPLNS * blockcount : Amount of blocks to check for avg shares From 5d568a462118e03d1c192d241e6d50a7dd081b2a Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 09:42:10 +0200 Subject: [PATCH 073/137] Adding CRONHOME detection to run-crons.sh This will ensure the cron will run without having to change the CRONHOME configuration. --- cronjobs/run-crons.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cronjobs/run-crons.sh b/cronjobs/run-crons.sh index e797cb44..ab1f482c 100755 --- a/cronjobs/run-crons.sh +++ b/cronjobs/run-crons.sh @@ -12,9 +12,6 @@ PHP_BIN=$( which php ) # Path to PID file, needs to be writable by user running this PIDFILE='/tmp/mmcfe-ng-cron.pid' -# Location of our cronjobs, assume current directory -CRONHOME='.' - # List of cruns to execute CRONS="findblock.php proportional_payout.php pplns_payout.php pps_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php statistics.php archive_cleanup.php" @@ -30,6 +27,9 @@ VERBOSE="0" # # ################################################################ +# Find scripts path +CRONHOME=$( dirname $0 ) + # Change working director to CRONHOME if ! cd $CRONHOME 2>/dev/null; then echo "Unable to change to working directory \$CRONHOME: $CRONHOME" From dad727a8f0a19df73ba66471da6e384a7da7df6f Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 09:50:51 +0200 Subject: [PATCH 074/137] Fix CRONHOME detection if a symlink is used This will fix CRONHOME detection if `run-rcrons.sh` is a symlink in `/etc/cron.minutely`. Before symlinks would not return the proper path. --- cronjobs/run-crons.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cronjobs/run-crons.sh b/cronjobs/run-crons.sh index ab1f482c..0988310c 100755 --- a/cronjobs/run-crons.sh +++ b/cronjobs/run-crons.sh @@ -28,7 +28,11 @@ VERBOSE="0" ################################################################ # Find scripts path -CRONHOME=$( dirname $0 ) +if [[ -L $0 ]]; then + CRONHOME=$( dirname $( readlink $0 ) ) +else + CRONHOME=$( dirname $0 ) +fi # Change working director to CRONHOME if ! cd $CRONHOME 2>/dev/null; then From 1344f39f9617ad346f5485656b32a413065be745 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 11:43:48 +0200 Subject: [PATCH 075/137] Ensure we set workers to 0 If no workers are found false is returned. Smarty is not able to set a default on `false` values so we have to set it to 0 if the query failed. Fixes #418 --- public/include/classes/worker.class.php | 10 ++-------- public/include/smarty_globals.inc.php | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/public/include/classes/worker.class.php b/public/include/classes/worker.class.php index ec7abdc9..7643a6d1 100644 --- a/public/include/classes/worker.class.php +++ b/public/include/classes/worker.class.php @@ -135,15 +135,9 @@ class Worker { **/ public function getCountAllActiveWorkers() { $this->debug->append("STA " . __METHOD__, 4); - $stmt = $this->mysqli->prepare("SELECT COUNT(DISTINCT username) AS total FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)"); - if ($this->checkStmt($stmt)) { - if (!$stmt->execute()) { - return false; - } - $result = $stmt->get_result(); - $stmt->close(); + $stmt = $this->mysqli->prepare("SELECT IFNULL(COUNT(DISTINCT username), 0) AS total FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)"); + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_object()->total; - } return false; } diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index e83b1ca3..33c5fda7 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -24,7 +24,7 @@ if (@$_SESSION['AUTHENTICATED']) { $bitcoin->can_connect() === true ? $dNetworkHashrate = $bitcoin->query('getnetworkhashps') : $dNetworkHashrate = 0; // Fetch some data -$iCurrentActiveWorkers = $worker->getCountAllActiveWorkers(); +if (!$iCurrentActiveWorkers = $worker->getCountAllActiveWorkers()) $iCurrentActiveWorkers = 0; $iCurrentPoolHashrate = $statistics->getCurrentHashrate(); $iCurrentPoolShareRate = $statistics->getCurrentShareRate(); From 875572813b3ca2a50da4a1bcec1034135ce9ed2d Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 13:25:22 +0200 Subject: [PATCH 076/137] Adding last runtime to monitoring page Displays the date/time of the last successful run. Fixes #431 --- cronjobs/cron_end.inc.php | 2 +- public/include/pages/admin/monitoring.inc.php | 8 ++++++++ public/templates/mmcFE/admin/monitoring/default.tpl | 3 +++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/cronjobs/cron_end.inc.php b/cronjobs/cron_end.inc.php index 177e5296..56607c45 100644 --- a/cronjobs/cron_end.inc.php +++ b/cronjobs/cron_end.inc.php @@ -22,7 +22,7 @@ limitations under the License. $monitoring->setStatus($cron_name . "_message", "message", "OK"); $monitoring->setStatus($cron_name . "_status", "okerror", 0); $monitoring->setStatus($cron_name . "_runtime", "time", microtime(true) - $cron_start[$cron_name]); - +$monitoring->setStatus($cron_name . "_lastrun", "date", time()); // Mark cron as running for monitoring $monitoring->setStatus($cron_name . '_active', "yesno", 0); ?> diff --git a/public/include/pages/admin/monitoring.inc.php b/public/include/pages/admin/monitoring.inc.php index efc20a6c..5a952a99 100644 --- a/public/include/pages/admin/monitoring.inc.php +++ b/public/include/pages/admin/monitoring.inc.php @@ -15,30 +15,35 @@ $aCronStatus = array( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('statistics_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('statistics_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('statistics_runtime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('statistics_lastrun') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('statistics_message') ), ), 'auto_payout' => array ( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('auto_payout_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('auto_payout_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('auto_payout_runtime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('auto_payout_lastrun') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('auto_payout_message') ), ), 'archive_cleanup' => array ( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('archive_cleanup_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('archive_cleanup_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('archive_cleanup_runtime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('archive_cleanup_lastrun') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('archive_cleanup_message') ), ), 'blockupdate' => array ( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('blockupdate_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('blockupdate_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('blockupdate_runtime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('blockupdate_lastrun') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('blockupdate_message') ), ), 'findblock' => array ( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('findblock_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('findblock_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('findblock_runtime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('findblock_lastrun') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('findblock_message') ), ) ); @@ -49,6 +54,7 @@ case 'pplns': array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('pplns_payout_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('pplns_payout_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('pplns_payout_runtime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('pplns_payout_lastrun') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('pplns_payout_message') ), ); break; @@ -57,6 +63,7 @@ case 'pps': array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('pps_payout_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('pps_payout_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('pps_payout_runtime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('pps_payout_lastrun') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('pps_payout_message') ), ); break; @@ -65,6 +72,7 @@ case 'prop': array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('proportional_payout_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('proportional_payout_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('proportional_payout_runtime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('proportional_payout_lastrun') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('proportional_payout_message') ), ); break; diff --git a/public/templates/mmcFE/admin/monitoring/default.tpl b/public/templates/mmcFE/admin/monitoring/default.tpl index 6213a164..856766ac 100644 --- a/public/templates/mmcFE/admin/monitoring/default.tpl +++ b/public/templates/mmcFE/admin/monitoring/default.tpl @@ -5,6 +5,7 @@ Exit Code Active Runtime + Last Run Message @@ -29,6 +30,8 @@ {/if} {else if $event.STATUS.type == 'time'} {$event.STATUS.value|default:"0"|number_format:"2"} seconds + {else if $event.STATUS.type == 'date'} + {$event.STATUS.value|date_format:"%m/%d %H:%M:%S"} {else} {$event.STATUS.value|default:""} {/if} From 739bd9c7b67009d470c97e9c029e0214a451b87f Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 13:45:54 +0200 Subject: [PATCH 077/137] Properly cache on a per-user basis This will fix an issue with templates of other users being applied to different users logged in. Basically the first cached page would be displayed for all users. Created a new cache key for smarty to allow the user ID to be reference in the cache key. Hence each user has their own cached file which will be used. Improved caching by creating subdirectories for cached files. This way we won't run into a file limit per directory with a lot of cached files. This fixes #430 and the mentioned issue in that report. --- public/include/pages/admin/transactions.inc.php | 2 +- public/include/pages/admin/wallet.inc.php | 2 +- public/include/pages/home.inc.php | 2 +- public/include/pages/statistics.inc.php | 2 +- public/include/pages/statistics/blocks.inc.php | 2 +- public/include/pages/statistics/graphs.inc.php | 2 +- public/include/pages/statistics/pool.inc.php | 2 +- public/include/smarty.inc.php | 2 ++ public/index.php | 2 +- 9 files changed, 10 insertions(+), 8 deletions(-) diff --git a/public/include/pages/admin/transactions.inc.php b/public/include/pages/admin/transactions.inc.php index b25d6f15..fe991ba5 100644 --- a/public/include/pages/admin/transactions.inc.php +++ b/public/include/pages/admin/transactions.inc.php @@ -9,7 +9,7 @@ if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) { die("404 Page not found"); } -if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { +if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { $debug->append('No cached version available, fetching from backend', 3); $aTransactions = $transaction->getAllTransactions(@$_REQUEST['start']); if (!$aTransactions) $_SESSION['POPUP'][] = array('CONTENT' => 'Could not find any transaction', 'TYPE' => 'errormsg'); diff --git a/public/include/pages/admin/wallet.inc.php b/public/include/pages/admin/wallet.inc.php index d0642b14..fee0fd16 100644 --- a/public/include/pages/admin/wallet.inc.php +++ b/public/include/pages/admin/wallet.inc.php @@ -9,7 +9,7 @@ if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) { die("404 Page not found"); } -if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { +if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { $debug->append('No cached version available, fetching from backend', 3); if ($bitcoin->can_connect() === true){ $dBalance = $bitcoin->query('getbalance'); diff --git a/public/include/pages/home.inc.php b/public/include/pages/home.inc.php index 9431e219..349b197d 100644 --- a/public/include/pages/home.inc.php +++ b/public/include/pages/home.inc.php @@ -6,7 +6,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); // Include markdown library use \Michelf\Markdown; -if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { +if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { $debug->append('No cached version available, fetching from backend', 3); // Fetch active news to display $aNews = $news->getAllActive(); diff --git a/public/include/pages/statistics.inc.php b/public/include/pages/statistics.inc.php index c5035f0c..076ce66f 100644 --- a/public/include/pages/statistics.inc.php +++ b/public/include/pages/statistics.inc.php @@ -4,7 +4,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); -if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { +if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { $debug->append('No cached version available, fetching from backend', 3); if ($bitcoin->can_connect() === true){ $dDifficulty = $bitcoin->query('getdifficulty'); diff --git a/public/include/pages/statistics/blocks.inc.php b/public/include/pages/statistics/blocks.inc.php index 98d5ed04..3218a9f4 100644 --- a/public/include/pages/statistics/blocks.inc.php +++ b/public/include/pages/statistics/blocks.inc.php @@ -4,7 +4,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); // Grab the last blocks found -if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { +if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { $debug->append('No cached version available, fetching from backend', 3); // Grab the last blocks found $iLimit = 20; diff --git a/public/include/pages/statistics/graphs.inc.php b/public/include/pages/statistics/graphs.inc.php index 4d31aadb..575ce36d 100644 --- a/public/include/pages/statistics/graphs.inc.php +++ b/public/include/pages/statistics/graphs.inc.php @@ -3,7 +3,7 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { +if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { $debug->append('No cached version available, fetching from backend', 3); if ($user->isAuthenticated()) { $aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA']['id']); diff --git a/public/include/pages/statistics/pool.inc.php b/public/include/pages/statistics/pool.inc.php index 8386cb07..f3669625 100644 --- a/public/include/pages/statistics/pool.inc.php +++ b/public/include/pages/statistics/pool.inc.php @@ -3,7 +3,7 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -if (!$smarty->isCached('master.tpl', md5(serialize($_REQUEST)))) { +if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { $debug->append('No cached version available, fetching from backend', 3); // Fetch data from wallet if ($bitcoin->can_connect() === true){ diff --git a/public/include/smarty.inc.php b/public/include/smarty.inc.php index e42e56e3..ac05814e 100644 --- a/public/include/smarty.inc.php +++ b/public/include/smarty.inc.php @@ -25,5 +25,7 @@ if ($config['smarty']['cache']) { $smarty->setCaching(Smarty::CACHING_LIFETIME_SAVED); $smarty->cache_lifetime = $config['smarty']['cache_lifetime']; $smarty->cache_dir = BASEPATH . "templates/cache"; + $smarty->use_sub_dirs = true; + $smarty_cache_key = md5(serialize($_REQUEST . @$_SESSION['USERDATA']['id'])); } ?> diff --git a/public/index.php b/public/index.php index dbb28f9a..36360172 100644 --- a/public/index.php +++ b/public/index.php @@ -82,7 +82,7 @@ $smarty->assign('DebuggerInfo', $debug->getDebugInfo()); // Display our page if (!@$supress_master) - $smarty->display("master.tpl", md5(serialize($_REQUEST))); + $smarty->display("master.tpl", $smarty_cache_key); // Unset any temporary values here unset($_SESSION['POPUP']); From dfde017267a953e1f609a88c356e0e4698ec049c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 14:26:53 +0200 Subject: [PATCH 078/137] Get rid of Orphan transaction types This fixes #432 and puts orphans on the same system as unconfirmed transactions. --- cronjobs/blockupdate.php | 2 +- public/include/classes/transaction.class.php | 54 ++++++++----------- .../mmcFE/account/transactions/default.tpl | 29 +++------- .../mmcFE/admin/transactions/default.tpl | 25 ++------- .../templates/mmcFE/global/sidebar_pplns.tpl | 1 + .../templates/mmcFE/global/sidebar_prop.tpl | 1 + 6 files changed, 38 insertions(+), 74 deletions(-) diff --git a/cronjobs/blockupdate.php b/cronjobs/blockupdate.php index 67d106b7..e11063bb 100755 --- a/cronjobs/blockupdate.php +++ b/cronjobs/blockupdate.php @@ -41,7 +41,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $log->logInfo($aBlock['id'] . "\t" . $aBlock['height'] . "\t" . $aBlock['blockhash'] . "\t" . $aBlock['confirmations'] . " -> " . $aBlockInfo['confirmations']); if ($aTxDetails['details'][0]['category'] == 'orphan') { // We have an orphaned block, we need to invalidate all transactions for this one - if ($transaction->setOrphan($aBlock['id']) && $block->setConfirmations($aBlock['id'], -1)) { + if ($block->setConfirmations($aBlock['id'], -1)) { $log->logInfo(" Block marked as orphan"); } else { $log->logError(" Block became orphaned but unable to update database entries"); diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index 2d65657b..d01a6d18 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -48,35 +48,6 @@ class Transaction { return false; } - /** - * Sometimes transactions become orphans when a block associated to them is orphaned - * Updates the transaction types to Orphan_ - * @param block_id int Orphaned block ID - * @return bool - **/ - public function setOrphan($block_id) { - $this->debug->append("STA " . __METHOD__, 4); - $aOrphans = array( - 'Credit' => 'Orphan_Credit', - 'Fee' => 'Orphan_Fee', - 'Donation' => 'Orphan_Donation', - 'Bonus' => 'Orphan_Bonus' - ); - foreach ($aOrphans as $from => $to) { - $stmt = $this->mysqli->prepare(" - UPDATE $this->table - SET type = '$to' - WHERE type = '$from' - AND block_id = ? - "); - if (!($this->checkStmt($stmt) && $stmt->bind_param('i', $block_id) && $stmt->execute())) { - $this->debug->append("Failed to set orphan $from => $to transactions for $block_id"); - return false; - } - } - return true; - } - /** * Get all transactions from start for account_id * @param account_id int Account ID @@ -227,7 +198,8 @@ class Transaction { $stmt = $this->mysqli->prepare(" SELECT ROUND(IFNULL(t1.credit, 0) - IFNULL(t2.debit, 0) - IFNULL(t3.other, 0), 8) AS confirmed, - ROUND(IFNULL(t4.credit, 0) - IFNULL(t5.other, 0), 8) AS unconfirmed + ROUND(IFNULL(t4.credit, 0) - IFNULL(t5.other, 0), 8) AS unconfirmed, + ROUND(IFNULL(t6.credit, 0) - IFNULL(t7.other, 0), 8) AS orphaned FROM ( SELECT sum(t.amount) AS credit @@ -274,10 +246,28 @@ class Transaction { t.type IN ('Donation','Fee') AND b.confirmations < ? ) AND t.account_id = ? - ) AS t5 + ) AS t5, + ( + SELECT sum(t.amount) AS credit + FROM $this->table AS t + LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id + WHERE + t.type IN ('Credit','Bonus') AND b.confirmations = -1 + AND t.account_id = ? + ) AS t6, + ( + SELECT sum(t.amount) AS other + FROM $this->table AS t + LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id + WHERE + ( + t.type IN ('Donation','Fee') AND b.confirmations = -1 + ) + AND t.account_id = ? + ) AS t7 "); if ($this->checkStmt($stmt)) { - $stmt->bind_param("iiiiiiiii", $this->config['confirmations'], $account_id, $account_id, $this->config['confirmations'], $account_id, $this->config['confirmations'], $account_id, $this->config['confirmations'], $account_id); + $stmt->bind_param("iiiiiiiiiii", $this->config['confirmations'], $account_id, $account_id, $this->config['confirmations'], $account_id, $this->config['confirmations'], $account_id, $this->config['confirmations'], $account_id, $account_id, $account_id); if (!$stmt->execute()) { $this->debug->append("Unable to execute statement: " . $stmt->error); $this->setErrorMessage("Fetching balance failed"); diff --git a/public/templates/mmcFE/account/transactions/default.tpl b/public/templates/mmcFE/account/transactions/default.tpl index 160b12e4..4bbe7b4b 100644 --- a/public/templates/mmcFE/account/transactions/default.tpl +++ b/public/templates/mmcFE/account/transactions/default.tpl @@ -17,15 +17,9 @@ {assign var=has_confirmed value=false} {section transaction $TRANSACTIONS} {if ( - (($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus')and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations) - or ($TRANSACTIONS[transaction].type == 'Donation' and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations) - or ($TRANSACTIONS[transaction].type == 'Fee' and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations) - or $TRANSACTIONS[transaction].type == 'Credit_PPS' - or $TRANSACTIONS[transaction].type == 'Fee_PPS' - or $TRANSACTIONS[transaction].type == 'Donation_PPS' - or $TRANSACTIONS[transaction].type == 'Debit_AP' - or $TRANSACTIONS[transaction].type == 'Debit_MP' - or $TRANSACTIONS[transaction].type == 'TXFee' + ( ( $TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus' or $TRANSACTIONS[transaction].type == 'Donation' or $TRANSACTIONS[transaction].type == 'Fee' ) and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations ) + or $TRANSACTIONS[transaction].type == 'Credit_PPS' or $TRANSACTIONS[transaction].type == 'Fee_PPS' or $TRANSACTIONS[transaction].type == 'Donation_PPS' + or $TRANSACTIONS[transaction].type == 'Debit_AP' or $TRANSACTIONS[transaction].type == 'Debit_MP' or $TRANSACTIONS[transaction].type == 'TXFee' )} {assign var=has_credits value=true} @@ -67,11 +61,9 @@ {assign var=has_unconfirmed value=false} {section transaction $TRANSACTIONS} - {if ( - ($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus') and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations - or ($TRANSACTIONS[transaction].type == 'Donation' and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations) - or ($TRANSACTIONS[transaction].type == 'Fee' and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations) - )} + {if + (($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus' or $TRANSACTIONS[transaction].type == 'Donation' or $TRANSACTIONS[transaction].type == 'Fee') and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations) + } {assign var=has_unconfirmed value=true} {$TRANSACTIONS[transaction].id} @@ -117,12 +109,7 @@ {assign var=has_orphaned value=false} {section transaction $TRANSACTIONS} - {if ( - $TRANSACTIONS[transaction].type == 'Orphan_Credit' - or $TRANSACTIONS[transaction].type == 'Orphan_Donation' - or $TRANSACTIONS[transaction].type == 'Orphan_Fee' - or $TRANSACTIONS[transaction].type == 'Orphan_Bonus' - )} + {if ($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Fee' or $TRANSACTIONS[transaction].type == 'Donation' or $TRANSACTIONS[transaction].type == 'Bonus') and $TRANSACTIONS[transaction].confirmations == -1} {$TRANSACTIONS[transaction].id} {$TRANSACTIONS[transaction].timestamp} @@ -131,7 +118,7 @@ {if $TRANSACTIONS[transaction].height == 0}n/a{else}{$TRANSACTIONS[transaction].height}{/if} {$TRANSACTIONS[transaction].amount|number_format:"8"} - {if $TRANSACTIONS[transaction].type == 'Orphan_Credit' or $TRANSACTIONS[transaction].type == 'Orphan_Bonus'} + {if $TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus'} {assign var="orphan_credits" value="`$orphan_credits|default:"0"+$TRANSACTIONS[transaction].amount`"} {else} {assign var="orphan_debits" value="`$orphan_debits|default:"0"+$TRANSACTIONS[transaction].amount`"} diff --git a/public/templates/mmcFE/admin/transactions/default.tpl b/public/templates/mmcFE/admin/transactions/default.tpl index d611f6d6..aba15f12 100644 --- a/public/templates/mmcFE/admin/transactions/default.tpl +++ b/public/templates/mmcFE/admin/transactions/default.tpl @@ -21,15 +21,9 @@ {assign var=confirmed value=0} {section transaction $TRANSACTIONS} {if ( - (($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus')and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations) - or ($TRANSACTIONS[transaction].type == 'Donation' and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations) - or ($TRANSACTIONS[transaction].type == 'Fee' and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations) - or $TRANSACTIONS[transaction].type == 'Credit_PPS' - or $TRANSACTIONS[transaction].type == 'Fee_PPS' - or $TRANSACTIONS[transaction].type == 'Donation_PPS' - or $TRANSACTIONS[transaction].type == 'Debit_AP' - or $TRANSACTIONS[transaction].type == 'Debit_MP' - or $TRANSACTIONS[transaction].type == 'TXFee' + ( ( $TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus' or $TRANSACTIONS[transaction].type == 'Donation' or $TRANSACTIONS[transaction].type == 'Fee' ) and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations ) + or $TRANSACTIONS[transaction].type == 'Credit_PPS' or $TRANSACTIONS[transaction].type == 'Fee_PPS' or $TRANSACTIONS[transaction].type == 'Donation_PPS' + or $TRANSACTIONS[transaction].type == 'Debit_AP' or $TRANSACTIONS[transaction].type == 'Debit_MP' or $TRANSACTIONS[transaction].type == 'TXFee' )} {assign var=confirmed value=1} @@ -74,11 +68,7 @@ {assign var=unconfirmed value=0} {section transaction $TRANSACTIONS} - {if ( - ($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus') and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations - or ($TRANSACTIONS[transaction].type == 'Donation' and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations) - or ($TRANSACTIONS[transaction].type == 'Fee' and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations) - )} + {if ($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus' or $TRANSACTIONS[transaction].type == 'Donation' or $TRANSACTIONS[transaction].type == 'Fee') and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations} {assign var=unconfirmed value=1} {$TRANSACTIONS[transaction].id} @@ -118,12 +108,7 @@ {assign var=orphaned value=0} {section transaction $TRANSACTIONS} - {if ( - $TRANSACTIONS[transaction].type == 'Orphan_Credit' - or $TRANSACTIONS[transaction].type == 'Orphan_Donation' - or $TRANSACTIONS[transaction].type == 'Orphan_Fee' - or $TRANSACTIONS[transaction].type == 'Orphan_Bonus' - )} + {if ($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Fee' or $TRANSACTIONS[transaction].type == 'Donation' or $TRANSACTIONS[transaction].type == 'Bonus') and $TRANSACTIONS[transaction].confirmations == -1} {assign var=orphaned value=1} {$TRANSACTIONS[transaction].id} diff --git a/public/templates/mmcFE/global/sidebar_pplns.tpl b/public/templates/mmcFE/global/sidebar_pplns.tpl index deb0c1cc..79ffec8a 100644 --- a/public/templates/mmcFE/global/sidebar_pplns.tpl +++ b/public/templates/mmcFE/global/sidebar_pplns.tpl @@ -67,6 +67,7 @@ {$GLOBAL.config.currency} Account Balance Confirmed{$GLOBAL.userdata.balance.confirmed|default:"0"} Unconfirmed{$GLOBAL.userdata.balance.unconfirmed|default:"0"} + Orphaned{$GLOBAL.userdata.balance.orphaned|default:"0"}
      diff --git a/public/templates/mmcFE/global/sidebar_prop.tpl b/public/templates/mmcFE/global/sidebar_prop.tpl index 09159318..c19c429d 100644 --- a/public/templates/mmcFE/global/sidebar_prop.tpl +++ b/public/templates/mmcFE/global/sidebar_prop.tpl @@ -62,6 +62,7 @@ {$GLOBAL.config.currency} Account Balance Confirmed{$GLOBAL.userdata.balance.confirmed|default:"0"} Unconfirmed{$GLOBAL.userdata.balance.unconfirmed|default:"0"} + Orphaned{$GLOBAL.userdata.balance.orphaned|default:"0"}
      From 5b504226754e48418cae671647f2511f331a162a Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 15:49:08 +0200 Subject: [PATCH 079/137] Fixing XSS for user registration --- public/include/classes/user.class.php | 7 ++++++- public/templates/mmcFE/admin/user/default.tpl | 4 ++-- public/templates/mmcFE/global/userinfo.tpl | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 68616e3e..53b86d6f 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -442,6 +442,10 @@ class User { **/ public function register($username, $password1, $password2, $pin, $email1='', $email2='') { $this->debug->append("STA " . __METHOD__, 4); + if (strlen($username > 40)) { + $this->setErrorMessage('Username exceeding character limit'); + return false; + } if ($this->getEmail($email1)) { $this->setErrorMessage( 'This e-mail address is already taken' ); return false; @@ -482,8 +486,9 @@ class User { $password_hash = $this->getHash($password1); $pin_hash = $this->getHash($pin); $apikey_hash = $this->getHash($username); + $username_clean = strip_tags($username); - if ($this->checkStmt($stmt) && $stmt->bind_param('sssss', $username, $password_hash, $email1, $pin_hash, $apikey_hash)) { + if ($this->checkStmt($stmt) && $stmt->bind_param('sssss', $username_clean, $password_hash, $email1, $pin_hash, $apikey_hash)) { if (!$stmt->execute()) { $this->setErrorMessage( 'Unable to register' ); if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username or email already registered' ); diff --git a/public/templates/mmcFE/admin/user/default.tpl b/public/templates/mmcFE/admin/user/default.tpl index 8c65c49f..fef10bfa 100644 --- a/public/templates/mmcFE/admin/user/default.tpl +++ b/public/templates/mmcFE/admin/user/default.tpl @@ -48,8 +48,8 @@ {section name=user loop=$USERS|default} {$USERS[user].id} - {$USERS[user].username} - {$USERS[user].email} + {$USERS[user].username|escape} + {$USERS[user].email|escape} {$USERS[user].shares} {$USERS[user].hashrate} {$USERS[user].payout.est_donation|number_format:"8"} diff --git a/public/templates/mmcFE/global/userinfo.tpl b/public/templates/mmcFE/global/userinfo.tpl index 92105bdc..d9745394 100644 --- a/public/templates/mmcFE/global/userinfo.tpl +++ b/public/templates/mmcFE/global/userinfo.tpl @@ -1,5 +1,5 @@ {if $GLOBAL.userdata.username|default} -

      Welcome, {$smarty.session.USERDATA.username} Active Account: {$GLOBAL.fees}% Pool Fee (You are donating {$GLOBAL.userdata.donate_percent}% of your earnings)

      +

      Welcome, {$smarty.session.USERDATA.username|escape} Active Account: {$GLOBAL.fees|escape}% Pool Fee (You are donating {$GLOBAL.userdata.donate_percent|escape}% of your earnings)

      {else}

      Welcome guest, please register to user this pool.

      {/if} From 08359c0d19a82e14c6f0c1e2a9867131dba32cf4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 15:53:38 +0200 Subject: [PATCH 080/137] Further escaping of user inputs --- public/templates/mmcFE/statistics/blocks/default.tpl | 2 +- public/templates/mmcFE/statistics/blocks/small_table.tpl | 2 +- .../templates/mmcFE/statistics/pool/contributors_hashrate.tpl | 4 ++-- .../templates/mmcFE/statistics/pool/contributors_shares.tpl | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/public/templates/mmcFE/statistics/blocks/default.tpl b/public/templates/mmcFE/statistics/blocks/default.tpl index f404673c..0856a269 100644 --- a/public/templates/mmcFE/statistics/blocks/default.tpl +++ b/public/templates/mmcFE/statistics/blocks/default.tpl @@ -57,7 +57,7 @@ target and network difficulty and assuming a zero variance scenario. {else if $BLOCKSFOUND[block].confirmations == -1} Orphan {else}{$GLOBAL.confirmations - $BLOCKSFOUND[block].confirmations} left{/if} - {$BLOCKSFOUND[block].finder|default:"unknown"} + {$BLOCKSFOUND[block].finder|default:"unknown"|escape} {$BLOCKSFOUND[block].time|date_format:"%d/%m %H:%M:%S"} {$BLOCKSFOUND[block].difficulty|number_format:"2"} {$BLOCKSFOUND[block].amount|number_format:"2"} diff --git a/public/templates/mmcFE/statistics/blocks/small_table.tpl b/public/templates/mmcFE/statistics/blocks/small_table.tpl index 731d57ef..2b0f8aac 100644 --- a/public/templates/mmcFE/statistics/blocks/small_table.tpl +++ b/public/templates/mmcFE/statistics/blocks/small_table.tpl @@ -14,7 +14,7 @@ {section block $BLOCKSFOUND} {$BLOCKSFOUND[block].height} - {$BLOCKSFOUND[block].finder|default:"unknown"} + {$BLOCKSFOUND[block].finder|default:"unknown"|escape} {$BLOCKSFOUND[block].time|date_format:"%d/%m %H:%M:%S"} {$BLOCKSFOUND[block].shares|number_format} diff --git a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl index a2a6ed58..b6168c36 100644 --- a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl +++ b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl @@ -17,7 +17,7 @@ {math assign="estday" equation="round(reward / ( diff * pow(2,32) / ( hashrate * 1000 ) / 3600 / 24), 3)" diff=$DIFFICULTY reward=$REWARD hashrate=$CONTRIBHASHES[contrib].hashrate} {$rank++} - {$CONTRIBHASHES[contrib].account} + {$CONTRIBHASHES[contrib].account|escape} {$CONTRIBHASHES[contrib].hashrate|number_format} {$estday|number_format:"3"} {if $GLOBAL.config.price.currency}{($estday * $GLOBAL.price)|default:"n/a"|number_format:"2"}{/if} @@ -27,7 +27,7 @@ {if $GLOBAL.userdata.hashrate > 0}{math assign="myestday" equation="round(reward / ( diff * pow(2,32) / ( hashrate * 1000 ) / 3600 / 24), 3)" diff=$DIFFICULTY reward=$REWARD hashrate=$GLOBAL.userdata.hashrate}{/if} n/a - {$GLOBAL.userdata.username} + {$GLOBAL.userdata.username|escape} {$GLOBAL.userdata.hashrate} {$myestday|number_format:"3"|default:"n/a"} {if $GLOBAL.config.price.currency}{($myestday * $GLOBAL.price)|default:"n/a"|number_format:"2"}{/if} diff --git a/public/templates/mmcFE/statistics/pool/contributors_shares.tpl b/public/templates/mmcFE/statistics/pool/contributors_shares.tpl index 2a482209..232e76b2 100644 --- a/public/templates/mmcFE/statistics/pool/contributors_shares.tpl +++ b/public/templates/mmcFE/statistics/pool/contributors_shares.tpl @@ -14,14 +14,14 @@ {section hashrate $CONTRIBSHARES} {$rank++} - {$CONTRIBSHARES[hashrate].account} + {$CONTRIBSHARES[hashrate].account|escape} {$CONTRIBSHARES[hashrate].shares|number_format} {/section} {if $listed != 1 && $GLOBAL.userdata.username|default:""} n/a - {$GLOBAL.userdata.username} + {$GLOBAL.userdata.username|escape} {$GLOBAL.userdata.shares.valid|number_format} {/if} From 7466689b507159c82cbee796c48c26f1ee58f954 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 15:55:32 +0200 Subject: [PATCH 081/137] further escapes on templates --- public/templates/mmcFE/account/edit/default.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/templates/mmcFE/account/edit/default.tpl b/public/templates/mmcFE/account/edit/default.tpl index c15b56d4..fd445286 100644 --- a/public/templates/mmcFE/account/edit/default.tpl +++ b/public/templates/mmcFE/account/edit/default.tpl @@ -4,7 +4,7 @@ - + From 5c0d9921346b2aa23a352a960b58a907bbf84a29 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 15:58:30 +0200 Subject: [PATCH 082/137] fixing smarty cache key generation --- public/include/smarty.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/smarty.inc.php b/public/include/smarty.inc.php index ac05814e..f95180d7 100644 --- a/public/include/smarty.inc.php +++ b/public/include/smarty.inc.php @@ -26,6 +26,6 @@ if ($config['smarty']['cache']) { $smarty->cache_lifetime = $config['smarty']['cache_lifetime']; $smarty->cache_dir = BASEPATH . "templates/cache"; $smarty->use_sub_dirs = true; - $smarty_cache_key = md5(serialize($_REQUEST . @$_SESSION['USERDATA']['id'])); + $smarty_cache_key = md5(serialize($_REQUEST) . serialize(@$_SESSION['USERDATA']['id'])); } ?> From 450cc4d24df975aee4aac80f72cd7cf40a9d7b1f Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 16:07:51 +0200 Subject: [PATCH 083/137] Command-line switch for PHP Binary and Verbosity This will fix #429 --- cronjobs/run-crons.sh | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/cronjobs/run-crons.sh b/cronjobs/run-crons.sh index 0988310c..de30645a 100755 --- a/cronjobs/run-crons.sh +++ b/cronjobs/run-crons.sh @@ -15,9 +15,6 @@ PIDFILE='/tmp/mmcfe-ng-cron.pid' # List of cruns to execute CRONS="findblock.php proportional_payout.php pplns_payout.php pps_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php statistics.php archive_cleanup.php" -# Additional arguments to pass to cronjobs -CRONARGS="-v" - # Output additional runtime information VERBOSE="0" @@ -27,6 +24,20 @@ VERBOSE="0" # # ################################################################ +# Overwrite some settings via command line arguments +while getopts "hvp:" opt; do + case "$opt" in + h|\?) + echo "Usage: $0 [-v] [-p PHP_BINARY]"; + exit 0 + ;; + v) VERBOSE=1 + ;; + p) PHP_BIN=$3; shift; + ;; + esac +done + # Find scripts path if [[ -L $0 ]]; then CRONHOME=$( dirname $( readlink $0 ) ) @@ -70,8 +81,8 @@ fi echo $PID > $PIDFILE for cron in $CRONS; do - [[ $VERBOSE == 1 ]] && echo "Running $cron, see output below for details" - $PHP_BIN $cron $CRONARGS + [[ $VERBOSE == 1 ]] && echo "Running $cron, check logfile for details" + $PHP_BIN $cron done # Remove pidfile From 65047d3f09cc9ed6645eb9d42842c98967a1450e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 16:09:21 +0200 Subject: [PATCH 084/137] wrong argument option --- cronjobs/run-crons.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cronjobs/run-crons.sh b/cronjobs/run-crons.sh index de30645a..1f55bd77 100755 --- a/cronjobs/run-crons.sh +++ b/cronjobs/run-crons.sh @@ -33,7 +33,7 @@ while getopts "hvp:" opt; do ;; v) VERBOSE=1 ;; - p) PHP_BIN=$3; shift; + p) PHP_BIN=$2; shift; ;; esac done From 09c00877758ce6dc21fcf2c66d4932ce056ac6be Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 11 Jul 2013 16:50:24 +0200 Subject: [PATCH 085/137] Add tickerupdate to cron monitoring Fixes #439 --- cronjobs/tickerupdate.php | 1 + public/include/pages/admin/monitoring.inc.php | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/cronjobs/tickerupdate.php b/cronjobs/tickerupdate.php index 4b36adb2..875be2b2 100755 --- a/cronjobs/tickerupdate.php +++ b/cronjobs/tickerupdate.php @@ -33,4 +33,5 @@ if ($price = $tools->getPrice()) { $log->logFatal("failed to fetch API data: " . $tools->getError()); } +require_once('cron_end.inc.php'); ?> diff --git a/public/include/pages/admin/monitoring.inc.php b/public/include/pages/admin/monitoring.inc.php index 5a952a99..0d7dd671 100644 --- a/public/include/pages/admin/monitoring.inc.php +++ b/public/include/pages/admin/monitoring.inc.php @@ -45,6 +45,13 @@ $aCronStatus = array( array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('findblock_runtime') ), array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('findblock_lastrun') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('findblock_message') ), + ), + 'tickerupdate' => array ( + array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('tickerupdate_status') ), + array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('tickerupdate_active') ), + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('tickerupdate_runtime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('tickerupdate_lastrun') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('tickerupdate_message') ), ) ); // Payout system specifics From 15e89ad4d3cb7a800f11d692d476003b7896e6bb Mon Sep 17 00:00:00 2001 From: Ilya Stromberg Date: Thu, 11 Jul 2013 19:11:03 +0400 Subject: [PATCH 086/137] (#409) Do not use Memcached if it switched off via config --- public/include/classes/statscache.class.php | 24 +++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/public/include/classes/statscache.class.php b/public/include/classes/statscache.class.php index e84386da..c3e9ea38 100644 --- a/public/include/classes/statscache.class.php +++ b/public/include/classes/statscache.class.php @@ -9,12 +9,16 @@ if (!defined('SECURITY')) * Can be enabled or disabled through site configuration * Also sets a default time if no time is passed to it to enforce caching **/ -class StatsCache extends Memcached { +class StatsCache { + private $cache; + public function __construct($config, $debug) { $this->config = $config; $this->debug = $debug; - if (! $config['memcache']['enabled'] ) $this->debug->append("Not storing any values in memcache"); - return parent::__construct(); + if (! $config['memcache']['enabled'] ) { + $this->debug->append("Not storing any values in memcache"); + } + else { $this->cache = new Memcached(); } } /** @@ -26,7 +30,7 @@ class StatsCache extends Memcached { if (empty($expiration)) $expiration = $this->config['memcache']['expiration'] + rand( -$this->config['memcache']['splay'], $this->config['memcache']['splay']); $this->debug->append("Storing " . $this->config['memcache']['keyprefix'] . "$key with expiration $expiration", 3); - return parent::set($this->config['memcache']['keyprefix'] . $key, $value, $expiration); + return $this->cache->set($this->config['memcache']['keyprefix'] . $key, $value, $expiration); } /** @@ -36,7 +40,7 @@ class StatsCache extends Memcached { public function get($key, $cache_cb = NULL, &$cas_token = NULL) { if (! $this->config['memcache']['enabled']) return false; $this->debug->append("Trying to fetch key " . $this->config['memcache']['keyprefix'] . "$key from cache", 3); - if ($data = parent::get($this->config['memcache']['keyprefix'].$key)) { + if ($data = $this->cache->get($this->config['memcache']['keyprefix'].$key)) { $this->debug->append("Found key in cache", 3); return $data; } else { @@ -55,7 +59,15 @@ class StatsCache extends Memcached { if ($this->config['memcache']['enabled']) $this->set($key, $data, $expiration); return $data; } - + + /** + * This method is invoked if the called method was not realised in this class + **/ + public function __call($name, $arguments) { + if (! $this->config['memcache']['enabled']) return false; + //Invoke method $name of $this->cache class with array of $arguments + return call_user_func_array(array($this->cache, $name), $arguments); + } } $memcache = new StatsCache($config, $debug); From 8f4b4ed9701611c027eb71aeca6f2a7cd74abe05 Mon Sep 17 00:00:00 2001 From: Ilya Stromberg Date: Thu, 11 Jul 2013 19:35:23 +0400 Subject: [PATCH 087/137] Fix code style --- public/include/classes/statscache.class.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/include/classes/statscache.class.php b/public/include/classes/statscache.class.php index c3e9ea38..e64d5d02 100644 --- a/public/include/classes/statscache.class.php +++ b/public/include/classes/statscache.class.php @@ -17,8 +17,9 @@ class StatsCache { $this->debug = $debug; if (! $config['memcache']['enabled'] ) { $this->debug->append("Not storing any values in memcache"); + } else { + $this->cache = new Memcached(); } - else { $this->cache = new Memcached(); } } /** From 521bcc80220ce68db186ecfc4acdf641c45477aa Mon Sep 17 00:00:00 2001 From: Ilya Stromberg Date: Thu, 11 Jul 2013 20:19:35 +0400 Subject: [PATCH 088/137] Fix built-in documentation --- public/include/config/global.inc.dist.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 1cf894bf..b564d6a9 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -315,10 +315,12 @@ $config['confirmations'] = 120; /** * Memcache configuration * - * Even though you can disable the memcache for debugging purposes, the memcache - * library is still required for mmcfe-ng to work. You should not disable this in - * a live environment since a lot of data is cached for the website to increase load - * times! + * To disable memcache set option $config['memcache']['enabled'] = false + * After disable memcache installation of memcache is not required. + * + * Please note that a memcache is greatly increasing performance + * when combined with the `statistics.php` cronjob. Disabling this + * is not recommended in a live environment! * * Explanations * enabled : Disable (false) memcache for debugging or enable (true) it From 638b8387c36e6edafbde258595c74d011a680c13 Mon Sep 17 00:00:00 2001 From: Iain Kay Date: Thu, 11 Jul 2013 19:17:49 +0000 Subject: [PATCH 089/137] Updated global.inc.php to reflect the new values required for cookie configuration and documented each of the options. --- public/include/config/global.inc.dist.php | 37 ++++++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 1cf894bf..33aa1151 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -349,16 +349,43 @@ $config['memcache']['splay'] = 15; /** * Cookie configiration * - * For multiple installations of this cookie change the cookie name + * You can configure the cookie behaviour to secure your cookies more than the PHP defaults + * + * For multiple installations of mmcfe-ng on the same domain you must change the cookie + * path or change the cookie name to avoid conflicts. + * + * Description + * duration: the amount of time, in seconds, that a cookie should persist in the users + * browser. 0 = until closed; 1440 = 24 minutes. + * + * domain: the only domain name that may access this cookie in the browser + * + * path: the highest path on the domain that can access this cookie; i.e. if running + * two pools from a single domain you might set the path /ltc/ and /ftc/ to + * separate user session cookies between the two. + * + * httponly: marks the cookie as accessible only through the HTTP protocol. The cookie + * can't be accessed by scripting languages, such as JavaScript. This can + * help to reduce identity theft through XSS attacks in most browsers. + * + * secure: marks the cookie as accessible only through the HTTPS protocol. If you're + * using SSL this will stop a user accidently accessing the site without SSL + * and exposing their session cookie. * * Default: - * path = '/' - * name = 'POOLERCOOKIE' - * domain = '' + * duration = '1440' + * domain = '' + * path = '/' + * name = 'POOLERCOOKIE' + * httponly = true + * secure = false **/ +$config['cookie']['duration'] = '1440'; +$config['cookie']['domain'] = ''; $config['cookie']['path'] = '/'; $config['cookie']['name'] = 'POOLERCOOKIE'; -$config['cookie']['domain'] = ''; +$config['cookie']['httponly'] = true; +$config['cookie']['secure'] = false; /** * Enable or disable the Smarty cache From d2bbc366d16b83baa9c9f713355bdc1a32c27451 Mon Sep 17 00:00:00 2001 From: Iain Kay Date: Thu, 11 Jul 2013 19:26:09 +0000 Subject: [PATCH 090/137] Changed the Cookie Explanation in global.inc.php to be more in line with the rest of the structure. --- public/include/config/global.inc.dist.php | 31 +++++++++++++---------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 33aa1151..a83f47be 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -354,23 +354,28 @@ $config['memcache']['splay'] = 15; * For multiple installations of mmcfe-ng on the same domain you must change the cookie * path or change the cookie name to avoid conflicts. * - * Description - * duration: the amount of time, in seconds, that a cookie should persist in the users - * browser. 0 = until closed; 1440 = 24 minutes. + * Explanation: + * duration: + * the amount of time, in seconds, that a cookie should persist in the users browser. + * 0 = until closed; 1440 = 24 minutes. * - * domain: the only domain name that may access this cookie in the browser + * domain: + * the only domain name that may access this cookie in the browser * - * path: the highest path on the domain that can access this cookie; i.e. if running - * two pools from a single domain you might set the path /ltc/ and /ftc/ to - * separate user session cookies between the two. + * path: + * the highest path on the domain that can access this cookie; i.e. if running two pools + * from a single domain you might set the path /ltc/ and /ftc/ to separate user session + * cookies between the two. * - * httponly: marks the cookie as accessible only through the HTTP protocol. The cookie - * can't be accessed by scripting languages, such as JavaScript. This can - * help to reduce identity theft through XSS attacks in most browsers. + * httponly: + * marks the cookie as accessible only through the HTTP protocol. The cookie can't be + * accessed by scripting languages, such as JavaScript. This can help to reduce identity + * theft through XSS attacks in most browsers. * - * secure: marks the cookie as accessible only through the HTTPS protocol. If you're - * using SSL this will stop a user accidently accessing the site without SSL - * and exposing their session cookie. + * secure: + * marks the cookie as accessible only through the HTTPS protocol. If you have a SSL + * certificate installed on your domain name then this will stop a user accidently + * accessing the site over a HTTP connection, without SSL, exposing their session cookie. * * Default: * duration = '1440' From 9f4789c707591f21e09c5b9e51aec93ee7d0c986 Mon Sep 17 00:00:00 2001 From: Iain Kay Date: Thu, 11 Jul 2013 19:29:24 +0000 Subject: [PATCH 091/137] In order to read the cookie configuration from include/config/globa.inc.php the session must begin after this has been included. --- public/index.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/index.php b/public/index.php index dbb28f9a..ad58b9e0 100644 --- a/public/index.php +++ b/public/index.php @@ -24,13 +24,13 @@ define("BASEPATH", "./"); // Our security check define("SECURITY", 1); +// Include our configuration (holding defines for the requires) +if (!include_once(BASEPATH . 'include/config/global.inc.php')) die('Unable to load site configuration'); + // Start a session session_start(); $session_id = session_id(); -// Include our configuration (holding defines for the requires) -if (!include_once(BASEPATH . 'include/config/global.inc.php')) die('Unable to load site configuration'); - // Load Classes, they name defines the $ variable used // We include all needed files here, even though our templates could load them themself require_once(INCLUDE_DIR . '/autoloader.inc.php'); From aac202da2b353eec3b302901ad8e464a7c86ada0 Mon Sep 17 00:00:00 2001 From: Iain Kay Date: Thu, 11 Jul 2013 19:34:58 +0000 Subject: [PATCH 092/137] Pull cookie session params from include/config/global.inc.php before session_start() --- public/index.php | 1 + 1 file changed, 1 insertion(+) diff --git a/public/index.php b/public/index.php index ad58b9e0..2f51e67f 100644 --- a/public/index.php +++ b/public/index.php @@ -28,6 +28,7 @@ define("SECURITY", 1); if (!include_once(BASEPATH . 'include/config/global.inc.php')) die('Unable to load site configuration'); // Start a session +session_set_cookie_params($config['cookie']['duration'], $config['cookie']['path'], $config['cookie']['domain'], $config['cookie']['secure'], $config['cookie']['httponly']); session_start(); $session_id = session_id(); From dfbaf621de80c4715ce020e6c85a240166f0e418 Mon Sep 17 00:00:00 2001 From: Iain Kay Date: Thu, 11 Jul 2013 19:41:50 +0000 Subject: [PATCH 093/137] When destroying a users session on the server we now also remove all session data immediately, rather than relying on garbage collection, and we destroy the cookie on the users browser. --- public/include/classes/user.class.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 68616e3e..86250001 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -387,7 +387,16 @@ class User { **/ public function logoutUser($redirect="index.php") { $this->debug->append("STA " . __METHOD__, 4); + // Unset all of the session variables + $_SESSION = array(); + // As we're killing the sesison, also kill the cookie! + if (ini_get("session.use_cookies")) { + $params = session_get_cookie_params(); + setcookie(session_name(), '', time() - 42000, $params["path"], $params["domain"], $params["secure"], $params["httponly"]); + } + // Destroy the session. session_destroy(); + // Enforce generation of a new Session ID and delete the old session_regenerate_id(true); // Enforce a page reload header("Location: $redirect"); From a635d2163c82301f0b473245ba6e69b61474ba8f Mon Sep 17 00:00:00 2001 From: Iain Kay Date: Thu, 11 Jul 2013 19:56:10 +0000 Subject: [PATCH 094/137] Added note about php.ini session.gc_maxlifetime value - Important to stop garbage collection removing cookies that should be valid. --- public/include/config/global.inc.dist.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index a83f47be..28a2cd8c 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -357,7 +357,8 @@ $config['memcache']['splay'] = 15; * Explanation: * duration: * the amount of time, in seconds, that a cookie should persist in the users browser. - * 0 = until closed; 1440 = 24 minutes. + * 0 = until closed; 1440 = 24 minutes. Check your php.ini 'session.gc_maxlifetime' value + * and ensure that it is at least the duration specified here. * * domain: * the only domain name that may access this cookie in the browser From 3e2608fcef42a3e7b61563e720207a4b7ca54bc1 Mon Sep 17 00:00:00 2001 From: Iain Kay Date: Thu, 11 Jul 2013 20:30:39 +0000 Subject: [PATCH 095/137] Removed unnecessary cookie name variable that was not used. --- public/include/config/global.inc.dist.php | 5 +---- public/index.php | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 28a2cd8c..d13632c5 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -351,8 +351,7 @@ $config['memcache']['splay'] = 15; * * You can configure the cookie behaviour to secure your cookies more than the PHP defaults * - * For multiple installations of mmcfe-ng on the same domain you must change the cookie - * path or change the cookie name to avoid conflicts. + * For multiple installations of mmcfe-ng on the same domain you must change the cookie path. * * Explanation: * duration: @@ -382,14 +381,12 @@ $config['memcache']['splay'] = 15; * duration = '1440' * domain = '' * path = '/' - * name = 'POOLERCOOKIE' * httponly = true * secure = false **/ $config['cookie']['duration'] = '1440'; $config['cookie']['domain'] = ''; $config['cookie']['path'] = '/'; -$config['cookie']['name'] = 'POOLERCOOKIE'; $config['cookie']['httponly'] = true; $config['cookie']['secure'] = false; diff --git a/public/index.php b/public/index.php index 2f51e67f..95d3427c 100644 --- a/public/index.php +++ b/public/index.php @@ -28,7 +28,7 @@ define("SECURITY", 1); if (!include_once(BASEPATH . 'include/config/global.inc.php')) die('Unable to load site configuration'); // Start a session -session_set_cookie_params($config['cookie']['duration'], $config['cookie']['path'], $config['cookie']['domain'], $config['cookie']['secure'], $config['cookie']['httponly']); +session_set_cookie_params(time()+$config['cookie']['duration'], $config['cookie']['path'], $config['cookie']['domain'], $config['cookie']['secure'], $config['cookie']['httponly']); session_start(); $session_id = session_id(); From 4a693e1bd9440b5f77f51c7086398c38c3eb3d4e Mon Sep 17 00:00:00 2001 From: Iain Kay Date: Thu, 11 Jul 2013 20:40:14 +0000 Subject: [PATCH 096/137] Fix bug in sessions where the duration did not increase as user actively browsed site. --- public/index.php | 1 + 1 file changed, 1 insertion(+) diff --git a/public/index.php b/public/index.php index 95d3427c..fa67432c 100644 --- a/public/index.php +++ b/public/index.php @@ -30,6 +30,7 @@ if (!include_once(BASEPATH . 'include/config/global.inc.php')) die('Unable to lo // Start a session session_set_cookie_params(time()+$config['cookie']['duration'], $config['cookie']['path'], $config['cookie']['domain'], $config['cookie']['secure'], $config['cookie']['httponly']); session_start(); +setcookie(session_name(),session_id(),time()+$config['cookie']['duration'], $config['cookie']['path'], $config['cookie']['domain'], $config['cookie']['secure'], $config['cookie']['httponly']); $session_id = session_id(); // Load Classes, they name defines the $ variable used From 3df40b5bb71e913f0fbe4f7b95dba863ed661281 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 12 Jul 2013 09:48:31 +0200 Subject: [PATCH 097/137] removing newlines --- cronjobs/auto_payout.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cronjobs/auto_payout.php b/cronjobs/auto_payout.php index c55cc913..548e5de8 100755 --- a/cronjobs/auto_payout.php +++ b/cronjobs/auto_payout.php @@ -23,7 +23,7 @@ limitations under the License. require_once('shared.inc.php'); if ($bitcoin->can_connect() !== true) { - $log->logFatal(" unable to connect to RPC server, exiting\n"); + $log->logFatal(" unable to connect to RPC server, exiting"); $monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server"); $monitoring->setStatus($cron_name . "_status", "okerror", 1); @@ -34,7 +34,7 @@ if ($bitcoin->can_connect() !== true) { $users = $user->getAllAutoPayout(); // Quick summary -$log->logInfo(" found " . count($users) . " queued payout(s)\n"); +$log->logInfo(" found " . count($users) . " queued payout(s)"); // Go through users and run transactions if (! empty($users)) { @@ -77,7 +77,7 @@ if (! empty($users)) { } } } else { - $log->logDebug(" no user has configured their AP > 0\n"); + $log->logDebug(" no user has configured their AP > 0"); } // Cron cleanup and monitoring From 8ec1d2cab39e7d4f75871a42965c508aef039451 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 12 Jul 2013 10:33:42 +0200 Subject: [PATCH 098/137] Adding anonymous account support * Added anonymous flag to accounts table * Added checkbox for anonymous flag in edit account page * Updated user class to support new flag * Updated statistics class to support anonymous and donations * Updated all templates showing usernames to show anonymous instead * Added new SQL `ALTER TABLE` file for upgrading the table Fixes #419 once merged. --- public/include/classes/statistics.class.php | 23 ++++++++++++++----- public/include/classes/transaction.class.php | 1 + public/include/classes/user.class.php | 8 +++---- public/include/pages/account/edit.inc.php | 2 +- .../templates/mmcFE/about/donors/default.tpl | 2 +- .../templates/mmcFE/account/edit/default.tpl | 4 ++++ public/templates/mmcFE/global/userinfo.tpl | 2 +- .../mmcFE/statistics/blocks/default.tpl | 2 +- .../mmcFE/statistics/blocks/small_table.tpl | 2 +- .../statistics/pool/contributors_hashrate.tpl | 2 +- .../statistics/pool/contributors_shares.tpl | 2 +- sql/003_accounts_anonymous.sql | 1 + 12 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 sql/003_accounts_anonymous.sql diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index 3e375249..e2d1848a 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -61,7 +61,10 @@ class Statistics { $this->debug->append("STA " . __METHOD__, 4); if ($data = $this->memcache->get(__FUNCTION__ . $limit)) return $data; $stmt = $this->mysqli->prepare(" - SELECT b.*, a.username as finder + SELECT + b.*, + a.username AS finder, + a.is_anonymous AS is_anonymous FROM " . $this->block->getTableName() . " AS b LEFT JOIN " . $this->user->getTableName() . " AS a ON b.account_id = a.id @@ -327,9 +330,13 @@ class Statistics { case 'shares': $stmt = $this->mysqli->prepare(" SELECT - COUNT(id) AS shares, - SUBSTRING_INDEX( username, '.', 1 ) AS account - FROM " . $this->share->getTableName() . " + a.donate_percent AS donate_percent, + a.is_anonymous AS is_anonymous, + COUNT(s.id) AS shares, + SUBSTRING_INDEX( s.username, '.', 1 ) AS account + FROM " . $this->share->getTableName() . " AS s + LEFT JOIN " . $this->user->getTableName() . " AS a + ON SUBSTRING_INDEX( s.username, '.', 1 ) = a.username WHERE our_result = 'Y' GROUP BY account ORDER BY shares DESC @@ -343,14 +350,18 @@ class Statistics { case 'hashes': $stmt = $this->mysqli->prepare(" SELECT - IFNULL(ROUND(COUNT(id) * POW(2," . $this->config['difficulty'] . ")/600/1000, 2), 0) AS hashrate, - SUBSTRING_INDEX( username, '.', 1 ) AS account + a.donate_percent AS donate_percent, + a.is_anonymous AS is_anonymous, + IFNULL(ROUND(COUNT(t1.id) * POW(2," . $this->config['difficulty'] . ")/600/1000, 2), 0) AS hashrate, + SUBSTRING_INDEX( t1.username, '.', 1 ) AS account FROM ( SELECT id, username FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y' UNION SELECT id, username FROM " . $this->share->getArchiveTableName() ." WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y' ) AS t1 + LEFT JOIN " . $this->user->getTableName() . " AS a + ON SUBSTRING_INDEX( t1.username, '.', 1 ) = a.username GROUP BY account ORDER BY hashrate DESC LIMIT ?"); if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result()) diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index d01a6d18..30867cc6 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -127,6 +127,7 @@ class Transaction { SELECT SUM(t.amount) AS donation, a.username AS username, + a.is_anonymous AS is_anonymous, a.donate_percent AS donate_percent FROM $this->table AS t LEFT JOIN " . $this->user->getTableName() . " AS a diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index a363ff58..ac4fabdf 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -283,7 +283,7 @@ class User { * @param donat float donation % of income * @return bool **/ - public function updateAccount($userID, $address, $threshold, $donate, $email) { + public function updateAccount($userID, $address, $threshold, $donate, $email, $is_anonymous) { $this->debug->append("STA " . __METHOD__, 4); $bUser = false; @@ -317,8 +317,8 @@ class User { $donate = min(100, max(0, floatval($donate))); // We passed all validation checks so update the account - $stmt = $this->mysqli->prepare("UPDATE $this->table SET coin_address = ?, ap_threshold = ?, donate_percent = ?, email = ? WHERE id = ?"); - if ($this->checkStmt($stmt) && $stmt->bind_param('sddsi', $address, $threshold, $donate, $email, $userID) && $stmt->execute()) + $stmt = $this->mysqli->prepare("UPDATE $this->table SET coin_address = ?, ap_threshold = ?, donate_percent = ?, email = ?, is_anonymous = ? WHERE id = ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param('sddsii', $address, $threshold, $donate, $email, $is_anonymous, $userID) && $stmt->execute()) return true; // Catchall $this->setErrorMessage('Failed to update your account'); @@ -421,7 +421,7 @@ class User { $this->debug->append("Fetching user information for user id: $userID"); $stmt = $this->mysqli->prepare(" SELECT - id, username, pin, api_key, is_admin, email, + id, username, pin, api_key, is_admin, is_anonymous, email, IFNULL(donate_percent, '0') as donate_percent, coin_address, ap_threshold FROM $this->table WHERE id = ? LIMIT 0,1"); diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php index 1ced549b..9ab38e49 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -61,7 +61,7 @@ if ($user->isAuthenticated()) { break; case 'updateAccount': - if ($user->updateAccount($_SESSION['USERDATA']['id'], $_POST['paymentAddress'], $_POST['payoutThreshold'], $_POST['donatePercent'], $_POST['email'])) { + if ($user->updateAccount($_SESSION['USERDATA']['id'], $_POST['paymentAddress'], $_POST['payoutThreshold'], $_POST['donatePercent'], $_POST['email'], $_POST['is_anonymous'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account details updated', 'TYPE' => 'success'); } else { $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update your account: ' . $user->getError(), 'TYPE' => 'errormsg'); diff --git a/public/templates/mmcFE/about/donors/default.tpl b/public/templates/mmcFE/about/donors/default.tpl index 2d5efaf3..8d5205ac 100644 --- a/public/templates/mmcFE/about/donors/default.tpl +++ b/public/templates/mmcFE/about/donors/default.tpl @@ -12,7 +12,7 @@ {section name=donor loop=$DONORS} - + diff --git a/public/templates/mmcFE/account/edit/default.tpl b/public/templates/mmcFE/account/edit/default.tpl index fd445286..fcf29c7c 100644 --- a/public/templates/mmcFE/account/edit/default.tpl +++ b/public/templates/mmcFE/account/edit/default.tpl @@ -11,6 +11,10 @@ +
      Username: {$GLOBAL.userdata.username}
      Username: {$GLOBAL.userdata.username|escape}
      User Id: {$GLOBAL.userdata.id}
      API Key: {$GLOBAL.userdata.api_key}
      E-Mail:
      {$DONORS[donor].username}{if $DONORS[donor].is_anonymous|default:"0" == 1}anonymous{else}{$DONORS[donor].username}{/if} {$DONORS[donor].donate_percent} {$DONORS[donor].donation|number_format:"2"}
      Payment Address:
      Donation %: [donation amount in percent (example: 0.5)]
      Automatic Payout Threshold: [{$GLOBAL.config.ap_threshold.min}-{$GLOBAL.config.ap_threshold.max} {$GLOBAL.config.currency}. Set to '0' for no auto payout]
      Anonymous Account: + + +
      4 digit PIN: [The 4 digit PIN you chose when registering]
      diff --git a/public/templates/mmcFE/global/userinfo.tpl b/public/templates/mmcFE/global/userinfo.tpl index d9745394..45e9ad92 100644 --- a/public/templates/mmcFE/global/userinfo.tpl +++ b/public/templates/mmcFE/global/userinfo.tpl @@ -1,5 +1,5 @@ {if $GLOBAL.userdata.username|default} -

      Welcome, {$smarty.session.USERDATA.username|escape} Active Account: {$GLOBAL.fees|escape}% Pool Fee (You are donating {$GLOBAL.userdata.donate_percent|escape}% of your earnings)

      +

      Welcome, {$smarty.session.USERDATA.username|escape} {if $GLOBAL.userdata.is_anonymous}Anonymous{else}Active{/if} Account: {$GLOBAL.fees|escape}% Pool Fee (You are donating {$GLOBAL.userdata.donate_percent|escape}% of your earnings)

      {else}

      Welcome guest, please register to user this pool.

      {/if} diff --git a/public/templates/mmcFE/statistics/blocks/default.tpl b/public/templates/mmcFE/statistics/blocks/default.tpl index 0856a269..2871cd01 100644 --- a/public/templates/mmcFE/statistics/blocks/default.tpl +++ b/public/templates/mmcFE/statistics/blocks/default.tpl @@ -57,7 +57,7 @@ target and network difficulty and assuming a zero variance scenario. {else if $BLOCKSFOUND[block].confirmations == -1} Orphan {else}{$GLOBAL.confirmations - $BLOCKSFOUND[block].confirmations} left{/if} - {$BLOCKSFOUND[block].finder|default:"unknown"|escape} + {if $BLOCKSFOUND[block].is_anonymous|default:"0" == 1}anonymous{else}{$BLOCKSFOUND[block].finder|default:"unknown"|escape}{/if} {$BLOCKSFOUND[block].time|date_format:"%d/%m %H:%M:%S"} {$BLOCKSFOUND[block].difficulty|number_format:"2"} {$BLOCKSFOUND[block].amount|number_format:"2"} diff --git a/public/templates/mmcFE/statistics/blocks/small_table.tpl b/public/templates/mmcFE/statistics/blocks/small_table.tpl index 2b0f8aac..010b3557 100644 --- a/public/templates/mmcFE/statistics/blocks/small_table.tpl +++ b/public/templates/mmcFE/statistics/blocks/small_table.tpl @@ -14,7 +14,7 @@ {section block $BLOCKSFOUND} {$BLOCKSFOUND[block].height} - {$BLOCKSFOUND[block].finder|default:"unknown"|escape} + {if $BLOCKSFOUND[block].is_anonymous|default:"0" == 1}anonymous{else}{$BLOCKSFOUND[block].finder|default:"unknown"|escape}{/if} {$BLOCKSFOUND[block].time|date_format:"%d/%m %H:%M:%S"} {$BLOCKSFOUND[block].shares|number_format} diff --git a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl index b6168c36..a7f3cd9f 100644 --- a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl +++ b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl @@ -17,7 +17,7 @@ {math assign="estday" equation="round(reward / ( diff * pow(2,32) / ( hashrate * 1000 ) / 3600 / 24), 3)" diff=$DIFFICULTY reward=$REWARD hashrate=$CONTRIBHASHES[contrib].hashrate} {$rank++} - {$CONTRIBHASHES[contrib].account|escape} + {if $CONTRIBHASHES[contrib].is_anonymous|default:"0" == 1}anonymous{else}{$CONTRIBHASHES[contrib].account|escape}{/if} {$CONTRIBHASHES[contrib].hashrate|number_format} {$estday|number_format:"3"} {if $GLOBAL.config.price.currency}{($estday * $GLOBAL.price)|default:"n/a"|number_format:"2"}{/if} diff --git a/public/templates/mmcFE/statistics/pool/contributors_shares.tpl b/public/templates/mmcFE/statistics/pool/contributors_shares.tpl index 232e76b2..3efb4a8e 100644 --- a/public/templates/mmcFE/statistics/pool/contributors_shares.tpl +++ b/public/templates/mmcFE/statistics/pool/contributors_shares.tpl @@ -14,7 +14,7 @@ {section hashrate $CONTRIBSHARES} {$rank++} - {$CONTRIBSHARES[hashrate].account|escape} + {if $CONTRIBHASHES[hashrate].is_anonymous|default:"0" == 1}anonymous{else}{$CONTRIBSHARES[hashrate].account|escape}{/if} {$CONTRIBSHARES[hashrate].shares|number_format} {/section} diff --git a/sql/003_accounts_anonymous.sql b/sql/003_accounts_anonymous.sql new file mode 100644 index 00000000..167ad5aa --- /dev/null +++ b/sql/003_accounts_anonymous.sql @@ -0,0 +1 @@ +ALTER TABLE `accounts` ADD `is_anonymous` BOOLEAN NOT NULL DEFAULT FALSE AFTER `is_admin` ; From 015da23d8e81ec5325a84b5e787fe3d8a0aa0a16 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 12 Jul 2013 19:42:26 +0200 Subject: [PATCH 099/137] disable SQL intensive admin query cache --- cronjobs/statistics.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cronjobs/statistics.php b/cronjobs/statistics.php index 2a145692..3d54fd78 100755 --- a/cronjobs/statistics.php +++ b/cronjobs/statistics.php @@ -42,11 +42,13 @@ $start = microtime(true); if (!$statistics->getCurrentHashrate()) $log->logError("getCurrentHashrate update failed"); $log->logInfo("getCurrentHashrate " . number_format(microtime(true) - $start, 2) . " seconds"); +/* // Admin specific statistics, we cache the global query due to slowness $start = microtime(true); if (!$statistics->getAllUserStats('%')) $log->logError("getAllUserStats update failed"); $log->logInfo("getAllUserStats " . number_format(microtime(true) - $start, 2) . " seconds"); +*/ // Per user share statistics based on all shares submitted $start = microtime(true); From b64ef5a48905a578a05c29547e271cbf6f944ba4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 12 Jul 2013 19:42:43 +0200 Subject: [PATCH 100/137] Use INFO as default logging level for crons --- cronjobs/shared.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cronjobs/shared.inc.php b/cronjobs/shared.inc.php index 14bed27e..33113c00 100644 --- a/cronjobs/shared.inc.php +++ b/cronjobs/shared.inc.php @@ -39,7 +39,7 @@ require_once(BASEPATH . 'include/config/global.inc.php'); require_once(INCLUDE_DIR . '/autoloader.inc.php'); // Load 3rd party logging library for running crons -$log = new KLogger ( 'logs/' . $cron_name . '.txt' , KLogger::DEBUG ); +$log = new KLogger ( 'logs/' . $cron_name . '.txt' , KLogger::INFO ); $log->LogDebug('Starting ' . $cron_name); // Load the start time for later runtime calculations for monitoring From 0775eaf8c1aef5b40e9bac34e7546395191d12d3 Mon Sep 17 00:00:00 2001 From: typ Date: Sat, 13 Jul 2013 16:41:51 +0200 Subject: [PATCH 101/137] add check for non alpa/-/_ chars --- public/include/classes/user.class.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index a363ff58..cd514966 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -455,6 +455,10 @@ class User { $this->setErrorMessage('Username exceeding character limit'); return false; } + if (!preg_match('/[^a-zA-Z0-9_\-]/', $username)) { + $this->setErrorMessage('Username may only contain alphanumeric characters'); + return false; + } if ($this->getEmail($email1)) { $this->setErrorMessage( 'This e-mail address is already taken' ); return false; From ed5e320ff60230fa23c48de21856398444c7309e Mon Sep 17 00:00:00 2001 From: typ Date: Sun, 14 Jul 2013 02:43:44 +0200 Subject: [PATCH 102/137] fucked up... --- public/include/classes/user.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index cd514966..a3a01ef5 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -455,7 +455,7 @@ class User { $this->setErrorMessage('Username exceeding character limit'); return false; } - if (!preg_match('/[^a-zA-Z0-9_\-]/', $username)) { + if (preg_match('/[^a-zA-Z0-9_\-]/', $username)) { $this->setErrorMessage('Username may only contain alphanumeric characters'); return false; } From 253d6e8a47d9ae9bbbb3190f5fa00a23c7c74bde Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 14 Jul 2013 21:08:03 +0200 Subject: [PATCH 103/137] Fixing username regexp during registration Fixes wrong regext of #453 --- public/include/classes/user.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index c87c8fbb..bdd87d1e 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -455,7 +455,7 @@ class User { $this->setErrorMessage('Username exceeding character limit'); return false; } - if (preg_match('/[^a-zA-Z0-9_\-]/', $username)) { + if (preg_match('/[^a-z_\-0-9]/i', $username)) { $this->setErrorMessage('Username may only contain alphanumeric characters'); return false; } From d5c14b9b4428b31bbecdada34aeca066eaae0578 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 10:40:23 +0200 Subject: [PATCH 104/137] Update 002_monitoring.sql --- sql/002_monitoring.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/002_monitoring.sql b/sql/002_monitoring.sql index e5a21682..99d44c2f 100644 --- a/sql/002_monitoring.sql +++ b/sql/002_monitoring.sql @@ -14,7 +14,7 @@ CREATE TABLE IF NOT EXISTS `monitoring` ( `value` varchar(25) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf32 COMMENT='Monitoring events from cronjobs'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Monitoring events from cronjobs'; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; From 29d5d36a7e31858a4ab2751110ccebd6383b79ee Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 14 Jul 2013 21:06:26 +0200 Subject: [PATCH 105/137] WiP for one time tokens * Added token type class * Storing Token Type as ID not varchar * Added new system to user class and fixed issues with it * Started on mail verification process in user class * Updated autoloader * Updated change password template Addresses #330 --- public/include/autoloader.inc.php | 2 + public/include/classes/base.class.php | 3 + public/include/classes/token.class.php | 60 +++++++++++++++++ public/include/classes/tokentype.class.php | 21 ++++++ public/include/classes/user.class.php | 64 ++++++++++--------- public/include/pages/password/change.inc.php | 4 +- public/include/pages/password/reset.inc.php | 2 +- .../mmcFE/password/change/default.tpl | 2 +- 8 files changed, 125 insertions(+), 33 deletions(-) create mode 100644 public/include/classes/token.class.php create mode 100644 public/include/classes/tokentype.class.php diff --git a/public/include/autoloader.inc.php b/public/include/autoloader.inc.php index 281b39c6..69a306bf 100644 --- a/public/include/autoloader.inc.php +++ b/public/include/autoloader.inc.php @@ -22,6 +22,8 @@ require_once(INCLUDE_DIR . '/database.inc.php'); require_once(INCLUDE_DIR . '/smarty.inc.php'); // Load classes that need the above as dependencies require_once(CLASS_DIR . '/base.class.php'); +require_once(CLASS_DIR . '/tokentype.class.php'); +require_once(CLASS_DIR . '/token.class.php'); require_once(CLASS_DIR . '/block.class.php'); require_once(CLASS_DIR . '/setting.class.php'); require_once(CLASS_DIR . '/monitoring.class.php'); diff --git a/public/include/classes/base.class.php b/public/include/classes/base.class.php index 6b3c2389..316b245e 100644 --- a/public/include/classes/base.class.php +++ b/public/include/classes/base.class.php @@ -24,6 +24,9 @@ class Base { public function setConfig($config) { $this->config = $config; } + public function setTokenType($tokentype) { + $this->tokentype = $tokentype; + } public function setErrorMessage($msg) { $this->sError = $msg; } diff --git a/public/include/classes/token.class.php b/public/include/classes/token.class.php new file mode 100644 index 00000000..fa472849 --- /dev/null +++ b/public/include/classes/token.class.php @@ -0,0 +1,60 @@ +mysqli->prepare("SELECT * FROM $this->table WHERE token = ? LIMIT 1"); + if ($stmt && $stmt->bind_param('s', $strToken) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_assoc(); + return false; + } + + /** + * Insert a new token + * @param name string Name of the variable + * @param value string Variable value + * @return mixed Insert ID on success, false on failure + **/ + public function createToken($strType, $account_id=NULL) { + $strToken = hash('sha256', $account_id.$strType.microtime()); + if (!$iToken_id = $this->tokentype->getTypeId($strType)) { + $this->setErrorMessage('Invalid token type: ' . $strType); + return false; + } + $stmt = $this->mysqli->prepare(" + INSERT INTO $this->table (token, type, account_id) + VALUES (?, ?, ?) + "); + if ($stmt && $stmt->bind_param('sii', $strToken, $iToken_id, $account_id) && $stmt->execute()) + return $stmt->insert_id; + $this->setErrorMessage('Unable to create new token'); + $this->debug->append('Failed to create new token in database: ' . $this->mysqli->error); + return false; + } + + /** + * Delete a used token + * @param token string Token name + * @return bool + **/ + public function deleteToken($token) { + $stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE token = ? LIMIT 1"); + if ($stmt && $stmt->bind_param('s', $token) && $stmt->execute()) + return true; + return false; + } +} + +$token = new Token(); +$token->setDebug($debug); +$token->setMysql($mysqli); +$token->setTokenType($tokentype); diff --git a/public/include/classes/tokentype.class.php b/public/include/classes/tokentype.class.php new file mode 100644 index 00000000..d33356cb --- /dev/null +++ b/public/include/classes/tokentype.class.php @@ -0,0 +1,21 @@ +getSingle($strName, 'id', 'name', 's'); + } +} + +$tokentype = new Token_Type(); +$tokentype->setDebug($debug); +$tokentype->setMysql($mysqli); diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index bdd87d1e..36b409c5 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -9,11 +9,11 @@ class User { private $userID = false; private $table = 'accounts'; private $user = array(); - private $tableAccountBalance = 'accountBalance'; - public function __construct($debug, $mysqli, $salt, $config) { + public function __construct($debug, $mysqli, $token, $salt, $config) { $this->debug = $debug; $this->mysqli = $mysqli; + $this->token = $token; $this->salt = $salt; $this->config = $config; $this->debug->append("Instantiated User class", 2); @@ -485,13 +485,13 @@ class User { } if ($this->mysqli->query("SELECT id FROM $this->table LIMIT 1")->num_rows > 0) { $stmt = $this->mysqli->prepare(" - INSERT INTO $this->table (username, pass, email, pin, api_key) - VALUES (?, ?, ?, ?, ?) + INSERT INTO $this->table (username, pass, email, pin, api_key, is_locked) + VALUES (?, ?, ?, ?, ?, ?) "); } else { $stmt = $this->mysqli->prepare(" - INSERT INTO $this->table (username, pass, email, pin, api_key, is_admin) - VALUES (?, ?, ?, ?, ?, 1) + INSERT INTO $this->table (username, pass, email, pin, api_key, is_admin, is_locked) + VALUES (?, ?, ?, ?, ?, 1, 0) "); } @@ -501,14 +501,19 @@ class User { $apikey_hash = $this->getHash($username); $username_clean = strip_tags($username); - if ($this->checkStmt($stmt) && $stmt->bind_param('sssss', $username_clean, $password_hash, $email1, $pin_hash, $apikey_hash)) { - if (!$stmt->execute()) { - $this->setErrorMessage( 'Unable to register' ); - if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username or email already registered' ); - return false; + // + $this->config['confirm_email'] ? $is_locked = 1 : $is_locked = 0; + + if ($this->checkStmt($stmt) && $stmt->bind_param('sssssi', $username_clean, $password_hash, $email1, $pin_hash, $apikey_hash, $is_locked) && $stmt->execute()) { + if ($this->config['confirm_email']) { + $this->token->createToken('confirm_email', $stmt->insert_id); + } else { + return true; } - $stmt->close(); - return true; + } else { + $this->setErrorMessage( 'Unable to register' ); + if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username or email already registered' ); + return false; } return false; } @@ -520,9 +525,9 @@ class User { * @param new2 string New password verification * @return bool **/ - public function useToken($token, $new1, $new2) { + public function resetPassword($token, $new1, $new2) { $this->debug->append("STA " . __METHOD__, 4); - if ($id = $this->getIdFromToken($token)) { + if ($token = $this->token->getToken($token)) { if ($new1 !== $new2) { $this->setErrorMessage( 'New passwords do not match' ); return false; @@ -532,14 +537,20 @@ class User { return false; } $new_hash = $this->getHash($new1); - $stmt = $this->mysqli->prepare("UPDATE $this->table SET pass = ?, token = NULL WHERE id = ? AND token = ?"); - if ($this->checkStmt($stmt) && $stmt->bind_param('sis', $new_hash, $id, $token) && $stmt->execute() && $stmt->affected_rows === 1) { - return true; + $stmt = $this->mysqli->prepare("UPDATE $this->table SET pass = ? WHERE id = ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param('si', $new_hash, $token['account_id']) && $stmt->execute() && $stmt->affected_rows === 1) { + if ($this->token->deleteToken($token)) { + return true; + } else { + $this->setErrorMessage('Unable to invalidate used token'); + } + } else { + $this->setErrorMessage('Unable to set new password'); } } else { - $this->setErrorMessage("Unable find user for your token"); - return false; + $this->setErrorMessage('Unable find user for your token'); } + $this->debug->append('Failed to update password:' . $this->mysqli->error); return false; } @@ -549,7 +560,7 @@ class User { * @param smarty object Smarty object for mail templating * @return bool **/ - public function resetPassword($username, $smarty) { + public function initResetPassword($username, $smarty) { $this->debug->append("STA " . __METHOD__, 4); // Fetch the users mail address if (empty($username)) { @@ -560,16 +571,11 @@ class User { $this->setErrorMessage("Unable to find a mail address for user $username"); return false; } - if (!$this->setUserToken($this->getUserId($username))) { + if (!$token = $this->token->getToken($this->token->createToken('password_reset', $this->getUserId($username)))) { $this->setErrorMessage("Unable to setup token for password reset"); return false; } - // Send password reset link - if (!$token = $this->getUserToken($this->getUserId($username))) { - $this->setErrorMessage("Unable fetch token for password reset"); - return false; - } - $smarty->assign('TOKEN', $token); + $smarty->assign('TOKEN', $token['token']); $smarty->assign('USERNAME', $username); $smarty->assign('SUBJECT', 'Password Reset Request'); $smarty->assign('WEBSITENAME', $this->config['website']['name']); @@ -608,4 +614,4 @@ class User { } // Make our class available automatically -$user = new User($debug, $mysqli, SALT, $config); +$user = new User($debug, $mysqli, $token, SALT, $config); diff --git a/public/include/pages/password/change.inc.php b/public/include/pages/password/change.inc.php index 8b5f4064..b8115c69 100644 --- a/public/include/pages/password/change.inc.php +++ b/public/include/pages/password/change.inc.php @@ -4,8 +4,8 @@ if (!defined('SECURITY')) die('Hacking attempt'); -if ($_POST['do'] == 'useToken') { - if ($user->useToken($_POST['token'], $_POST['newPassword'], $_POST['newPassword2'])) { +if ($_POST['do'] == 'resetPassword') { + if ($user->resetPassword($_POST['token'], $_POST['newPassword'], $_POST['newPassword2'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'Password reset complete! Please login.'); } else { $_SESSION['POPUP'][] = array('CONTENT' => $user->getError(), 'TYPE' => 'errormsg'); diff --git a/public/include/pages/password/reset.inc.php b/public/include/pages/password/reset.inc.php index 796f5811..f7334fe8 100644 --- a/public/include/pages/password/reset.inc.php +++ b/public/include/pages/password/reset.inc.php @@ -5,7 +5,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); // Process password reset request -if ($user->resetPassword($_POST['username'], $smarty)) { +if ($user->initResetPassword($_POST['username'], $smarty)) { $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mail account to finish your password reset'); } else { $_SESSION['POPUP'][] = array('CONTENT' => $user->getError(), 'TYPE' => 'errormsg'); diff --git a/public/templates/mmcFE/password/change/default.tpl b/public/templates/mmcFE/password/change/default.tpl index 0d254677..ae3aab55 100644 --- a/public/templates/mmcFE/password/change/default.tpl +++ b/public/templates/mmcFE/password/change/default.tpl @@ -3,7 +3,7 @@ - + From 0ede05a6fd732e7efb801a513bbfabd96c1eebc1 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 14 Jul 2013 22:17:54 +0200 Subject: [PATCH 106/137] Adding email verification * Adding mail verification during account registration * Added new dist file option for mail verification * Added account confirmation page using tokens * Added mail class into user class for password resets * Moved password reset template * Adjusted account registration page * Adjusted user class for email confirmation Also fixed a bug with smarty_cache_key not being used properly if smarty is disabled. Key still needs to be available even if caching is disabled Addresses #330 and prepare the ticket for invitation only system. --- public/include/autoloader.inc.php | 2 +- public/include/classes/token.class.php | 12 ++-- public/include/classes/user.class.php | 64 +++++++++++-------- public/include/config/global.inc.dist.php | 17 +++++ public/include/pages/account/confirm.inc.php | 17 +++++ public/include/pages/password/change.inc.php | 2 +- .../include/pages/register/register.inc.php | 4 +- public/include/smarty.inc.php | 2 +- .../mail/{body.tpl => password/reset.tpl} | 4 +- .../templates/mail/register/confirm_email.tpl | 10 +++ .../mmcFE/account/confirm/default.tpl | 1 + sql/004_tokens.sql | 47 ++++++++++++++ 12 files changed, 143 insertions(+), 39 deletions(-) create mode 100644 public/include/pages/account/confirm.inc.php rename public/templates/mail/{body.tpl => password/reset.tpl} (80%) create mode 100644 public/templates/mail/register/confirm_email.tpl create mode 100644 public/templates/mmcFE/account/confirm/default.tpl create mode 100644 sql/004_tokens.sql diff --git a/public/include/autoloader.inc.php b/public/include/autoloader.inc.php index 69a306bf..bb765523 100644 --- a/public/include/autoloader.inc.php +++ b/public/include/autoloader.inc.php @@ -22,6 +22,7 @@ require_once(INCLUDE_DIR . '/database.inc.php'); require_once(INCLUDE_DIR . '/smarty.inc.php'); // Load classes that need the above as dependencies require_once(CLASS_DIR . '/base.class.php'); +require_once(CLASS_DIR . '/mail.class.php'); require_once(CLASS_DIR . '/tokentype.class.php'); require_once(CLASS_DIR . '/token.class.php'); require_once(CLASS_DIR . '/block.class.php'); @@ -32,7 +33,6 @@ require_once(CLASS_DIR . '/share.class.php'); require_once(CLASS_DIR . '/worker.class.php'); require_once(CLASS_DIR . '/statistics.class.php'); require_once(CLASS_DIR . '/transaction.class.php'); -require_once(CLASS_DIR . '/mail.class.php'); require_once(CLASS_DIR . '/notification.class.php'); require_once(CLASS_DIR . '/news.class.php'); require_once(INCLUDE_DIR . '/lib/Michelf/Markdown.php'); diff --git a/public/include/classes/token.class.php b/public/include/classes/token.class.php index fa472849..c65da13c 100644 --- a/public/include/classes/token.class.php +++ b/public/include/classes/token.class.php @@ -22,7 +22,7 @@ class Token Extends Base { * Insert a new token * @param name string Name of the variable * @param value string Variable value - * @return mixed Insert ID on success, false on failure + * @return mixed Token string on success, false on failure **/ public function createToken($strType, $account_id=NULL) { $strToken = hash('sha256', $account_id.$strType.microtime()); @@ -35,7 +35,7 @@ class Token Extends Base { VALUES (?, ?, ?) "); if ($stmt && $stmt->bind_param('sii', $strToken, $iToken_id, $account_id) && $stmt->execute()) - return $stmt->insert_id; + return $strToken; $this->setErrorMessage('Unable to create new token'); $this->debug->append('Failed to create new token in database: ' . $this->mysqli->error); return false; @@ -54,7 +54,7 @@ class Token Extends Base { } } -$token = new Token(); -$token->setDebug($debug); -$token->setMysql($mysqli); -$token->setTokenType($tokentype); +$oToken = new Token(); +$oToken->setDebug($debug); +$oToken->setMysql($mysqli); +$oToken->setTokenType($tokentype); diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 36b409c5..9927ac30 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -10,16 +10,21 @@ class User { private $table = 'accounts'; private $user = array(); - public function __construct($debug, $mysqli, $token, $salt, $config) { + public function __construct($debug, $mysqli, $salt, $config) { $this->debug = $debug; $this->mysqli = $mysqli; - $this->token = $token; $this->salt = $salt; $this->config = $config; $this->debug->append("Instantiated User class", 2); } // get and set methods + public function setMail($mail) { + $this->mail = $mail; + } + public function setToken($token) { + $this->token= $token; + } private function setErrorMessage($msg) { $this->sError = $msg; } @@ -484,14 +489,16 @@ class User { return false; } if ($this->mysqli->query("SELECT id FROM $this->table LIMIT 1")->num_rows > 0) { + $this->config['accounts']['confirm_email'] ? $is_locked = 1 : $is_locked = 0; $stmt = $this->mysqli->prepare(" INSERT INTO $this->table (username, pass, email, pin, api_key, is_locked) VALUES (?, ?, ?, ?, ?, ?) "); } else { + $is_locked = 0; $stmt = $this->mysqli->prepare(" INSERT INTO $this->table (username, pass, email, pin, api_key, is_admin, is_locked) - VALUES (?, ?, ?, ?, ?, 1, 0) + VALUES (?, ?, ?, ?, ?, 1, ?) "); } @@ -501,17 +508,29 @@ class User { $apikey_hash = $this->getHash($username); $username_clean = strip_tags($username); - // - $this->config['confirm_email'] ? $is_locked = 1 : $is_locked = 0; - if ($this->checkStmt($stmt) && $stmt->bind_param('sssssi', $username_clean, $password_hash, $email1, $pin_hash, $apikey_hash, $is_locked) && $stmt->execute()) { - if ($this->config['confirm_email']) { - $this->token->createToken('confirm_email', $stmt->insert_id); + if ($this->config['accounts']['confirm_email']) { + if ($token = $this->token->createToken('confirm_email', $stmt->insert_id)) { + $aData['username'] = $username_clean; + $aData['token'] = $token; + $aData['email'] = $email1; + $aData['subject'] = 'E-Mail verification'; + if (!$this->mail->sendMail('register/confirm_email', $aData)) { + $this->setErrorMessage('Unable to request email confirmation'); + return false; + } + return true; + } else { + $this->setErrorMessage('Failed to create confirmation token'); + $this->debug->append('Unable to create confirm_email token: ' . $this->token->getError()); + return false; + } } else { return true; } } else { $this->setErrorMessage( 'Unable to register' ); + $this->debug->append('Failed to insert user into DB: ' . $this->mysqli->error); if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username or email already registered' ); return false; } @@ -548,7 +567,7 @@ class User { $this->setErrorMessage('Unable to set new password'); } } else { - $this->setErrorMessage('Unable find user for your token'); + $this->setErrorMessage('Invalid token'); } $this->debug->append('Failed to update password:' . $this->mysqli->error); return false; @@ -557,35 +576,26 @@ class User { /** * Reset a password by sending a password reset mail * @param username string Username to reset password for - * @param smarty object Smarty object for mail templating * @return bool **/ - public function initResetPassword($username, $smarty) { + public function initResetPassword($username) { $this->debug->append("STA " . __METHOD__, 4); // Fetch the users mail address if (empty($username)) { $this->serErrorMessage("Username must not be empty"); return false; } - if (!$email = $this->getUserEmail($username)) { + if (!$aData['email'] = $this->getUserEmail($username)) { $this->setErrorMessage("Unable to find a mail address for user $username"); return false; } - if (!$token = $this->token->getToken($this->token->createToken('password_reset', $this->getUserId($username)))) { - $this->setErrorMessage("Unable to setup token for password reset"); + if (!$aData['token'] = $this->token->createToken('password_reset', $this->getUserId($username))) { + $this->setErrorMessage('Unable to setup token for password reset'); return false; } - $smarty->assign('TOKEN', $token['token']); - $smarty->assign('USERNAME', $username); - $smarty->assign('SUBJECT', 'Password Reset Request'); - $smarty->assign('WEBSITENAME', $this->config['website']['name']); - $headers = 'From: Website Administration <' . $this->config['website']['email'] . ">\n"; - $headers .= "MIME-Version: 1.0\n"; - $headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n"; - if (mail($email, - $smarty->fetch('templates/mail/subject.tpl'), - $smarty->fetch('templates/mail/body.tpl'), - $headers)) { + $aData['username'] = $username; + $aData['subject'] = 'Password Reset Request'; + if ($this->mail->sendMail('password/reset', $aData)) { return true; } else { $this->setErrorMessage("Unable to send mail to your address"); @@ -614,4 +624,6 @@ class User { } // Make our class available automatically -$user = new User($debug, $mysqli, $token, SALT, $config); +$user = new User($debug, $mysqli, SALT, $config); +$user->setMail($mail); +$user->setToken($oToken); diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 25667017..4c7595cc 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -121,6 +121,23 @@ $config['website']['theme'] = 'mmcFE'; $config['website']['mobile'] = true; $config['website']['mobile_theme'] = 'mobile'; +/** + * Account specific settings + * + * Explanation + * You can change some defaults on how accounts are created or registered + * By default, all newly created accounts will require an email verificaiton. + * Only after acitivating an account the user will be able to login + * + * Options: + * confirm_email : Send confirmation mail to user after registration + * + * Defaults: + * confirm_email : true + * + **/ +$config['accounts']['confirm_email'] = true; + /** * Some basic access restrictions on some pages * diff --git a/public/include/pages/account/confirm.inc.php b/public/include/pages/account/confirm.inc.php new file mode 100644 index 00000000..3611c5de --- /dev/null +++ b/public/include/pages/account/confirm.inc.php @@ -0,0 +1,17 @@ + 'Missing token', 'TYPE' => 'errormsg'); +} else if (!$aToken = $oToken->getToken($_GET['token'])) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to activate your account. Invalid token', 'TYPE' => 'errormsg'); +} else { + $user->changeLocked($aToken['account_id']); + $oToken->deleteToken($aToken['token']); + $_SESSION['POPUP'][] = array('CONTENT' => 'Account activated. Please login.'); +} +$smarty->assign('CONTENT', 'default.tpl'); +?> diff --git a/public/include/pages/password/change.inc.php b/public/include/pages/password/change.inc.php index b8115c69..b45b3ee4 100644 --- a/public/include/pages/password/change.inc.php +++ b/public/include/pages/password/change.inc.php @@ -11,7 +11,7 @@ if ($_POST['do'] == 'resetPassword') { $_SESSION['POPUP'][] = array('CONTENT' => $user->getError(), 'TYPE' => 'errormsg'); } } - // Tempalte specifics $smarty->assign("CONTENT", "default.tpl"); + ?> diff --git a/public/include/pages/register/register.inc.php b/public/include/pages/register/register.inc.php index 01a27e10..526ef247 100644 --- a/public/include/pages/register/register.inc.php +++ b/public/include/pages/register/register.inc.php @@ -20,7 +20,7 @@ if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_PO if ($setting->getValue('lock_registration')) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); } else if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2']) && !$setting->getValue('lock_registration')) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); + $config['accounts']['confirm_email'] ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); } else { $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); } @@ -37,7 +37,7 @@ if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_PO if ($setting->getValue('lock_registration')) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); } else if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2']) && !$setting->getValue('lock_registration')) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); + $config['accounts']['confirm_email'] ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); } else { $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); } diff --git a/public/include/smarty.inc.php b/public/include/smarty.inc.php index f95180d7..dfd1f4f6 100644 --- a/public/include/smarty.inc.php +++ b/public/include/smarty.inc.php @@ -18,6 +18,7 @@ $smarty = new Smarty; $debug->append('Define Smarty Paths', 3); $smarty->template_dir = BASEPATH . 'templates/' . THEME . '/'; $smarty->compile_dir = BASEPATH . 'templates/compile/'; +$smarty_cache_key = md5(serialize($_REQUEST) . serialize(@$_SESSION['USERDATA']['id'])); // Optional smarty caching, check Smarty documentation for details if ($config['smarty']['cache']) { @@ -26,6 +27,5 @@ if ($config['smarty']['cache']) { $smarty->cache_lifetime = $config['smarty']['cache_lifetime']; $smarty->cache_dir = BASEPATH . "templates/cache"; $smarty->use_sub_dirs = true; - $smarty_cache_key = md5(serialize($_REQUEST) . serialize(@$_SESSION['USERDATA']['id'])); } ?> diff --git a/public/templates/mail/body.tpl b/public/templates/mail/password/reset.tpl similarity index 80% rename from public/templates/mail/body.tpl rename to public/templates/mail/password/reset.tpl index a80b579a..fe488dca 100644 --- a/public/templates/mail/body.tpl +++ b/public/templates/mail/password/reset.tpl @@ -1,8 +1,8 @@ -

      Hello {$USERNAME},


      +

      Hello {$DATA.username},


      You have requested a password reset through our online form. In order to complete the request please follow this link:

      -

      http://{$smarty.server.SERVER_NAME}{$smarty.server.PHP_SELF}?page=password&action=change&token={$TOKEN}

      +

      http://{$smarty.server.SERVER_NAME}{$smarty.server.PHP_SELF}?page=password&action=change&token={$DATA.token}

      You will be asked to change your password. You can then use this new password to login to your account.

      Cheers,

      Website Administration

      diff --git a/public/templates/mail/register/confirm_email.tpl b/public/templates/mail/register/confirm_email.tpl new file mode 100644 index 00000000..67ea72c5 --- /dev/null +++ b/public/templates/mail/register/confirm_email.tpl @@ -0,0 +1,10 @@ + + +

      Hello {$DATA.username},


      +

      You have create a new account. In order to complete the registration process please follow this link:

      +

      http://{$smarty.server.SERVER_NAME}{$smarty.server.PHP_SELF}?page=account&action=confirm&token={$DATA.token}

      +

      +

      Cheers,

      +

      Website Administration

      + + diff --git a/public/templates/mmcFE/account/confirm/default.tpl b/public/templates/mmcFE/account/confirm/default.tpl new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/public/templates/mmcFE/account/confirm/default.tpl @@ -0,0 +1 @@ + diff --git a/sql/004_tokens.sql b/sql/004_tokens.sql new file mode 100644 index 00000000..439c3ab5 --- /dev/null +++ b/sql/004_tokens.sql @@ -0,0 +1,47 @@ +SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; + + +CREATE TABLE IF NOT EXISTS `tokens` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `account_id` int(11) NOT NULL, + `token` varchar(65) NOT NULL, + `type` tinyint(4) NOT NULL, + `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `token` (`token`), + KEY `account_id` (`account_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; + + +CREATE TABLE IF NOT EXISTS `token_types` ( + `id` tinyint(4) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(25) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ; + +INSERT INTO `token_types` (`id`, `name`) VALUES +(1, 'password_reset'), +(2, 'confirm_email'); + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; + +ALTER TABLE `accounts` DROP `token`; From be9a8d3fda49d79e5aa8a85cca6b4627b5688b6e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 09:05:44 +0200 Subject: [PATCH 107/137] Go through activiation even for admin accounts Fixes an issue with accounts being locked after trying to activate it. Addresses #330 --- public/include/classes/user.class.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 9927ac30..ef640098 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -421,7 +421,7 @@ class User { * @param userID int User ID * return data array Database fields as used in SELECT **/ - public function getUserData($userID) { + public function getUserData($userID) e $this->debug->append("STA " . __METHOD__, 4); $this->debug->append("Fetching user information for user id: $userID"); $stmt = $this->mysqli->prepare(" @@ -495,7 +495,6 @@ class User { VALUES (?, ?, ?, ?, ?, ?) "); } else { - $is_locked = 0; $stmt = $this->mysqli->prepare(" INSERT INTO $this->table (username, pass, email, pin, api_key, is_admin, is_locked) VALUES (?, ?, ?, ?, ?, 1, ?) From 7cc1e2543c71221c000429c1328185796fecda10 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 09:06:54 +0200 Subject: [PATCH 108/137] fixing syntax error --- public/include/classes/user.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index ef640098..52d19c44 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -421,7 +421,7 @@ class User { * @param userID int User ID * return data array Database fields as used in SELECT **/ - public function getUserData($userID) e + public function getUserData($userID) { $this->debug->append("STA " . __METHOD__, 4); $this->debug->append("Fetching user information for user id: $userID"); $stmt = $this->mysqli->prepare(" From bd32dfa9f8f6f3a9d7108d7185ea73e525948011 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 12:25:16 +0200 Subject: [PATCH 109/137] Adding invitation system to mmcfe-ng core This will allow users to send invitations to other people via email. Each account will still need to confirm the email address if the option is enabled. Addresses #330, will need to allow pool operators to enable this feature even with registrations turned off. --- public/include/autoloader.inc.php | 1 + public/include/classes/base.class.php | 6 + public/include/classes/invitation.class.php | 106 ++++++++++++++++++ public/include/classes/user.class.php | 23 +++- public/include/config/global.inc.dist.php | 17 ++- .../include/pages/account/invitations.inc.php | 25 +++++ .../include/pages/register/register.inc.php | 8 +- public/include/smarty_globals.inc.php | 1 + public/templates/mail/invitations/body.tpl | 11 ++ .../mmcFE/account/invitations/default.tpl | 43 +++++++ public/templates/mmcFE/global/navigation.tpl | 1 + public/templates/mmcFE/register/default.tpl | 3 + ..._tokens.sql => 004_tokens_invitations.sql} | 29 ++++- 13 files changed, 261 insertions(+), 13 deletions(-) create mode 100644 public/include/classes/invitation.class.php create mode 100644 public/include/pages/account/invitations.inc.php create mode 100644 public/templates/mail/invitations/body.tpl create mode 100644 public/templates/mmcFE/account/invitations/default.tpl rename sql/{004_tokens.sql => 004_tokens_invitations.sql} (60%) diff --git a/public/include/autoloader.inc.php b/public/include/autoloader.inc.php index bb765523..a5b21621 100644 --- a/public/include/autoloader.inc.php +++ b/public/include/autoloader.inc.php @@ -29,6 +29,7 @@ require_once(CLASS_DIR . '/block.class.php'); require_once(CLASS_DIR . '/setting.class.php'); require_once(CLASS_DIR . '/monitoring.class.php'); require_once(CLASS_DIR . '/user.class.php'); +require_once(CLASS_DIR . '/invitation.class.php'); require_once(CLASS_DIR . '/share.class.php'); require_once(CLASS_DIR . '/worker.class.php'); require_once(CLASS_DIR . '/statistics.class.php'); diff --git a/public/include/classes/base.class.php b/public/include/classes/base.class.php index 316b245e..836ee3b3 100644 --- a/public/include/classes/base.class.php +++ b/public/include/classes/base.class.php @@ -15,6 +15,9 @@ class Base { public function setMysql($mysqli) { $this->mysqli = $mysqli; } + public function setMail($mail) { + $this->mail = $mail; + } public function setSmarty($smarty) { $this->smarty = $smarty; } @@ -24,6 +27,9 @@ class Base { public function setConfig($config) { $this->config = $config; } + public function setToken($token) { + $this->token = $token; + } public function setTokenType($tokentype) { $this->tokentype = $tokentype; } diff --git a/public/include/classes/invitation.class.php b/public/include/classes/invitation.class.php new file mode 100644 index 00000000..ef640420 --- /dev/null +++ b/public/include/classes/invitation.class.php @@ -0,0 +1,106 @@ +debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE account_id = ?"); + if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_all(MYSQLI_ASSOC); + $this->setErrorMessage('Unable to fetch invitiations send from your account'); + $this->debug->append('Failed to fetch invitations from database: ' . $this->mysqli->errro); + return false; + } + + public function getCountInvitations($account_id) { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare("SELECT count(id) AS total FROM $this->table WHERE account_id = ?"); + if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $stmt->bind_result($total) && $stmt->fetch()) + return $total; + $this->setErrorMessage('Unable to fetch invitiations send from your account'); + $this->debug->append('Failed to fetch invitations from database: ' . $this->mysqli->errro); + return false; + } + public function getByEmail($strEmail) { + $this->debug->append("STA " . __METHOD__, 4); + return $this->getSingle($strEmail, 'id', 'email', 's'); + } + + public function getByTokenId($token_id) { + $this->debug->append("STA " . __METHOD__, 4); + return $this->getSingle($token_id, 'id', 'token_id'); + } + public function setActivated($token_id) { + if (!$iInvitationId = $this->getByTokenId($token_id)) { + $this->setErrorMessage('Unable to convert token ID to invitation ID'); + return false; + } + $field = array('name' => 'is_activated', 'type' => 'i', 'value' => 1); + return $this->updateSingle($iInvitationId, $field); + } + public function createInvitation($account_id, $email, $token_id) { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare("INSERT INTO $this->table ( account_id, email, token_id ) VALUES ( ?, ?, ?)"); + if ($stmt && $stmt->bind_param('isi', $account_id, $email, $token_id) && $stmt->execute()) + return true; + return false; + } + public function sendInvitation($account_id, $aData) { + $this->debug->append("STA " . __METHOD__, 4); + // Check data input + if (empty($aData['email']) || !filter_var($aData['email'], FILTER_VALIDATE_EMAIL)) { + $this->setErrorMessage( 'Invalid e-mail address' ); + return false; + } + if (preg_match('/[^a-z_\.\!\?\-0-9 ]/i', $aData['message'])) { + $this->setErrorMessage('Message may only contain alphanumeric characters'); + return false; + } + // Ensure this invitation does not exist yet nor do we have an account with that email + if ($this->user->getEmail($aData['email'])) { + $this->setErrorMessage('This email is already registered as an account'); + return false; + } + if ($this->getByEmail($aData['email'])) { + $this->setErrorMessage('A pending invitation for this address already exists'); + return false; + } + if (!$aData['token'] = $this->token->createToken('invitation', $account_id)) { + $this->setErrorMessage('Unable to generate invitation token: ' . $this->token->getError()); + return false; + } + $aData['username'] = $this->user->getUserName($account_id); + $aData['subject'] = 'Pending Invitation'; + if ($this->mail->sendMail('invitations/body', $aData)) { + $aToken = $this->token->getToken($aData['token']); + if (!$this->createInvitation($account_id, $aData['email'], $aToken['id'])) { + $this->setErrorMessage('Unable to create invitation record'); + return false; + } + return true; + } else { + $this->setErrorMessage('Unable to send email to recipient'); + } + $this->setErrorMessage('Unable to send invitation'); + return false; + } +} + +$invitation = new invitation(); +$invitation->setDebug($debug); +$invitation->setMysql($mysqli); +$invitation->setMail($mail); +$invitation->setUser($user); +$invitation->setToken($oToken); +$invitation->setConfig($config); + +?> diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 52d19c44..64427486 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -454,7 +454,7 @@ class User { * @param email2 string Email confirmation * @return bool **/ - public function register($username, $password1, $password2, $pin, $email1='', $email2='') { + public function register($username, $password1, $password2, $pin, $email1='', $email2='', $strToken='') { $this->debug->append("STA " . __METHOD__, 4); if (strlen($username > 40)) { $this->setErrorMessage('Username exceeding character limit'); @@ -488,8 +488,25 @@ class User { $this->setErrorMessage( 'Invalid PIN' ); return false; } + if (isset($strToken)) { + $aToken = $this->token->getToken($strToken); + // Circle dependency, so we create our own object here + $invitation = new Invitation(); + $invitation->setMysql($this->mysqli); + $invitation->setDebug($this->debug); + $invitation->setUser($this); + $invitation->setConfig($this->config); + if (!$invitation->setActivated($aToken['id'])) { + $this->setErrorMessage('Unable to activate your invitation'); + return false; + } + if (!$this->token->deleteToken($strToken)) { + $this->setErrorMessage('Unable to remove used token'); + return false; + } + } if ($this->mysqli->query("SELECT id FROM $this->table LIMIT 1")->num_rows > 0) { - $this->config['accounts']['confirm_email'] ? $is_locked = 1 : $is_locked = 0; + $this->config['accounts']['confirm_email']['enabled'] ? $is_locked = 1 : $is_locked = 0; $stmt = $this->mysqli->prepare(" INSERT INTO $this->table (username, pass, email, pin, api_key, is_locked) VALUES (?, ?, ?, ?, ?, ?) @@ -508,7 +525,7 @@ class User { $username_clean = strip_tags($username); if ($this->checkStmt($stmt) && $stmt->bind_param('sssssi', $username_clean, $password_hash, $email1, $pin_hash, $apikey_hash, $is_locked) && $stmt->execute()) { - if ($this->config['accounts']['confirm_email']) { + if ($this->config['accounts']['confirm_email']['enabled']) { if ($token = $this->token->createToken('confirm_email', $stmt->insert_id)) { $aData['username'] = $username_clean; $aData['token'] = $token; diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 4c7595cc..deeba5b9 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -129,14 +129,27 @@ $config['website']['mobile_theme'] = 'mobile'; * By default, all newly created accounts will require an email verificaiton. * Only after acitivating an account the user will be able to login * + * Invitations will allow your users to invite new members to join the pool. + * After sending a mail to the invited user, they can register using the token + * created. Invitations can be enabled and disabled. They are listed on the accounts + * page. + * + * You can limit the number of registrations send per account via configuration + * variable. + * * Options: * confirm_email : Send confirmation mail to user after registration + * invitations : Enable or disable the invitation system + * count : Maximum invitations a user is able to send * * Defaults: * confirm_email : true - * + * invitations : true + * count : 5 **/ -$config['accounts']['confirm_email'] = true; +$config['accounts']['confirm_email']['enabled'] = true; +$config['accounts']['invitations']['enabled'] = true; +$config['accounts']['invitations']['count'] = 5; /** * Some basic access restrictions on some pages diff --git a/public/include/pages/account/invitations.inc.php b/public/include/pages/account/invitations.inc.php new file mode 100644 index 00000000..cd8d644a --- /dev/null +++ b/public/include/pages/account/invitations.inc.php @@ -0,0 +1,25 @@ +isAuthenticated()) { + if ($config['accounts']['invitations']['enabled']) { + if ($invitation->getCountInvitations($_SESSION['USERDATA']['id']) >= $config['accounts']['invitations']['count']) { + $_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded the allowed invitations of ' . $config['accounts']['invitations']['count'], 'TYPE' => 'errormsg'); + } else if (isset($_POST['do']) && $_POST['do'] == 'sendInvitation') { + if ($invitation->sendInvitation($_SESSION['USERDATA']['id'], $_POST['data'])) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Invitation sent'); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to send invitation to recipient: ' . $invitation->getError(), 'TYPE' => 'errormsg'); + } + } + $aInvitations = $invitation->getInvitations($_SESSION['USERDATA']['id']); + $smarty->assign('INVITATIONS', $aInvitations); + } else { + $aInvitations = array(); + $_SESSION['POPUP'][] = array('CONTENT' => 'Invitations are disabled', 'TYPE' => 'errormsg'); + } +} +$smarty->assign('CONTENT', 'default.tpl'); +?> diff --git a/public/include/pages/register/register.inc.php b/public/include/pages/register/register.inc.php index 526ef247..9a099816 100644 --- a/public/include/pages/register/register.inc.php +++ b/public/include/pages/register/register.inc.php @@ -19,8 +19,8 @@ if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_PO $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'])); if ($setting->getValue('lock_registration')) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); - } else if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2']) && !$setting->getValue('lock_registration')) { - $config['accounts']['confirm_email'] ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); + } else if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $_POST['token']) && !$setting->getValue('lock_registration')) { + $config['accounts']['confirm_email']['enabled'] ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); } else { $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); } @@ -36,8 +36,8 @@ if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_PO } else { if ($setting->getValue('lock_registration')) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); - } else if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2']) && !$setting->getValue('lock_registration')) { - $config['accounts']['confirm_email'] ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); + } else if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $_POST['token']) && !$setting->getValue('lock_registration')) { + $config['accounts']['confirm_email']['enabled'] ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); } else { $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); } diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index a3a47e66..d0d5dbbf 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -48,6 +48,7 @@ $aGlobal = array( 'chaininfo' => $config['chaininfo'], 'config' => array( 'website' => array( 'title' => $config['website']['title'], 'acl' => $config['website']['acl'] ), + 'accounts' => $config['accounts'], 'price' => array( 'currency' => $config['price']['currency'] ), 'targetdiff' => $config['difficulty'], 'currency' => $config['currency'], diff --git a/public/templates/mail/invitations/body.tpl b/public/templates/mail/invitations/body.tpl new file mode 100644 index 00000000..353b82e0 --- /dev/null +++ b/public/templates/mail/invitations/body.tpl @@ -0,0 +1,11 @@ + + +

      Hello valued miner,


      +

      {$DATA.username} invited you to participate on this pool: +

      http://{$smarty.server.SERVER_NAME}{$smarty.server.PHP_SELF}?page=register&token={$DATA.token}

      +{if $DATA.message}

      Personal message:

      {$DATA.message}

      {/if} +

      +

      Cheers,

      +

      Website Administration

      + + diff --git a/public/templates/mmcFE/account/invitations/default.tpl b/public/templates/mmcFE/account/invitations/default.tpl new file mode 100644 index 00000000..b8d6743a --- /dev/null +++ b/public/templates/mmcFE/account/invitations/default.tpl @@ -0,0 +1,43 @@ +{include file="global/block_header.tpl" ALIGN="left" BLOCK_HEADER="Invitations"} +
      + + + +
      New Password:
      New Password Repeat:
      + + + + + + + + + + + +
      E-Mail
      Message
      + +
      + +{include file="global/block_footer.tpl"} + +{include file="global/block_header.tpl" ALIGN="right" BLOCK_HEADER="Past Invitations"} + + + + + + + + + +{section name=invite loop=$INVITATIONS} + + + + + +{/section} + +
      E-MailSentActivated
      {$INVITATIONS[invite].email}{$INVITATIONS[invite].time|date_format:"%d/%m/%Y %H:%M:%S"}
      +{include file="global/block_footer.tpl"} diff --git a/public/templates/mmcFE/global/navigation.tpl b/public/templates/mmcFE/global/navigation.tpl index 42c89e9a..4d3d3621 100644 --- a/public/templates/mmcFE/global/navigation.tpl +++ b/public/templates/mmcFE/global/navigation.tpl @@ -7,6 +7,7 @@
    • My Workers
    • Transactions
    • Notifications
    • + {if $GLOBAL.config.accounts.invitations}
    • Invitations
    • {/if}
  • {/if} diff --git a/public/templates/mmcFE/register/default.tpl b/public/templates/mmcFE/register/default.tpl index 3316dfa9..7253a243 100644 --- a/public/templates/mmcFE/register/default.tpl +++ b/public/templates/mmcFE/register/default.tpl @@ -1,6 +1,9 @@ {include file="global/block_header.tpl" BLOCK_HEADER="Join our pool" BLOCK_STYLE="clear:none;"}
    +{if $smarty.request.token|default:""} + +{/if} diff --git a/sql/004_tokens.sql b/sql/004_tokens_invitations.sql similarity index 60% rename from sql/004_tokens.sql rename to sql/004_tokens_invitations.sql index 439c3ab5..3ed5a8dc 100644 --- a/sql/004_tokens.sql +++ b/sql/004_tokens_invitations.sql @@ -21,7 +21,7 @@ CREATE TABLE IF NOT EXISTS `tokens` ( /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; SET time_zone = "+00:00"; /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; @@ -34,14 +34,35 @@ CREATE TABLE IF NOT EXISTS `token_types` ( `id` tinyint(4) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(25) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ; INSERT INTO `token_types` (`id`, `name`) VALUES (1, 'password_reset'), -(2, 'confirm_email'); +(2, 'confirm_email'), +(3, 'invitation'); /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; -ALTER TABLE `accounts` DROP `token`; +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; + + +CREATE TABLE IF NOT EXISTS `invitations` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `account_id` int(11) unsigned NOT NULL, + `email` varchar(50) CHARACTER SET utf8 NOT NULL, + `token_id` int(11) NOT NULL, + `is_activated` tinyint(1) NOT NULL DEFAULT '0', + `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; From 7f4f5cd343c8481ab825948b12061563eb562d28 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 12:43:29 +0200 Subject: [PATCH 110/137] Make invitations configurable via admin panel Invitations can now be configured through admin panel settings. By default, invitations are enabled. Invitation system is also available if registrations are disabled. To completely remove the ability of new users to sign up, disable both registration and invitations. Fixes #330 --- public/include/config/global.inc.dist.php | 7 +-- .../include/pages/account/invitations.inc.php | 2 +- public/include/pages/admin/settings.inc.php | 1 + public/include/pages/register.inc.php | 2 +- .../include/pages/register/register.inc.php | 46 +++++++++---------- public/include/smarty_globals.inc.php | 1 + .../mmcFE/admin/settings/default.tpl | 10 ++++ public/templates/mmcFE/global/navigation.tpl | 2 +- 8 files changed, 40 insertions(+), 31 deletions(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index deeba5b9..e66197fe 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -131,24 +131,21 @@ $config['website']['mobile_theme'] = 'mobile'; * * Invitations will allow your users to invite new members to join the pool. * After sending a mail to the invited user, they can register using the token - * created. Invitations can be enabled and disabled. They are listed on the accounts - * page. + * created. Invitations can be enabled and disabled through the admin panel. + * Sent invitations are listed on the account invitations page. * * You can limit the number of registrations send per account via configuration * variable. * * Options: * confirm_email : Send confirmation mail to user after registration - * invitations : Enable or disable the invitation system * count : Maximum invitations a user is able to send * * Defaults: * confirm_email : true - * invitations : true * count : 5 **/ $config['accounts']['confirm_email']['enabled'] = true; -$config['accounts']['invitations']['enabled'] = true; $config['accounts']['invitations']['count'] = 5; /** diff --git a/public/include/pages/account/invitations.inc.php b/public/include/pages/account/invitations.inc.php index cd8d644a..b12e2d4e 100644 --- a/public/include/pages/account/invitations.inc.php +++ b/public/include/pages/account/invitations.inc.php @@ -4,7 +4,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); if ($user->isAuthenticated()) { - if ($config['accounts']['invitations']['enabled']) { + if (!$setting->getValue('disable_invitations')) { if ($invitation->getCountInvitations($_SESSION['USERDATA']['id']) >= $config['accounts']['invitations']['count']) { $_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded the allowed invitations of ' . $config['accounts']['invitations']['count'], 'TYPE' => 'errormsg'); } else if (isset($_POST['do']) && $_POST['do'] == 'sendInvitation') { diff --git a/public/include/pages/admin/settings.inc.php b/public/include/pages/admin/settings.inc.php index dab1f096..9bc7ef3b 100644 --- a/public/include/pages/admin/settings.inc.php +++ b/public/include/pages/admin/settings.inc.php @@ -19,6 +19,7 @@ if (@$_REQUEST['do'] == 'save' && !empty($_REQUEST['data'])) { // Fetch settings to propagate to template $smarty->assign("MAINTENANCE", $setting->getValue('maintenance')); $smarty->assign("LOCKREGISTRATION", $setting->getValue('lock_registration')); +$smarty->assign("DISABLEINVITATIONS", $setting->getValue('disable_invitations')); // Tempalte specifics $smarty->assign("CONTENT", "default.tpl"); diff --git a/public/include/pages/register.inc.php b/public/include/pages/register.inc.php index d47c67ed..9b338b51 100644 --- a/public/include/pages/register.inc.php +++ b/public/include/pages/register.inc.php @@ -3,7 +3,7 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -if ($setting->getValue('lock_registration')) { +if ($setting->getValue('lock_registration') && !$config['accounts']['invitations']['enabled']) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); $smarty->assign("CONTENT", "disabled.tpl"); } else { diff --git a/public/include/pages/register/register.inc.php b/public/include/pages/register/register.inc.php index 9a099816..9e438853 100644 --- a/public/include/pages/register/register.inc.php +++ b/public/include/pages/register/register.inc.php @@ -13,33 +13,33 @@ if ($config['recaptcha']['enabled']) { ); } -// Check if recaptcha is enabled, process form data if valid -if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_POST["recaptcha_response_field"]!=''){ - if ($rsp->is_valid) { - $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'])); - if ($setting->getValue('lock_registration')) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); - } else if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $_POST['token']) && !$setting->getValue('lock_registration')) { +if ($setting->getValue('disable_invitations') && $setting->getValue('lock_registration')) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); +} else { + // Check if recaptcha is enabled, process form data if valid + if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_POST["recaptcha_response_field"]!=''){ + if ($rsp->is_valid) { + $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'])); + if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $_POST['token'])) { + $config['accounts']['confirm_email']['enabled'] ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); + } + } else { + $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'], $rsp->error)); + $_SESSION['POPUP'][] = array('CONTENT' => 'Invalid Captcha, please try again. (' . $rsp->error . ')', 'TYPE' => 'errormsg'); + } + // Empty captcha + } else if ($config['recaptcha']['enabled']) { + $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'], $rsp->error)); + $_SESSION['POPUP'][] = array('CONTENT' => 'Empty Captcha, please try again.', 'TYPE' => 'errormsg'); + // Captcha disabled + } else { + if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $_POST['token'])) { $config['accounts']['confirm_email']['enabled'] ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); } else { $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); } - } else { - $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'], $rsp->error)); - $_SESSION['POPUP'][] = array('CONTENT' => 'Invalid Captcha, please try again. (' . $rsp->error . ')', 'TYPE' => 'errormsg'); - } -// Empty captcha -} else if ($config['recaptcha']['enabled']) { - $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'], $rsp->error)); - $_SESSION['POPUP'][] = array('CONTENT' => 'Empty Captcha, please try again.', 'TYPE' => 'errormsg'); -// Captcha disabled -} else { - if ($setting->getValue('lock_registration')) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); - } else if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $_POST['token']) && !$setting->getValue('lock_registration')) { - $config['accounts']['confirm_email']['enabled'] ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); - } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); } } diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index d0d5dbbf..5f6ca959 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -49,6 +49,7 @@ $aGlobal = array( 'config' => array( 'website' => array( 'title' => $config['website']['title'], 'acl' => $config['website']['acl'] ), 'accounts' => $config['accounts'], + 'disable_invitations' => $setting->getValue('disable_invitations'), 'price' => array( 'currency' => $config['price']['currency'] ), 'targetdiff' => $config['difficulty'], 'currency' => $config['currency'], diff --git a/public/templates/mmcFE/admin/settings/default.tpl b/public/templates/mmcFE/admin/settings/default.tpl index a2ffbd5b..154c39d1 100644 --- a/public/templates/mmcFE/admin/settings/default.tpl +++ b/public/templates/mmcFE/admin/settings/default.tpl @@ -30,6 +30,16 @@ + + + + +
    Disable Invitations + +
    diff --git a/public/templates/mmcFE/global/navigation.tpl b/public/templates/mmcFE/global/navigation.tpl index 4d3d3621..79d36af8 100644 --- a/public/templates/mmcFE/global/navigation.tpl +++ b/public/templates/mmcFE/global/navigation.tpl @@ -7,7 +7,7 @@
  • My Workers
  • Transactions
  • Notifications
  • - {if $GLOBAL.config.accounts.invitations}
  • Invitations
  • {/if} + {if !$GLOBAL.config.disable_invitations}
  • Invitations
  • {/if} {/if} From bf3cd25326eb054767de77f96f6ba8768205ade4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 12:52:55 +0200 Subject: [PATCH 111/137] removing unused token methods --- public/include/classes/user.class.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 64427486..8c108dbd 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -49,9 +49,6 @@ class User { public function getUserLocked($id) { return $this->getSingle($id, 'is_locked', 'id'); } - public function getUserToken($id) { - return $this->getSingle($id, 'token', 'id'); - } public function getUserIp($id) { return $this->getSingle($id, 'loggedIp', 'id'); } @@ -61,9 +58,6 @@ class User { public function getUserFailed($id) { return $this->getSingle($id, 'failed_logins', 'id'); } - public function getIdFromToken($token) { - return $this->getSingle($token, 'id', 'token', 's'); - } public function isLocked($id) { return $this->getUserLocked($id); } @@ -78,10 +72,6 @@ class User { $field = array('name' => 'is_admin', 'type' => 'i', 'value' => !$this->isAdmin($id)); return $this->updateSingle($id, $field); } - public function setUserToken($id) { - $field = array('name' => 'token', 'type' => 's', 'value' => setHash($id.time())); - return $this->updateSingle($id, $field); - } public function setUserFailed($id, $value) { $field = array( 'name' => 'failed_logins', 'type' => 'i', 'value' => $value); return $this->updateSingle($id, $field); From 8f720625585d25d3f6dcd2cb71d93f1eef987d2d Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 13:44:22 +0200 Subject: [PATCH 112/137] Fixing issue with registration disabled and invitations This will fix an issue with certain combinations of registration and/or invitations being enabled or disabled. Addresses #330 --- public/include/pages/register.inc.php | 5 ++++- public/include/pages/register/register.inc.php | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/public/include/pages/register.inc.php b/public/include/pages/register.inc.php index 9b338b51..2866a4b6 100644 --- a/public/include/pages/register.inc.php +++ b/public/include/pages/register.inc.php @@ -3,9 +3,12 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -if ($setting->getValue('lock_registration') && !$config['accounts']['invitations']['enabled']) { +if ($setting->getValue('lock_registration') && $setting->getValue('disable_invitations')) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); $smarty->assign("CONTENT", "disabled.tpl"); +} else if (!$setting->getValue('disable_invitations') && !isset($_GET['token'])) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Only invited users are allowed to register.', 'TYPE' => 'errormsg'); + $smarty->assign("CONTENT", "disabled.tpl"); } else { if ($config['recaptcha']['enabled']) { require_once(INCLUDE_DIR . '/lib/recaptchalib.php'); diff --git a/public/include/pages/register/register.inc.php b/public/include/pages/register/register.inc.php index 9e438853..560961f6 100644 --- a/public/include/pages/register/register.inc.php +++ b/public/include/pages/register/register.inc.php @@ -15,6 +15,8 @@ if ($config['recaptcha']['enabled']) { if ($setting->getValue('disable_invitations') && $setting->getValue('lock_registration')) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); +} else if (!$setting->getValue('disable_invitations') && !isset($_POST['token'])) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Only invited users are allowed to register.', 'TYPE' => 'errormsg'); } else { // Check if recaptcha is enabled, process form data if valid if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_POST["recaptcha_response_field"]!=''){ From 0f00f7d322826ecfc2f64e77f377f9031cec1275 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 13:46:20 +0200 Subject: [PATCH 113/137] Another fix for registration issues Addresses #330 --- public/include/pages/register.inc.php | 2 +- public/include/pages/register/register.inc.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/include/pages/register.inc.php b/public/include/pages/register.inc.php index 2866a4b6..01b71b18 100644 --- a/public/include/pages/register.inc.php +++ b/public/include/pages/register.inc.php @@ -6,7 +6,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); if ($setting->getValue('lock_registration') && $setting->getValue('disable_invitations')) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); $smarty->assign("CONTENT", "disabled.tpl"); -} else if (!$setting->getValue('disable_invitations') && !isset($_GET['token'])) { +} else if ($setting->getValue('lock_registration') && !$setting->getValue('disable_invitations') && !isset($_GET['token'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'Only invited users are allowed to register.', 'TYPE' => 'errormsg'); $smarty->assign("CONTENT", "disabled.tpl"); } else { diff --git a/public/include/pages/register/register.inc.php b/public/include/pages/register/register.inc.php index 560961f6..ab43ba34 100644 --- a/public/include/pages/register/register.inc.php +++ b/public/include/pages/register/register.inc.php @@ -15,7 +15,7 @@ if ($config['recaptcha']['enabled']) { if ($setting->getValue('disable_invitations') && $setting->getValue('lock_registration')) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); -} else if (!$setting->getValue('disable_invitations') && !isset($_POST['token'])) { +} else if ($setting->getValue('lock_registration') && !$setting->getValue('disable_invitations') && !isset($_POST['token'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'Only invited users are allowed to register.', 'TYPE' => 'errormsg'); } else { // Check if recaptcha is enabled, process form data if valid From 525c0ab0090a67273b455ed846a942a100ec07a5 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 14:28:54 +0200 Subject: [PATCH 114/137] Fixing PHP Warning on unsert token on register Addresses #330 and cleans up PHP Log --- public/include/classes/user.class.php | 2 +- public/include/pages/register/register.inc.php | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 8c108dbd..6d7a6d27 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -478,7 +478,7 @@ class User { $this->setErrorMessage( 'Invalid PIN' ); return false; } - if (isset($strToken)) { + if (isset($strToken) && !empty($strToken)) { $aToken = $this->token->getToken($strToken); // Circle dependency, so we create our own object here $invitation = new Invitation(); diff --git a/public/include/pages/register/register.inc.php b/public/include/pages/register/register.inc.php index ab43ba34..ca165e3b 100644 --- a/public/include/pages/register/register.inc.php +++ b/public/include/pages/register/register.inc.php @@ -22,7 +22,8 @@ if ($setting->getValue('disable_invitations') && $setting->getValue('lock_regist if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_POST["recaptcha_response_field"]!=''){ if ($rsp->is_valid) { $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'])); - if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $_POST['token'])) { + isset($_POST['token']) ? $token = $_POST['token'] : $token = ''; + if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $token)) { $config['accounts']['confirm_email']['enabled'] ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); } else { $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); @@ -37,7 +38,8 @@ if ($setting->getValue('disable_invitations') && $setting->getValue('lock_regist $_SESSION['POPUP'][] = array('CONTENT' => 'Empty Captcha, please try again.', 'TYPE' => 'errormsg'); // Captcha disabled } else { - if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $_POST['token'])) { + isset($_POST['token']) ? $token = $_POST['token'] : $token = ''; + if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $token)) { $config['accounts']['confirm_email']['enabled'] ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); } else { $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); From 501f369b4e570d64776be3342edee783560b2e88 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 15:31:18 +0200 Subject: [PATCH 115/137] Further fixes to PHP warnings Addresses #330 and further cleans up the PHP log. --- public/include/classes/user.class.php | 6 +++--- public/include/pages/password/change.inc.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 6d7a6d27..9d448a40 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -552,7 +552,7 @@ class User { **/ public function resetPassword($token, $new1, $new2) { $this->debug->append("STA " . __METHOD__, 4); - if ($token = $this->token->getToken($token)) { + if ($aToken = $this->token->getToken($token)) { if ($new1 !== $new2) { $this->setErrorMessage( 'New passwords do not match' ); return false; @@ -563,8 +563,8 @@ class User { } $new_hash = $this->getHash($new1); $stmt = $this->mysqli->prepare("UPDATE $this->table SET pass = ? WHERE id = ?"); - if ($this->checkStmt($stmt) && $stmt->bind_param('si', $new_hash, $token['account_id']) && $stmt->execute() && $stmt->affected_rows === 1) { - if ($this->token->deleteToken($token)) { + if ($this->checkStmt($stmt) && $stmt->bind_param('si', $new_hash, $aToken['account_id']) && $stmt->execute() && $stmt->affected_rows === 1) { + if ($this->token->deleteToken($aToken['token'])) { return true; } else { $this->setErrorMessage('Unable to invalidate used token'); diff --git a/public/include/pages/password/change.inc.php b/public/include/pages/password/change.inc.php index b45b3ee4..919632bd 100644 --- a/public/include/pages/password/change.inc.php +++ b/public/include/pages/password/change.inc.php @@ -4,7 +4,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); -if ($_POST['do'] == 'resetPassword') { +if (isset($_POST['do']) && $_POST['do'] == 'resetPassword') { if ($user->resetPassword($_POST['token'], $_POST['newPassword'], $_POST['newPassword2'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'Password reset complete! Please login.'); } else { From 7b929ed3e4b97ec92a5f84c5f42eaae6697358c6 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 16:16:40 +0200 Subject: [PATCH 116/137] Fixing PHP Warning on resetting failed login count Addresses #330 and further cleans PHP Log --- public/include/pages/account/reset_failed.inc.php | 3 ++- public/templates/mmcFE/global/empty.tpl | 0 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 public/templates/mmcFE/global/empty.tpl diff --git a/public/include/pages/account/reset_failed.inc.php b/public/include/pages/account/reset_failed.inc.php index bce9b418..39541dc2 100644 --- a/public/include/pages/account/reset_failed.inc.php +++ b/public/include/pages/account/reset_failed.inc.php @@ -8,5 +8,6 @@ if ($user->isAuthenticated()) { $user->setUserFailed($_SESSION['USERDATA']['id'], 0); header("Location: " . $_SERVER['HTTP_REFERER']); } - +// Somehow we still need to load this empty template +$smarty->assign("CONTENT", "../../global/empty.tpl"); ?> diff --git a/public/templates/mmcFE/global/empty.tpl b/public/templates/mmcFE/global/empty.tpl new file mode 100644 index 00000000..e69de29b From 41ec58ea168a4b8aa6eb2a939901df8c3032051a Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 16:28:22 +0200 Subject: [PATCH 117/137] Adding inline docuemtation to invitation class Adding proper inline documentation to invitation class. --- public/include/classes/invitation.class.php | 40 +++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/public/include/classes/invitation.class.php b/public/include/classes/invitation.class.php index ef640420..822dfef3 100644 --- a/public/include/classes/invitation.class.php +++ b/public/include/classes/invitation.class.php @@ -21,6 +21,11 @@ class Invitation extends Base { return false; } + /** + * Count invitations sent by an account_id + * @param account_id integer Account ID + * @return mixes Integer on success, boolean on failure + **/ public function getCountInvitations($account_id) { $this->debug->append("STA " . __METHOD__, 4); $stmt = $this->mysqli->prepare("SELECT count(id) AS total FROM $this->table WHERE account_id = ?"); @@ -30,15 +35,34 @@ class Invitation extends Base { $this->debug->append('Failed to fetch invitations from database: ' . $this->mysqli->errro); return false; } + + /** + * Get a specific invitation by email address + * Used to ensure no invitation was already sent + * @param strEmail string Email address to check for + * @return bool boolean true of ralse + **/ public function getByEmail($strEmail) { $this->debug->append("STA " . __METHOD__, 4); return $this->getSingle($strEmail, 'id', 'email', 's'); } + /** + * Get a specific token by token ID + * Used to match an invitation against a token + * @param token_id integer Token ID stored in invitation + * @return data mixed Invitation ID on success, false on error + **/ public function getByTokenId($token_id) { $this->debug->append("STA " . __METHOD__, 4); return $this->getSingle($token_id, 'id', 'token_id'); } + + /** + * Set an invitation as activated by the invitee + * @param token_id integer Token to activate + * @return bool boolean true or false + **/ public function setActivated($token_id) { if (!$iInvitationId = $this->getByTokenId($token_id)) { $this->setErrorMessage('Unable to convert token ID to invitation ID'); @@ -47,6 +71,14 @@ class Invitation extends Base { $field = array('name' => 'is_activated', 'type' => 'i', 'value' => 1); return $this->updateSingle($iInvitationId, $field); } + + /** + * Insert a new invitation to the database + * @param account_id integer Account ID to bind the invitation to + * @param email string Email address the invite was sent to + * @param token_id integer Token ID used during invitation + * @return bool boolean True of false + **/ public function createInvitation($account_id, $email, $token_id) { $this->debug->append("STA " . __METHOD__, 4); $stmt = $this->mysqli->prepare("INSERT INTO $this->table ( account_id, email, token_id ) VALUES ( ?, ?, ?)"); @@ -54,6 +86,13 @@ class Invitation extends Base { return true; return false; } + /** + * Send an invitation out to a user + * Uses the mail class to send mails + * @param account_id integer Sending account ID + * @param aData array Data array including mail information + * @return bool boolean True or false + **/ public function sendInvitation($account_id, $aData) { $this->debug->append("STA " . __METHOD__, 4); // Check data input @@ -95,6 +134,7 @@ class Invitation extends Base { } } +// Instantiate class $invitation = new invitation(); $invitation->setDebug($debug); $invitation->setMysql($mysqli); From b7ffbd0bfd798bfa872cd189ee16b133bb1bf447 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 16 Jul 2013 16:02:52 +0200 Subject: [PATCH 118/137] fixing issue for first created admin user --- public/include/classes/user.class.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 9d448a40..4f9f5cd4 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -497,11 +497,14 @@ class User { } if ($this->mysqli->query("SELECT id FROM $this->table LIMIT 1")->num_rows > 0) { $this->config['accounts']['confirm_email']['enabled'] ? $is_locked = 1 : $is_locked = 0; + $is_admin = 0; $stmt = $this->mysqli->prepare(" INSERT INTO $this->table (username, pass, email, pin, api_key, is_locked) VALUES (?, ?, ?, ?, ?, ?) "); } else { + $is_locked = 0; + $is_admin = 1; $stmt = $this->mysqli->prepare(" INSERT INTO $this->table (username, pass, email, pin, api_key, is_admin, is_locked) VALUES (?, ?, ?, ?, ?, 1, ?) @@ -515,7 +518,7 @@ class User { $username_clean = strip_tags($username); if ($this->checkStmt($stmt) && $stmt->bind_param('sssssi', $username_clean, $password_hash, $email1, $pin_hash, $apikey_hash, $is_locked) && $stmt->execute()) { - if ($this->config['accounts']['confirm_email']['enabled']) { + if ($this->config['accounts']['confirm_email']['enabled'] && $is_admin != 1) { if ($token = $this->token->createToken('confirm_email', $stmt->insert_id)) { $aData['username'] = $username_clean; $aData['token'] = $token; From 271c7760534ab9a0445517336cd3b76b3381772c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 16 Jul 2013 21:16:53 +0200 Subject: [PATCH 119/137] Fixing issue with wrong require Fixes #469 --- cronjobs/pps_payout.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php index bb48e62e..5e6f673a 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -148,5 +148,5 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { } } -require_once('cron.inc.php'); +require_once('cron_end.inc.php'); ?> From 60b4bba4898d178b9d19fb5ffde80c80c333f69c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 16 Jul 2013 21:49:12 +0200 Subject: [PATCH 120/137] Fixing wrong round share calculations on PPLNS This addresses #468, might be the actual fix already. --- cronjobs/pplns_payout.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cronjobs/pplns_payout.php b/cronjobs/pplns_payout.php index 6bb4234a..f7cdb6b2 100755 --- a/cronjobs/pplns_payout.php +++ b/cronjobs/pplns_payout.php @@ -65,7 +65,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { if ($iRoundShares >= $pplns_target) { $log->logDebug("Matching or exceeding PPLNS target of $pplns_target with $iRoundShares"); - $aAccountShares = $share->getSharesForAccounts($aBlock['share_id'] - $pplns_target + 1, $aBlock['share_id']); + $aAccountShares = $share->getSharesForAccounts($aBlock['share_id'] - $pplns_target, $aBlock['share_id']); if (empty($aAccountShares)) { $log->logFatal("No shares found for this block, aborted! Block Height : " . $aBlock['height']); $monitoring->setStatus($cron_name . "_active", "yesno", 0); From a12499ecd9782fc524931eb7e3b48f914efc053e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 16 Jul 2013 21:55:52 +0200 Subject: [PATCH 121/137] Fixing negative time since last block This will fix #466 if approved by reporter. --- public/include/pages/statistics/pool.inc.php | 1 + 1 file changed, 1 insertion(+) diff --git a/public/include/pages/statistics/pool.inc.php b/public/include/pages/statistics/pool.inc.php index f3669625..256d4edf 100644 --- a/public/include/pages/statistics/pool.inc.php +++ b/public/include/pages/statistics/pool.inc.php @@ -38,6 +38,7 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { $now = new DateTime( "now" ); if (!empty($aBlockData)) { $dTimeSinceLast = ($now->getTimestamp() - $aBlockData['time']); + if ($dTimeSinceLast < 0) $dTimeSinceLast = 0; } else { $dTimeSinceLast = 0; } From 39eb7f5714a4f48dfdd4a27fc61d6eaa47fff405 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 17 Jul 2013 09:18:18 +0200 Subject: [PATCH 122/137] Fixing option argument in run-crons for PHP binary Fixes #470 --- cronjobs/run-crons.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cronjobs/run-crons.sh b/cronjobs/run-crons.sh index 1f55bd77..fcc66f70 100755 --- a/cronjobs/run-crons.sh +++ b/cronjobs/run-crons.sh @@ -31,10 +31,12 @@ while getopts "hvp:" opt; do echo "Usage: $0 [-v] [-p PHP_BINARY]"; exit 0 ;; - v) VERBOSE=1 - ;; - p) PHP_BIN=$2; shift; - ;; + v) VERBOSE=1 ;; + p) PHP_BIN=$OPTARG ;; + :) + echo "Option -$OPTARG requires an argument." >&2 + exit 1 + ;; esac done From d492b532b7d71df3c64bba0651ec719d86daf3c7 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 17 Jul 2013 09:43:02 +0200 Subject: [PATCH 123/137] Adding ability to disable the sites API functions Addresses #467 and will fix upon merge. --- public/include/autoloader.inc.php | 1 + public/include/classes/api.class.php | 23 ++++++++++++++ public/include/config/global.inc.dist.php | 31 ++++++++++--------- public/include/pages/api.inc.php | 6 ++-- .../include/pages/api/getblockcount.inc.php | 6 ++-- .../include/pages/api/getblocksfound.inc.php | 6 ++-- .../pages/api/getcurrentworkers.inc.php | 6 ++-- .../include/pages/api/getdifficulty.inc.php | 6 ++-- .../pages/api/getestimatedtime.inc.php | 6 ++-- .../include/pages/api/getpoolhashrate.inc.php | 6 ++-- .../pages/api/getpoolsharerate.inc.php | 6 ++-- .../include/pages/api/getpoolstatus.inc.php | 3 ++ .../pages/api/gettimesincelastblock.inc.php | 6 ++-- .../include/pages/api/getuserstatus.inc.php | 6 ++-- .../include/pages/api/getuserworkers.inc.php | 6 ++-- public/include/pages/api/public.inc.php | 6 ++-- 16 files changed, 91 insertions(+), 39 deletions(-) create mode 100644 public/include/classes/api.class.php diff --git a/public/include/autoloader.inc.php b/public/include/autoloader.inc.php index a5b21621..929e2f85 100644 --- a/public/include/autoloader.inc.php +++ b/public/include/autoloader.inc.php @@ -22,6 +22,7 @@ require_once(INCLUDE_DIR . '/database.inc.php'); require_once(INCLUDE_DIR . '/smarty.inc.php'); // Load classes that need the above as dependencies require_once(CLASS_DIR . '/base.class.php'); +require_once(CLASS_DIR . '/api.class.php'); require_once(CLASS_DIR . '/mail.class.php'); require_once(CLASS_DIR . '/tokentype.class.php'); require_once(CLASS_DIR . '/token.class.php'); diff --git a/public/include/classes/api.class.php b/public/include/classes/api.class.php new file mode 100644 index 00000000..36374162 --- /dev/null +++ b/public/include/classes/api.class.php @@ -0,0 +1,23 @@ +config['website']['api']['disabled']) { + return true; + } else { + if ($error == true) { + header('HTTP/1.1 501 Not implemented'); + die('501 Not implemented'); + } + } + } +} + +$api = new Api(); +$api->setConfig($config); diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index e66197fe..173a47b7 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -96,22 +96,24 @@ $config['ap_threshold']['max'] = 250; * Website specific configuration settings * * Explanation: - * title : Website title used in master template - * name : The pool name, displayed in the header and mails - * slogan : A special slogan, also displayed in the header below name - * email : `From` addresses used in notifications - * theme : Theme used for desktop browsers - * mobile : Enable/Disable mobile theme support - * mobile_theme : Theme used for mobile browsers + * title : Website title used in master template + * name : The pool name, displayed in the header and mails + * slogan : A special slogan, also displayed in the header below name + * email : `From` addresses used in notifications + * theme : Theme used for desktop browsers + * mobile : Enable/Disable mobile theme support + * mobile_theme : Theme used for mobile browsers + * api disabled : Disable the sites API functions * * Defaults: - * title = `The Pool - Mining Evolved` - * name = `The Pool` - * slogan = `Resistance is futile` - * email = `test@example.com` - * theme = `mmcFE` - * mobile = true - * mobile_theme = `mobile` + * title = `The Pool - Mining Evolved` + * name = `The Pool` + * slogan = `Resistance is futile` + * email = `test@example.com` + * theme = `mmcFE` + * mobile = true + * mobile_theme = `mobile` + * api disbabled = false **/ $config['website']['title'] = 'The Pool - Mining Evolved'; $config['website']['name'] = 'The Pool'; @@ -120,6 +122,7 @@ $config['website']['email'] = 'test@example.com'; $config['website']['theme'] = 'mmcFE'; $config['website']['mobile'] = true; $config['website']['mobile_theme'] = 'mobile'; +$config['website']['api']['disabled'] = false; /** * Account specific settings diff --git a/public/include/pages/api.inc.php b/public/include/pages/api.inc.php index c2e64a60..c85ada42 100644 --- a/public/include/pages/api.inc.php +++ b/public/include/pages/api.inc.php @@ -1,8 +1,10 @@ isActive(); // Check for valid API key $id = $user->checkApiKey($_REQUEST['api_key']); diff --git a/public/include/pages/api/getblockcount.inc.php b/public/include/pages/api/getblockcount.inc.php index 2cbd06a5..6764f436 100644 --- a/public/include/pages/api/getblockcount.inc.php +++ b/public/include/pages/api/getblockcount.inc.php @@ -1,8 +1,10 @@ isActive(); // Check user token $id = $user->checkApiKey($_REQUEST['api_key']); diff --git a/public/include/pages/api/getblocksfound.inc.php b/public/include/pages/api/getblocksfound.inc.php index 00883dad..b238bdc3 100644 --- a/public/include/pages/api/getblocksfound.inc.php +++ b/public/include/pages/api/getblocksfound.inc.php @@ -1,8 +1,10 @@ isActive(); // Check user token $id = $user->checkApiKey($_REQUEST['api_key']); diff --git a/public/include/pages/api/getcurrentworkers.inc.php b/public/include/pages/api/getcurrentworkers.inc.php index 4e26cc1c..48864316 100644 --- a/public/include/pages/api/getcurrentworkers.inc.php +++ b/public/include/pages/api/getcurrentworkers.inc.php @@ -1,8 +1,10 @@ isActive(); // Check user token $id = $user->checkApiKey($_REQUEST['api_key']); diff --git a/public/include/pages/api/getdifficulty.inc.php b/public/include/pages/api/getdifficulty.inc.php index 0e9cb6f9..e84d4045 100644 --- a/public/include/pages/api/getdifficulty.inc.php +++ b/public/include/pages/api/getdifficulty.inc.php @@ -1,8 +1,10 @@ isActive(); // Check user token $id = $user->checkApiKey($_REQUEST['api_key']); diff --git a/public/include/pages/api/getestimatedtime.inc.php b/public/include/pages/api/getestimatedtime.inc.php index a48393fa..f06e28f3 100644 --- a/public/include/pages/api/getestimatedtime.inc.php +++ b/public/include/pages/api/getestimatedtime.inc.php @@ -1,8 +1,10 @@ isActive(); // Check user token $id = $user->checkApiKey($_REQUEST['api_key']); diff --git a/public/include/pages/api/getpoolhashrate.inc.php b/public/include/pages/api/getpoolhashrate.inc.php index 6f6763ec..5546d321 100644 --- a/public/include/pages/api/getpoolhashrate.inc.php +++ b/public/include/pages/api/getpoolhashrate.inc.php @@ -1,8 +1,10 @@ isActive(); // Check user token $id = $user->checkApiKey($_REQUEST['api_key']); diff --git a/public/include/pages/api/getpoolsharerate.inc.php b/public/include/pages/api/getpoolsharerate.inc.php index 8e9117f1..a87859f6 100644 --- a/public/include/pages/api/getpoolsharerate.inc.php +++ b/public/include/pages/api/getpoolsharerate.inc.php @@ -1,8 +1,10 @@ isActive(); // Check user token $id = $user->checkApiKey($_REQUEST['api_key']); diff --git a/public/include/pages/api/getpoolstatus.inc.php b/public/include/pages/api/getpoolstatus.inc.php index ece9f557..4cf0d5ef 100644 --- a/public/include/pages/api/getpoolstatus.inc.php +++ b/public/include/pages/api/getpoolstatus.inc.php @@ -3,6 +3,9 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); +// Check if the API is activated +$api->isActive(); + // Check user token $user_id = $user->checkApiKey($_REQUEST['api_key']); diff --git a/public/include/pages/api/gettimesincelastblock.inc.php b/public/include/pages/api/gettimesincelastblock.inc.php index 532da6bd..14575364 100644 --- a/public/include/pages/api/gettimesincelastblock.inc.php +++ b/public/include/pages/api/gettimesincelastblock.inc.php @@ -1,8 +1,10 @@ isActive(); // Check user token $id = $user->checkApiKey($_REQUEST['api_key']); diff --git a/public/include/pages/api/getuserstatus.inc.php b/public/include/pages/api/getuserstatus.inc.php index ac8f6654..afdd59fc 100644 --- a/public/include/pages/api/getuserstatus.inc.php +++ b/public/include/pages/api/getuserstatus.inc.php @@ -1,8 +1,10 @@ isActive(); // Check user token $user_id = $user->checkApiKey($_REQUEST['api_key']); diff --git a/public/include/pages/api/getuserworkers.inc.php b/public/include/pages/api/getuserworkers.inc.php index 9aaca562..06eb3411 100644 --- a/public/include/pages/api/getuserworkers.inc.php +++ b/public/include/pages/api/getuserworkers.inc.php @@ -1,8 +1,10 @@ isActive(); // Check user token $user_id = $user->checkApiKey($_REQUEST['api_key']); diff --git a/public/include/pages/api/public.inc.php b/public/include/pages/api/public.inc.php index 162e9134..5a98367a 100644 --- a/public/include/pages/api/public.inc.php +++ b/public/include/pages/api/public.inc.php @@ -1,10 +1,10 @@ isActive(); // Fetch last block information $aLastBlock = $block->getLast(); From 8393053641b4a557748d33d2e1bebd08cf7c887a Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 18 Jul 2013 09:04:59 +0200 Subject: [PATCH 124/137] Disabel API references if API disabled This will turn off displaying any API references and links when API was turned off in the configuration. Fixes #467 --- public/include/smarty_globals.inc.php | 2 +- public/templates/mmcFE/account/edit/default.tpl | 2 +- public/templates/mmcFE/global/navigation.tpl | 2 +- public/templates/mmcFE/statistics/default.tpl | 2 +- public/templates/mmcFE/statistics/pool/authenticated.tpl | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index 5f6ca959..fe1868d6 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -47,7 +47,7 @@ $aGlobal = array( 'blockexplorer' => $config['blockexplorer'], 'chaininfo' => $config['chaininfo'], 'config' => array( - 'website' => array( 'title' => $config['website']['title'], 'acl' => $config['website']['acl'] ), + 'website' => $config['website'], 'accounts' => $config['accounts'], 'disable_invitations' => $setting->getValue('disable_invitations'), 'price' => array( 'currency' => $config['price']['currency'] ), diff --git a/public/templates/mmcFE/account/edit/default.tpl b/public/templates/mmcFE/account/edit/default.tpl index fcf29c7c..be3ed355 100644 --- a/public/templates/mmcFE/account/edit/default.tpl +++ b/public/templates/mmcFE/account/edit/default.tpl @@ -6,7 +6,7 @@ - + {if !$GLOBAL.config.website.api.disabled}{/if} diff --git a/public/templates/mmcFE/global/navigation.tpl b/public/templates/mmcFE/global/navigation.tpl index 79d36af8..14a002ef 100644 --- a/public/templates/mmcFE/global/navigation.tpl +++ b/public/templates/mmcFE/global/navigation.tpl @@ -47,7 +47,7 @@
  • About
  • diff --git a/public/templates/mmcFE/statistics/default.tpl b/public/templates/mmcFE/statistics/default.tpl index a8e9a4c2..59603d94 100644 --- a/public/templates/mmcFE/statistics/default.tpl +++ b/public/templates/mmcFE/statistics/default.tpl @@ -19,5 +19,5 @@
    Username: {$GLOBAL.userdata.username|escape}
    User Id: {$GLOBAL.userdata.id}
    API Key: {$GLOBAL.userdata.api_key}
    API Key: {$GLOBAL.userdata.api_key}
    E-Mail:
    Payment Address:
    Donation %: [donation amount in percent (example: 0.5)]
    -
  • These stats are also available in JSON format HERE
  • +{if !$GLOBAL.config.website.api.disabled}
  • These stats are also available in JSON format HERE
  • {/if} {include file="global/block_footer.tpl"} diff --git a/public/templates/mmcFE/statistics/pool/authenticated.tpl b/public/templates/mmcFE/statistics/pool/authenticated.tpl index 9e41b6d3..8d35df1a 100644 --- a/public/templates/mmcFE/statistics/pool/authenticated.tpl +++ b/public/templates/mmcFE/statistics/pool/authenticated.tpl @@ -56,7 +56,7 @@ -
  • These stats are also available in JSON format HERE
  • +{if !$GLOBAL.config.website.api.disabled}
  • These stats are also available in JSON format HERE
  • {/if} {include file="global/block_footer.tpl"} From 72ed08c92cb7a7fd2b122409ebba0bb13bff7a0c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 18 Jul 2013 09:30:44 +0200 Subject: [PATCH 125/137] Adding tooltip help for anonymous account flag Fixes #459 --- public/templates/mmcFE/account/edit/default.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/templates/mmcFE/account/edit/default.tpl b/public/templates/mmcFE/account/edit/default.tpl index fcf29c7c..98452cb6 100644 --- a/public/templates/mmcFE/account/edit/default.tpl +++ b/public/templates/mmcFE/account/edit/default.tpl @@ -11,7 +11,7 @@ Payment Address: Donation %: [donation amount in percent (example: 0.5)] Automatic Payout Threshold: [{$GLOBAL.config.ap_threshold.min}-{$GLOBAL.config.ap_threshold.max} {$GLOBAL.config.currency}. Set to '0' for no auto payout] - Anonymous Account: + Anonymous Account : From 73ec70738186cf7b1e0ef62972a408b8785ceb72 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 18 Jul 2013 09:43:16 +0200 Subject: [PATCH 126/137] Adds blockhash to blockexplorer URL * Changed blockexplorer URL to use Blockhash instead of height * Added calls to find current networks blocks blockhash * Propagated changes onto temmplate * Added new dist configuration for new blockexplorer URL Fixes #446 --- public/include/config/global.inc.dist.php | 7 ++++--- public/include/pages/statistics/pool.inc.php | 10 +++++++++- .../templates/mmcFE/statistics/pool/authenticated.tpl | 4 ++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index e66197fe..22fe69cd 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -243,9 +243,10 @@ $config['payout_system'] = 'prop'; $config['archive']['maxrounds'] = 10; $config['archive']['maxage'] = 60 * 24; -// URL prefix for block searches, used for block links, default: `http://explorer.litecoin.net/search?q=` -// If empty, the block link to the block information page will be removed -$config['blockexplorer'] = 'http://explorer.litecoin.net/search?q='; +// URL prefix for block searches, used for block links, default: `http://explorer.litecoin.net/block/` +// The Blockhash is appended on the templates to this URL +// If this config is empty, the block link to the block information page will be removed +$config['blockexplorer'] = 'http://explorer.litecoin.net/block/'; // Link to blockchain information, used for difficulty link, default: `http://allchains.info` // If empty, the difficulty link to the chain information will be removed diff --git a/public/include/pages/statistics/pool.inc.php b/public/include/pages/statistics/pool.inc.php index 256d4edf..9aafadcf 100644 --- a/public/include/pages/statistics/pool.inc.php +++ b/public/include/pages/statistics/pool.inc.php @@ -11,6 +11,8 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) $dDifficulty = $dDifficulty['proof-of-work']; $iBlock = $bitcoin->getblockcount(); + $sBlockHash = $bitcoin->query('getblockhash', $iBlock); + var_dump($sBlockHash); } else { $dDifficulty = 1; $iBlock = 0; @@ -51,7 +53,13 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { $smarty->assign("CONTRIBSHARES", $aContributorsShares); $smarty->assign("CONTRIBHASHES", $aContributorsHashes); $smarty->assign("CURRENTBLOCK", $iBlock); - count($aBlockData) > 0 ? $smarty->assign("LASTBLOCK", $aBlockData['height']) : $smarty->assign("LASTBLOCK", 0); + $smarty->assign("CURRENTBLOCKHASH", $sBlockHash); + if (count($aBlockData) > 0) { + $smarty->assign("LASTBLOCK", $aBlockData['height']); + $smarty->assign("LASTBLOCKHASH", $aBlockData['blockhash']); + } else { + $smarty->assign("LASTBLOCK", 0); + } $smarty->assign("DIFFICULTY", $dDifficulty); $smarty->assign("REWARD", $config['reward']); } else { diff --git a/public/templates/mmcFE/statistics/pool/authenticated.tpl b/public/templates/mmcFE/statistics/pool/authenticated.tpl index 9e41b6d3..7834b9b5 100644 --- a/public/templates/mmcFE/statistics/pool/authenticated.tpl +++ b/public/templates/mmcFE/statistics/pool/authenticated.tpl @@ -22,7 +22,7 @@ {if $GLOBAL.blockexplorer} Next Network Block - {$CURRENTBLOCK + 1}    (Current: {$CURRENTBLOCK}) + {$CURRENTBLOCK + 1}    (Current: {$CURRENTBLOCK}) {else} @@ -32,7 +32,7 @@ {/if} Last Block Found - {if $GLOBAL.blockexplorer}{$LASTBLOCK|default:"0"}{else}{$LASTBLOCK|default:"0"}{/if} + {if $GLOBAL.blockexplorer}{$LASTBLOCK|default:"0"}{else}{$LASTBLOCK|default:"0"}{/if} Current Difficulty From 6d379e8e58bccc199c78042a1c21aa1ad653c231 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 18 Jul 2013 09:45:20 +0200 Subject: [PATCH 127/137] Removing debug output --- public/include/pages/statistics/pool.inc.php | 1 - 1 file changed, 1 deletion(-) diff --git a/public/include/pages/statistics/pool.inc.php b/public/include/pages/statistics/pool.inc.php index 9aafadcf..b8872293 100644 --- a/public/include/pages/statistics/pool.inc.php +++ b/public/include/pages/statistics/pool.inc.php @@ -12,7 +12,6 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { $dDifficulty = $dDifficulty['proof-of-work']; $iBlock = $bitcoin->getblockcount(); $sBlockHash = $bitcoin->query('getblockhash', $iBlock); - var_dump($sBlockHash); } else { $dDifficulty = 1; $iBlock = 0; From b57f6b59a1f69bf4338d82a395b438d342b08070 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 18 Jul 2013 10:49:16 +0200 Subject: [PATCH 128/137] Disable API on mobile theme if disabled --- public/templates/mobile/statistics/default.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/templates/mobile/statistics/default.tpl b/public/templates/mobile/statistics/default.tpl index 994e60a3..7b03653d 100644 --- a/public/templates/mobile/statistics/default.tpl +++ b/public/templates/mobile/statistics/default.tpl @@ -18,4 +18,4 @@ -
  • These stats are also available in JSON format HERE
  • +{if !$GLOBAL.config.website.api.disabled}
  • These stats are also available in JSON format HERE
  • {/if} From a655abddbf378761cdcbd97b9c4feb21efe85874 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 18 Jul 2013 11:05:09 +0200 Subject: [PATCH 129/137] fixing mobile template --- public/templates/mobile/statistics/pool/authenticated.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/templates/mobile/statistics/pool/authenticated.tpl b/public/templates/mobile/statistics/pool/authenticated.tpl index 0889347c..a7025779 100644 --- a/public/templates/mobile/statistics/pool/authenticated.tpl +++ b/public/templates/mobile/statistics/pool/authenticated.tpl @@ -27,7 +27,7 @@ {if $GLOBAL.blockexplorer} Next Network Block - {$CURRENTBLOCK + 1}    (Current: {$CURRENTBLOCK}) + {$CURRENTBLOCK + 1}    (Current: {$CURRENTBLOCK}) {else} @@ -37,7 +37,7 @@ {/if} Last Block Found - {if $GLOBAL.blockexplorer}{$LASTBLOCK|default:"0"}{else}{$LASTBLOCK|default:"0"}{/if} + {if $GLOBAL.blockexplorer}{$LASTBLOCK|default:"0"}{else}{$LASTBLOCK|default:"0"}{/if} {if $GLOBAL.chaininfo} From 7cf3ff3d883b56cca22c59079588eba2a61b576f Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 18 Jul 2013 13:55:05 +0200 Subject: [PATCH 130/137] Fix anonymous detection in share contributors list Fixes #484 --- .../mmcFE/statistics/pool/contributors_shares.tpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/templates/mmcFE/statistics/pool/contributors_shares.tpl b/public/templates/mmcFE/statistics/pool/contributors_shares.tpl index 3efb4a8e..6dd36eb8 100644 --- a/public/templates/mmcFE/statistics/pool/contributors_shares.tpl +++ b/public/templates/mmcFE/statistics/pool/contributors_shares.tpl @@ -11,11 +11,11 @@ {assign var=rank value=1} {assign var=listed value=0} -{section hashrate $CONTRIBSHARES} - +{section shares $CONTRIBSHARES} + {$rank++} - {if $CONTRIBHASHES[hashrate].is_anonymous|default:"0" == 1}anonymous{else}{$CONTRIBSHARES[hashrate].account|escape}{/if} - {$CONTRIBSHARES[hashrate].shares|number_format} + {if $CONTRIBSHARES[shares].is_anonymous|default:"0" == 1}anonymous{else}{$CONTRIBSHARES[shares].account|escape}{/if} + {$CONTRIBSHARES[shares].shares|number_format} {/section} {if $listed != 1 && $GLOBAL.userdata.username|default:""} From 3d20d041d7d3aefe75f4a0d75e1704889b2b393b Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 18 Jul 2013 13:58:36 +0200 Subject: [PATCH 131/137] Update POOLS.md Added new pool --- POOLS.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/POOLS.md b/POOLS.md index 88b986b4..9268a9d4 100644 --- a/POOLS.md +++ b/POOLS.md @@ -27,7 +27,7 @@ They have succesfully mined blocks on each of those pools listed. All pools are running on Stratum only. | Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes | -| -------- | ---- | ------------- | ----------------- | ----- | +| -------- | ---- | ------------- | -----------------: | ----- | | http://wdc.nordicminers.eu | Worldcoin | n/a | n/a | | | http://lky.nordicminers.eu | Luckycoin | n/a | n/a | | | http://fst.nordicminers.eu | Fastcoin | n/a | n/a | | @@ -48,7 +48,7 @@ running more or less without any issues (related to `mmcfe-ng` that is ;-)). He the most powerful pool! | Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes | -| -------- | ---- | ------------- | ----------------- | ----- | +| -------- | ---- | ------------- | ------------------: | ----- | | http://www.ejpool.info | Litecoin | 155 MHash | 120 | | ### Obigal @@ -65,6 +65,12 @@ Small Time Miners are running various stratum only pools for different coins. ### Feeleep75 | Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes | -| -------- | ---- | ------------- | ------------------- | ----- | +| -------- | ---- | ------------- | ------------------: | ----- | | http://bot.coinmine.pl | Bottlecaps | 3 - 50 MHash | n/a | PoS/PoW type coin | | http://yacp.coinmine.pl | YaCoin | 19 MHash | n/a | | + +### LiteSaber + +| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes | +| -------- | ---- | ------------- | ------------------: | ----- | +| http://coinhuntr.com | Litecoin | 200 MHash | 250 | Custom Frontend template | From 6641cf7f5c137d315688786a563f01f64d04a6ce Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 18 Jul 2013 15:41:52 +0200 Subject: [PATCH 132/137] Adding notifications and start/end times Fixes #486 --- cronjobs/cron_end.inc.php | 2 +- cronjobs/notifications.php | 2 ++ cronjobs/shared.inc.php | 1 + public/include/pages/admin/monitoring.inc.php | 35 ++++++++++++++----- .../mmcFE/admin/monitoring/default.tpl | 3 +- 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/cronjobs/cron_end.inc.php b/cronjobs/cron_end.inc.php index 56607c45..03a548d2 100644 --- a/cronjobs/cron_end.inc.php +++ b/cronjobs/cron_end.inc.php @@ -22,7 +22,7 @@ limitations under the License. $monitoring->setStatus($cron_name . "_message", "message", "OK"); $monitoring->setStatus($cron_name . "_status", "okerror", 0); $monitoring->setStatus($cron_name . "_runtime", "time", microtime(true) - $cron_start[$cron_name]); -$monitoring->setStatus($cron_name . "_lastrun", "date", time()); +$monitoring->setStatus($cron_name . "_endtime", "date", time()); // Mark cron as running for monitoring $monitoring->setStatus($cron_name . '_active', "yesno", 0); ?> diff --git a/cronjobs/notifications.php b/cronjobs/notifications.php index a60e4657..effcf002 100755 --- a/cronjobs/notifications.php +++ b/cronjobs/notifications.php @@ -65,4 +65,6 @@ if (!empty($aNotifications)) { } else { $log->logDebug(" no active IDLE worker notifications\n"); } + +require_once('cron_end.inc.php'); ?> diff --git a/cronjobs/shared.inc.php b/cronjobs/shared.inc.php index 33113c00..f66be4f4 100644 --- a/cronjobs/shared.inc.php +++ b/cronjobs/shared.inc.php @@ -48,4 +48,5 @@ $cron_start[$cron_name] = microtime(true); // Mark cron as running for monitoring $log->logDebug('Marking cronjob as running for monitoring'); $monitoring->setStatus($cron_name . '_active', 'yesno', 1); +$monitoring->setStatus($cron_name . '_starttime', 'date', time()); ?> diff --git a/public/include/pages/admin/monitoring.inc.php b/public/include/pages/admin/monitoring.inc.php index 0d7dd671..a82dd78a 100644 --- a/public/include/pages/admin/monitoring.inc.php +++ b/public/include/pages/admin/monitoring.inc.php @@ -15,42 +15,56 @@ $aCronStatus = array( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('statistics_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('statistics_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('statistics_runtime') ), - array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('statistics_lastrun') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('statistics_starttime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('statistics_endtime') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('statistics_message') ), ), 'auto_payout' => array ( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('auto_payout_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('auto_payout_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('auto_payout_runtime') ), - array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('auto_payout_lastrun') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('auto_payout_starttime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('auto_payout_endtime') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('auto_payout_message') ), ), 'archive_cleanup' => array ( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('archive_cleanup_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('archive_cleanup_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('archive_cleanup_runtime') ), - array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('archive_cleanup_lastrun') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('archive_cleanup_starttime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('archive_cleanup_endtime') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('archive_cleanup_message') ), ), 'blockupdate' => array ( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('blockupdate_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('blockupdate_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('blockupdate_runtime') ), - array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('blockupdate_lastrun') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('blockupdate_starttime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('blockupdate_endtime') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('blockupdate_message') ), ), 'findblock' => array ( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('findblock_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('findblock_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('findblock_runtime') ), - array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('findblock_lastrun') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('findblock_starttime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('findblock_endtime') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('findblock_message') ), ), + 'notifications' => array ( + array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('notifications_status') ), + array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('notifications_active') ), + array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('notifications_runtime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('notifications_starttime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('notifications_endtime') ), + array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('notifications_message') ), + ), 'tickerupdate' => array ( array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('tickerupdate_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('tickerupdate_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('tickerupdate_runtime') ), - array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('tickerupdate_lastrun') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('tickerupdate_starttime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('tickerupdate_endtime') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('tickerupdate_message') ), ) ); @@ -61,7 +75,8 @@ case 'pplns': array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('pplns_payout_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('pplns_payout_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('pplns_payout_runtime') ), - array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('pplns_payout_lastrun') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('pplns_payout_starttime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('pplns_payout_endtime') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('pplns_payout_message') ), ); break; @@ -70,7 +85,8 @@ case 'pps': array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('pps_payout_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('pps_payout_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('pps_payout_runtime') ), - array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('pps_payout_lastrun') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('pps_payout_starttime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('pps_payout_endtime') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('pps_payout_message') ), ); break; @@ -79,7 +95,8 @@ case 'prop': array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('proportional_payout_status') ), array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('proportional_payout_active') ), array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('proportional_payout_runtime') ), - array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('proportional_payout_lastrun') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('proportional_payout_starttime') ), + array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('proportional_payout_endtime') ), array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('proportional_payout_message') ), ); break; diff --git a/public/templates/mmcFE/admin/monitoring/default.tpl b/public/templates/mmcFE/admin/monitoring/default.tpl index 856766ac..f7b099b1 100644 --- a/public/templates/mmcFE/admin/monitoring/default.tpl +++ b/public/templates/mmcFE/admin/monitoring/default.tpl @@ -5,7 +5,8 @@ Exit Code Active Runtime - Last Run + Start Time + End Time Message From 440ca027a2fba59347929c5ccd11af3cb3840d46 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 21 Jul 2013 08:12:40 +0200 Subject: [PATCH 133/137] Fixing PPLNS target calculation on blockavg This will fix #492 with PPLNS targets not taking the blocks in proper order. --- public/include/classes/block.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/classes/block.class.php b/public/include/classes/block.class.php index 711ed0b5..c08f89d6 100644 --- a/public/include/classes/block.class.php +++ b/public/include/classes/block.class.php @@ -109,7 +109,7 @@ class Block { * @return data float Float value of average shares **/ public function getAvgBlockShares($limit=10) { - $stmt = $this->mysqli->prepare("SELECT AVG(shares) AS average FROM $this->table LIMIT ?"); + $stmt = $this->mysqli->prepare("SELECT AVG(shares) AS average FROM $this->table ORDER BY height DESC LIMIT ?"); if ($this->checkStmt($stmt) && $stmt->bind_param('i', $limit) && $stmt->execute() && $result = $stmt->get_result()) return (float)$result->fetch_object()->average; return false; From 7d801a561c8daf5cb706250c3c4cdc8a0ce6cb26 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 21 Jul 2013 08:20:53 +0200 Subject: [PATCH 134/137] Fixing Orphan showing as unconfirmed * Fixes orphaned transactions showing as unconfirmed too * Fixes transaction tables to show orphaned credits in green, not red Fixes #490 --- public/include/classes/transaction.class.php | 4 ++-- public/templates/mmcFE/account/transactions/default.tpl | 2 +- public/templates/mmcFE/admin/transactions/default.tpl | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index 30867cc6..229c8581 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -235,7 +235,7 @@ class Transaction { FROM $this->table AS t LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id WHERE - t.type IN ('Credit','Bonus') AND b.confirmations < ? + t.type IN ('Credit','Bonus') AND b.confirmations < ? AND b.confirmations > 0 AND t.account_id = ? ) AS t4, ( @@ -244,7 +244,7 @@ class Transaction { LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id WHERE ( - t.type IN ('Donation','Fee') AND b.confirmations < ? + t.type IN ('Donation','Fee') AND b.confirmations < ? AND b.confirmations > 0 ) AND t.account_id = ? ) AS t5, diff --git a/public/templates/mmcFE/account/transactions/default.tpl b/public/templates/mmcFE/account/transactions/default.tpl index 4bbe7b4b..52602088 100644 --- a/public/templates/mmcFE/account/transactions/default.tpl +++ b/public/templates/mmcFE/account/transactions/default.tpl @@ -116,7 +116,7 @@ {$TRANSACTIONS[transaction].type} {$TRANSACTIONS[transaction].coin_address} {if $TRANSACTIONS[transaction].height == 0}n/a{else}{$TRANSACTIONS[transaction].height}{/if} - {$TRANSACTIONS[transaction].amount|number_format:"8"} + {$TRANSACTIONS[transaction].amount|number_format:"8"} {if $TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus'} {assign var="orphan_credits" value="`$orphan_credits|default:"0"+$TRANSACTIONS[transaction].amount`"} diff --git a/public/templates/mmcFE/admin/transactions/default.tpl b/public/templates/mmcFE/admin/transactions/default.tpl index aba15f12..ecf0dc70 100644 --- a/public/templates/mmcFE/admin/transactions/default.tpl +++ b/public/templates/mmcFE/admin/transactions/default.tpl @@ -117,7 +117,7 @@ {$TRANSACTIONS[transaction].type} {$TRANSACTIONS[transaction].coin_address} {if $TRANSACTIONS[transaction].height == 0}n/a{else}{$TRANSACTIONS[transaction].height}{/if} - {$TRANSACTIONS[transaction].amount|number_format:"8"} + {$TRANSACTIONS[transaction].amount|number_format:"8"} {/if} {/section} From c94c1be7be64d4d2416741361f9d95810f63475a Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 21 Jul 2013 08:35:57 +0200 Subject: [PATCH 135/137] Using proper SQL query by @CaptainAK Proposed fix did not work, using proper Query now. Thank @CaptainAK for the fix! Fixes #492 --- public/include/classes/block.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/include/classes/block.class.php b/public/include/classes/block.class.php index c08f89d6..8ce985d1 100644 --- a/public/include/classes/block.class.php +++ b/public/include/classes/block.class.php @@ -108,8 +108,8 @@ class Block { * @param limit int Maximum blocks to check * @return data float Float value of average shares **/ - public function getAvgBlockShares($limit=10) { - $stmt = $this->mysqli->prepare("SELECT AVG(shares) AS average FROM $this->table ORDER BY height DESC LIMIT ?"); + public function getAvgBlockShares($limit=1) { + $stmt = $this->mysqli->prepare("SELECT AVG(x.shares) AS average FROM (SELECT shares FROM $this->table ORDER BY height DESC LIMIT ?) AS x"); if ($this->checkStmt($stmt) && $stmt->bind_param('i', $limit) && $stmt->execute() && $result = $stmt->get_result()) return (float)$result->fetch_object()->average; return false; From 36a74b0bbf206b7b0f5c9bfed48192ecf3934a79 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 21 Jul 2013 17:25:09 +0200 Subject: [PATCH 136/137] Fix transaction table, adjust transaction class * Ensure we also check newly added blocks for unconfirmed tx * Only list orphaned transactions in the orphaned tab Fixes #490 --- public/include/classes/transaction.class.php | 4 ++-- public/templates/mmcFE/account/transactions/default.tpl | 2 +- public/templates/mmcFE/admin/transactions/default.tpl | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index 229c8581..2cbe4312 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -235,7 +235,7 @@ class Transaction { FROM $this->table AS t LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id WHERE - t.type IN ('Credit','Bonus') AND b.confirmations < ? AND b.confirmations > 0 + t.type IN ('Credit','Bonus') AND b.confirmations < ? AND b.confirmations >= 0 AND t.account_id = ? ) AS t4, ( @@ -244,7 +244,7 @@ class Transaction { LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id WHERE ( - t.type IN ('Donation','Fee') AND b.confirmations < ? AND b.confirmations > 0 + t.type IN ('Donation','Fee') AND b.confirmations < ? AND b.confirmations >= 0 ) AND t.account_id = ? ) AS t5, diff --git a/public/templates/mmcFE/account/transactions/default.tpl b/public/templates/mmcFE/account/transactions/default.tpl index 52602088..a7337bd6 100644 --- a/public/templates/mmcFE/account/transactions/default.tpl +++ b/public/templates/mmcFE/account/transactions/default.tpl @@ -62,7 +62,7 @@ {assign var=has_unconfirmed value=false} {section transaction $TRANSACTIONS} {if - (($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus' or $TRANSACTIONS[transaction].type == 'Donation' or $TRANSACTIONS[transaction].type == 'Fee') and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations) + (($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus' or $TRANSACTIONS[transaction].type == 'Donation' or $TRANSACTIONS[transaction].type == 'Fee') and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations and $TRANSACTIONS[transaction].confirmations >= 0) } {assign var=has_unconfirmed value=true} diff --git a/public/templates/mmcFE/admin/transactions/default.tpl b/public/templates/mmcFE/admin/transactions/default.tpl index ecf0dc70..c493de8b 100644 --- a/public/templates/mmcFE/admin/transactions/default.tpl +++ b/public/templates/mmcFE/admin/transactions/default.tpl @@ -68,7 +68,7 @@ {assign var=unconfirmed value=0} {section transaction $TRANSACTIONS} - {if ($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus' or $TRANSACTIONS[transaction].type == 'Donation' or $TRANSACTIONS[transaction].type == 'Fee') and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations} + {if ($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus' or $TRANSACTIONS[transaction].type == 'Donation' or $TRANSACTIONS[transaction].type == 'Fee') and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations and $TRANSACTIONS[transaction].confirmations >= 0} {assign var=unconfirmed value=1} {$TRANSACTIONS[transaction].id} From bb313bee3605b321e77a2153d15c265e293cbe07 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 21 Jul 2013 17:31:32 +0200 Subject: [PATCH 137/137] Update README.md Adding smarty template reference --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a7787bb2..95a26d3f 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ Features The following feature have been implemented so far: +* Fully re-written GUI with [Smarty][2] templates * Mobile WebUI * Reward Systems * Propotional @@ -142,3 +143,4 @@ limitations under the License. [1]: https://github.com/TheSerapher/php-mmcfe-ng/issues "Issue" + [2]: http://www.smarty.net/docs/en/ "Smarty"