From 07d49f83d3372ec60ab0176b644b76f1dacb4f3b Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 3 Jun 2013 15:03:04 +0200 Subject: [PATCH 001/168] Changing hash- and share rate calculation SQL * Do not include shares_archive table * Main reason: This table is optional * Secondary reason: Speeds up the query from 1.3s to 0.005s * Drawback: Once a block is found it takes 10 minutes for the stats to be accuracte again This could potentially be reverted but since shares_archive is optional and the speed increase is rather significant I'd like to keep it this way. --- public/include/classes/statistics.class.php | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index 8b12391e..b8fa232c 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -90,12 +90,8 @@ class Statistics { $this->debug->append("STA " . __METHOD__, 4); if ($data = $this->memcache->get(__FUNCTION__)) return $data; $stmt = $this->mysqli->prepare(" - SELECT SUM(hashrate) AS hashrate FROM - ( - SELECT ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ")/600/1000) AS hashrate FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) - UNION - SELECT ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ")/600/1000) AS hashrate FROM " . $this->share->getArchiveTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) - ) AS sum"); + SELECT ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ")/600/1000) AS hashrate FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) + "); // Catchall if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->hashrate); $this->debug->append("Failed to get hashrate: " . $this->mysqli->error); @@ -111,12 +107,8 @@ class Statistics { $this->debug->append("STA " . __METHOD__, 4); if ($data = $this->memcache->get(__FUNCTION__)) return $data; $stmt = $this->mysqli->prepare(" - SELECT ROUND(SUM(sharerate) / 600, 2) AS sharerate FROM - ( - SELECT COUNT(id) AS sharerate FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) - UNION ALL - SELECT COUNT(id) AS sharerate FROM " . $this->share->getArchiveTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) - ) AS sum"); + SELECT ROUND(COUNT(id) / 600, 2) AS sharerate FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) + "); if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->sharerate); // Catchall $this->debug->append("Failed to fetch share rate: " . $this->mysqli->error); From da1d7daaa2a8dcd4f25b539c1ed99c74c350f2e7 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 3 Jun 2013 20:54:58 +0200 Subject: [PATCH 002/168] Removed warning from worker list * Workers can safely be removed even during a round --- public/templates/mmcFE/account/workers/default.tpl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/public/templates/mmcFE/account/workers/default.tpl b/public/templates/mmcFE/account/workers/default.tpl index c09cdde3..90637260 100644 --- a/public/templates/mmcFE/account/workers/default.tpl +++ b/public/templates/mmcFE/account/workers/default.tpl @@ -1,9 +1,4 @@ {include file="global/block_header.tpl" BLOCK_HEADER="My Workers"} - -
From bdcc3f747c7e2317d18b86d5bc40f3c7356d1093 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 4 Jun 2013 09:08:30 +0200 Subject: [PATCH 003/168] Addressing #96, missing difficulty on stats page * Properly assign template variable * Removed unneeded additional template --- public/include/pages/statistics.inc.php | 8 +++---- public/include/pages/statistics/pool.inc.php | 2 +- .../mmcFE/statistics/pool/default.tpl | 22 ------------------- 3 files changed, 5 insertions(+), 27 deletions(-) delete mode 100644 public/templates/mmcFE/statistics/pool/default.tpl diff --git a/public/include/pages/statistics.inc.php b/public/include/pages/statistics.inc.php index c465f091..195e3545 100644 --- a/public/include/pages/statistics.inc.php +++ b/public/include/pages/statistics.inc.php @@ -5,14 +5,14 @@ if (!defined('SECURITY')) die('Hacking attempt'); if ($bitcoin->can_connect() === true){ - $iDifficulty = $bitcoin->query('getdifficulty'); + $dDifficulty = $bitcoin->query('getdifficulty'); $iBlock = $bitcoin->query('getblockcount'); } else { - $iDifficulty = 1; + $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("CURRENTDIFFICULTY", $iDifficulty); -$smarty->assign("CONTENT", "pool/default.tpl"); +$smarty->assign("DIFFICULTY", $dDifficulty); +$smarty->assign("CONTENT", "default.tpl"); diff --git a/public/include/pages/statistics/pool.inc.php b/public/include/pages/statistics/pool.inc.php index 0c9c2eb7..ede0933a 100644 --- a/public/include/pages/statistics/pool.inc.php +++ b/public/include/pages/statistics/pool.inc.php @@ -53,6 +53,6 @@ $smarty->assign("REWARD", $config['reward']); if ($_SESSION['AUTHENTICATED']) { $smarty->assign("CONTENT", "authenticated.tpl"); } else { - $smarty->assign("CONTENT", "default.tpl"); + $smarty->assign("CONTENT", "../default.tpl"); } ?> diff --git a/public/templates/mmcFE/statistics/pool/default.tpl b/public/templates/mmcFE/statistics/pool/default.tpl deleted file mode 100644 index d1dfd202..00000000 --- a/public/templates/mmcFE/statistics/pool/default.tpl +++ /dev/null @@ -1,22 +0,0 @@ -{include file="global/block_header.tpl" BLOCK_HEADER="Pool Statistics"} - - - - - - - - - - - - - - - - - - - -
Pool Hash Rate{$GLOBAL.hashrate / 1000} Mhash/s
Current Total Miners{$GLOBAL.workers}
Current Block{$CURRENTBLOCK}
Current Difficulty{$DIFFICULTY}
-{include file="global/block_footer.tpl"} From ed0853202b7eb8073be8963e807adfe97e717070 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 28 May 2013 15:02:43 +0200 Subject: [PATCH 004/168] Initial file import for admin panel * Added isAdmin to user class * Run isAdmin on EACH page to ensure admin status hasn't changed * Added main page with no content * Added user query page with basic form, no content --- public/include/classes/user.class.php | 18 ++++++++++-------- public/include/pages/admin.inc.php | 15 +++++++++++++++ public/include/pages/admin/user.inc.php | 19 +++++++++++++++++++ public/templates/mmcFE/admin/default.tpl | 3 +++ public/templates/mmcFE/admin/user/default.tpl | 8 ++++++++ public/templates/mmcFE/global/navigation.tpl | 6 ++++++ 6 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 public/include/pages/admin.inc.php create mode 100644 public/include/pages/admin/user.inc.php create mode 100644 public/templates/mmcFE/admin/default.tpl create mode 100644 public/templates/mmcFE/admin/user/default.tpl diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index d6582676..daa4a6a0 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -26,26 +26,28 @@ class User { public function getError() { return $this->sError; } - public function getUserName($id) { return $this->getSingle($id, 'username', 'id'); } - public function getUserId($username) { return $this->getSingle($username, 'id', 'username', 's'); } - public function getUserEmail($username) { return $this->getSingle($username, 'email', 'username', 's'); } - + public function getUserAdmin($id) { + return $this->getSingle($id, 'admin', 'id'); + } public function getUserToken($id) { return $this->getSingle($id, 'token', 'id'); } - public function getIdFromToken($token) { return $this->getSingle($token, 'id', 'token', 's'); } + public function isAdmin($id) { + if ($this->getUserAdmin($id) == 1) return true; + return false; + } public function setUserToken($id) { $field = array( @@ -266,15 +268,15 @@ class User { private function checkUserPassword($username, $password) { $this->debug->append("STA " . __METHOD__, 4); $user = array(); - $stmt = $this->mysqli->prepare("SELECT username, id FROM $this->table WHERE username=? AND pass=? LIMIT 1"); + $stmt = $this->mysqli->prepare("SELECT username, id, admin FROM $this->table WHERE username=? AND pass=? LIMIT 1"); if ($this->checkStmt($stmt)) { $stmt->bind_param('ss', $username, hash('sha256', $password.$this->salt)); $stmt->execute(); - $stmt->bind_result($row_username, $row_id); + $stmt->bind_result($row_username, $row_id, $row_admin); $stmt->fetch(); $stmt->close(); // Store the basic login information - $this->user = array('username' => $row_username, 'id' => $row_id); + $this->user = array('username' => $row_username, 'id' => $row_id, 'admin' => $row_admin); return $username === $row_username; } return false; diff --git a/public/include/pages/admin.inc.php b/public/include/pages/admin.inc.php new file mode 100644 index 00000000..4409a769 --- /dev/null +++ b/public/include/pages/admin.inc.php @@ -0,0 +1,15 @@ +isAdmin($_SESSION['USERDATA']['id'])) { + header("HTTP/1.1 404 Page not found"); + die(); +} + +// Tempalte specifics +$smarty->assign("CONTENT", "default.tpl"); +?> diff --git a/public/include/pages/admin/user.inc.php b/public/include/pages/admin/user.inc.php new file mode 100644 index 00000000..6706e547 --- /dev/null +++ b/public/include/pages/admin/user.inc.php @@ -0,0 +1,19 @@ +isAdmin($_SESSION['USERDATA']['id'])) { + header("HTTP/1.1 404 Page not found"); + die(); +} + +if ($_POST['query']) { + // Fetch all users from DB cross referencing all stats +} + +// Tempalte specifics +$smarty->assign("CONTENT", "default.tpl"); +?> diff --git a/public/templates/mmcFE/admin/default.tpl b/public/templates/mmcFE/admin/default.tpl new file mode 100644 index 00000000..60cdf352 --- /dev/null +++ b/public/templates/mmcFE/admin/default.tpl @@ -0,0 +1,3 @@ +{include file="global/block_header.tpl" BLOCK_HEADER="Admin Panel"} +

Welcome to the admin panel. Please select an option from the drop-down menu.

+{include file="global/block_footer.tpl"} diff --git a/public/templates/mmcFE/admin/user/default.tpl b/public/templates/mmcFE/admin/user/default.tpl new file mode 100644 index 00000000..8077c4a7 --- /dev/null +++ b/public/templates/mmcFE/admin/user/default.tpl @@ -0,0 +1,8 @@ +{include file="global/block_header.tpl" BLOCK_HEADER="Query User Database"} + + + + + + +{include file="global/block_footer.tpl"} diff --git a/public/templates/mmcFE/global/navigation.tpl b/public/templates/mmcFE/global/navigation.tpl index 3830006b..828ed68a 100644 --- a/public/templates/mmcFE/global/navigation.tpl +++ b/public/templates/mmcFE/global/navigation.tpl @@ -11,6 +11,12 @@ {/if} {if $smarty.session.AUTHENTICATED|default:"0" == 1 && $GLOBAL.userdata.admin == 1}
  • Admin Panel
  • {/if} +
  • Admin Panel + +
  • + {/if}
  • Statistics
    • Pool Stats
    • From 065f5e6d164d5c14ede0697838dcc7299b723c37 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 4 Jun 2013 11:34:22 +0200 Subject: [PATCH 005/168] Adding working user admin panel to query user DB * Users are all fetched from the DB * Each user is assigned special stats * Hashrate * Shares * Balance * Est. Payout * Est. Donation * Display in sortable and paginated table This is not well optimized. Each user stats are grabbed individually via the stats and transaction classes. It would probably make sense to expand the statistics class to list a full user list with all statistics available instead to cover this in a single SQL query. --- public/include/classes/user.class.php | 12 +++ public/include/pages/admin/user.inc.php | 22 ++++- public/site_assets/mmcFE/css/style.css | 6 ++ public/site_assets/mmcFE/images/first.png | Bin 0 -> 720 bytes public/site_assets/mmcFE/images/last.png | Bin 0 -> 737 bytes public/site_assets/mmcFE/images/next.png | Bin 0 -> 736 bytes public/site_assets/mmcFE/images/prev.png | Bin 0 -> 745 bytes public/site_assets/mmcFE/js/custom.js | 4 + public/templates/mmcFE/admin/user/default.tpl | 79 +++++++++++++++++- public/templates/mmcFE/global/navigation.tpl | 2 +- 10 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 public/site_assets/mmcFE/images/first.png create mode 100644 public/site_assets/mmcFE/images/last.png create mode 100644 public/site_assets/mmcFE/images/next.png create mode 100644 public/site_assets/mmcFE/images/prev.png diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index daa4a6a0..2b5f093e 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -58,6 +58,18 @@ class User { return $this->updateSingle($id, $field); } + /** + * Fetch all users for administrative tasks + * @param none + * @return data array All users with db columns as array fields + **/ + public function getUsers($filter='%') { + $stmt = $this->mysqli->prepare("SELECT * FROM " . $this->getTableName() . " WHERE username LIKE ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param('s', $filter) && $stmt->execute() && $result = $stmt->get_result()) { + return $result->fetch_all(MYSQLI_ASSOC); + } + } + /** * Check user login * @param username string Username diff --git a/public/include/pages/admin/user.inc.php b/public/include/pages/admin/user.inc.php index 6706e547..17766283 100644 --- a/public/include/pages/admin/user.inc.php +++ b/public/include/pages/admin/user.inc.php @@ -10,10 +10,30 @@ if (!$user->isAdmin($_SESSION['USERDATA']['id'])) { die(); } +$aRoundShares = $statistics->getRoundShares(); + if ($_POST['query']) { - // Fetch all users from DB cross referencing all stats + // Fetch requested users + $aUsers = $user->getUsers($_POST['query']); + + // Add additional stats to each user + // This is not optimized yet, best is a proper SQL + // Query against the stats table? Currently cached though. + foreach ($aUsers as $iKey => $aUser) { + $aUser['balance'] = $transaction->getBalance($aUser['id']); + $aUser['hashrate'] = $statistics->getUserHashrate($aUser['id']); + $aUser['shares'] = $statistics->getUserShares($aUser['id']); + $aUser['payout']['est_block'] = round(( (int)$aUser['shares']['valid'] / (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); + $aUsers[$iKey] = $aUser; + } } +// Assign our variables +$smarty->assign("USERS", $aUsers); + // Tempalte specifics $smarty->assign("CONTENT", "default.tpl"); ?> diff --git a/public/site_assets/mmcFE/css/style.css b/public/site_assets/mmcFE/css/style.css index a41aef4c..8133150a 100644 --- a/public/site_assets/mmcFE/css/style.css +++ b/public/site_assets/mmcFE/css/style.css @@ -465,6 +465,12 @@ a:hover { .block table tr th.right{ text-align: right; } +.block table tr td.center{ + text-align: center; +} +.block table tr th.center{ + text-align: center; +} .block table tr td.delete a { color: #666; } diff --git a/public/site_assets/mmcFE/images/first.png b/public/site_assets/mmcFE/images/first.png new file mode 100644 index 0000000000000000000000000000000000000000..6f11fcb086b1bfa51bc5070ca45fac0a988c624c GIT binary patch literal 720 zcmV;>0x$iEP)ITpohD^Ctgf=H~o==W*|y0)VM;-LQd9lR=YF)677| z!Jh8V_;KSkP5LJrlF9S+j0qkk+2iPm7~x{#{$ZSbvzK9J-4zqYVC)27V7~717pTZd znit9v$VwYJS3ROyLxp^?0)a!0U)oMn7(0Xv0eUoZ@gH)t)d5_J+J%bT22oSJ)g^nf zqQZVCEIYOm2+rB;rB+cID%~N1o%g50=A<=fWgC_GFUb) z%s4&22M=t~3G+RMgR^pz#KbcfK)ZtsdJ$j@d{400GnQJeAz+sPu#&f$`6hKh0J%~V zq(%oWs|qZyzh}{lfSK@dRK#JSxg@DDP3^*cD+kP0j(npKkSQvp3bk_uYtD5My##O+ z;RpcDs9R5c#9;NFP5{z^GN_^r%PX%(0OOZ~7+aNFP*XZk{E2|i#E`vI`YjCrsfz|V zndEsDXoqvug2CxaF}ZvhdOaY6!{9q~KO+jO!o5Vk0qt%NcD}`cvVR;6QTNzYGWq-= zUX+?7V0e7VLW2m6_>T!vT6W8eRsOC$*+0hNMWWwDsLfA)S*X-WV71;M{bCzza-4I45g$8m^55K?|L?r+odSTVNnJ96LXt|7TGI4L>d~Fz z&-h8>1xd<>>vgSt=Y;X^$Jv9(As-4y`R%SPp?5wYYBW2LD^|g`7v>f>k`%^{A!C3N&EeR0q1EmI9?j zT<^fuCti3LD9UEdoCoe2BcvKUvD7KhHa~-21gL%Aj#%s&OGVR_$6iSuSoaqp%eN%J z76DWmU8pyEP^yWrwDz7wF9N262N52Jg#ie_jHv7Leqfv#&`?`&#}Fpfom5)T>7B!> zWsOHK0qlpu1b_k9E1Iu8-65F`u4B0T$PKrdAs|z3NmX=VY5BDbP=DEtvNfR%4Y5Zu z5VMpOD@qkb3GkT!aIm-Gn* z`85iZ>7zVE#RF?u>-GjXzTT2IfyTsX6XN-%yiPPHr0n_^E8Hzpe7MH(L!#e?D90zS zP^g}fN@{04Qcb!flJ%iEo~?q;Y!zl{=dXt}`IHwd0cyqO;QYY^WD0dM6YM#f=E3C+ z!2-R=&FDY;{gfv^)4f+|?c7Tj^US!+(gqX%T+i_HIx{<~voklC*uOv1|9t14Z-oeY T_TF$F*h9sL-j zNpc$weSXQZ6Gag%5kOy9HfA1@g zVA*&|VjFHeVhIV!`DzQM7F-a@NRTNvNlYYp4z%6NAQuJno^MeOJ!7dzy8e(=;etr6 z4q2h83Vb36o=G4!6u79$u)6u4LoN!;_)kItHmh9g5CP1HxVh+sSiS*Mlqx_uVfwL~ z+}p|}Y*;r1{g* z9DE4_W&SW~qUN5hWN@-RUJ#q3Ngxr&iJp;+4aEd;HM{A>I)B?7>yL4`Nc6h^we^ve z3YC6fwXTqAQWr8=Cd?emR={YnNptkhuSFVP{Q?6Lt^ zqF4A?)Arx1JpP&Pyh<4sUhXnam)AJ@xmkE-VuVGbg_|=vm|INb-=FD!zVlDxJ_uiv SXLQ>D0000yhD literal 0 HcmV?d00001 diff --git a/public/site_assets/mmcFE/images/prev.png b/public/site_assets/mmcFE/images/prev.png new file mode 100644 index 0000000000000000000000000000000000000000..15d1584bdbb2c26a9fc4c8269aa54615a58a4657 GIT binary patch literal 745 zcmV1hlor z)=Ec^h7>5J3?Bj(D6|X+0|UcQj&m*_Qg!D|-c07sd7pF6opXl(U}#WhWg0W$d_xV%ATb~UcS*dC+@kfS*m{?64KBH-eM78JQAu#&2c z7TM!?0X98;&a@msh@1ehFDx4~_smCS0rm@Z$dv@5Y^ec>Jl}_`uDh5!i#cpZh5&_) z{y|~cgJzZ1d%`?CxkdJIg%by0u(~PqB0%Zdj?maMmYO=M!=;B#0>~5_5YO>&?w*4H z@LRbi!eNPrxg`gUUIeK8JG(3%R{o~|7so$K+8_qZe0Iz($pKd%d&oOhA&AGYXkKE` zO8~w=kN^smCS05H!-WT*2mpbXAJK6B{s*`+;U?8|s%XO8{Hs1d`DH6am$(L0coEX9 z)c+Ho)WS9-4?6~g4NrO@c2FaKCYmmR!DQ~YpfIn+s8pr^?KThtmU&}3Kr*7R^y^Rk z5Xsb_*=oc3w-9KI@B4=+yK6a8uQ@#oTkf1PnlL+NjAm=Zh)+xqld|idFEF=^(avw| z4U*w)Kx6;tnV0ZXQD@SHtHr7{`Y12 bpX>Y!%!CO%>C8GV00000NkvXXu0mjfJYGkQ literal 0 HcmV?d00001 diff --git a/public/site_assets/mmcFE/js/custom.js b/public/site_assets/mmcFE/js/custom.js index 1e047555..2070bd82 100644 --- a/public/site_assets/mmcFE/js/custom.js +++ b/public/site_assets/mmcFE/js/custom.js @@ -66,6 +66,10 @@ $(function () { widgets: ['zebra'] }); + $("table.pagesort") + .tablesorter({ widgets: ['zebra'] }) + .tablesorterPager({ positionFixed: false, container: $("#pager") }); + $('.block table tr th.header').css('cursor', 'pointer'); // Check / uncheck all checkboxes diff --git a/public/templates/mmcFE/admin/user/default.tpl b/public/templates/mmcFE/admin/user/default.tpl index 8077c4a7..105fb9a2 100644 --- a/public/templates/mmcFE/admin/user/default.tpl +++ b/public/templates/mmcFE/admin/user/default.tpl @@ -2,7 +2,82 @@
      - - + +
      {include file="global/block_footer.tpl"} + +{include file="global/block_header.tpl" BLOCK_HEADER="User Information"} +
      +
      +
      + + + + + + +
      +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + +{section name=user loop=$USERS|default} + + + + + + + + + + + +{/section} + + + + + + + + + + + + + +
      UsernameE-MailHashrateValidInvalidBalanceEst. DonationEst. PayoutAdmin
      {$USERS[user].username}{$USERS[user].email}{$USERS[user].hashrate}{$USERS[user].shares.valid}{$USERS[user].shares.invalid}{$USERS[user].balance}{$USERS[user].payout.est_donation}{$USERS[user].payout.est_payout} + +
      UsernameE-MailHashrateSharesBalanceEst. DonationEst. PayoutAdmin
      +{include file="global/block_footer.tpl"} diff --git a/public/templates/mmcFE/global/navigation.tpl b/public/templates/mmcFE/global/navigation.tpl index 828ed68a..13172478 100644 --- a/public/templates/mmcFE/global/navigation.tpl +++ b/public/templates/mmcFE/global/navigation.tpl @@ -10,7 +10,7 @@
  • {/if} - {if $smarty.session.AUTHENTICATED|default:"0" == 1 && $GLOBAL.userdata.admin == 1}
  • Admin Panel
  • {/if} + {if $smarty.session.AUTHENTICATED|default:"0" == 1 && $GLOBAL.userdata.admin == 1}
  • Admin Panel
    • User Info
    • From 60789b93322b2cf0a9281ec5be38b8be22d0cb96 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 4 Jun 2013 11:55:36 +0200 Subject: [PATCH 006/168] adding user ID to table --- public/templates/mmcFE/admin/user/default.tpl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/templates/mmcFE/admin/user/default.tpl b/public/templates/mmcFE/admin/user/default.tpl index 105fb9a2..905ee09b 100644 --- a/public/templates/mmcFE/admin/user/default.tpl +++ b/public/templates/mmcFE/admin/user/default.tpl @@ -27,6 +27,7 @@
  • + @@ -39,6 +40,7 @@ + @@ -53,6 +55,7 @@ {section name=user loop=$USERS|default} + @@ -69,6 +72,7 @@ + From 38a66b917fe29bcb45effa4606f72708651b9c66 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 4 Jun 2013 12:01:16 +0200 Subject: [PATCH 007/168] adding number format to admin user query table --- public/templates/mmcFE/admin/user/default.tpl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/templates/mmcFE/admin/user/default.tpl b/public/templates/mmcFE/admin/user/default.tpl index 905ee09b..147c3fe4 100644 --- a/public/templates/mmcFE/admin/user/default.tpl +++ b/public/templates/mmcFE/admin/user/default.tpl @@ -58,12 +58,12 @@ - - - - - - + + + + + + From 43772f439bb28cea910d9ae533c221255b123d7c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 4 Jun 2013 12:10:43 +0200 Subject: [PATCH 008/168] fixing sort arrows showing behind text --- public/templates/mmcFE/admin/user/default.tpl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/templates/mmcFE/admin/user/default.tpl b/public/templates/mmcFE/admin/user/default.tpl index 147c3fe4..4e3e0770 100644 --- a/public/templates/mmcFE/admin/user/default.tpl +++ b/public/templates/mmcFE/admin/user/default.tpl @@ -43,12 +43,12 @@ - - - - - - + + + + + + From 4fa3089655d6d2192b72ac4d86527baa7498fdfb Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 4 Jun 2013 12:13:38 +0200 Subject: [PATCH 009/168] fixing JS crash when table has no content --- public/templates/mmcFE/admin/user/default.tpl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/templates/mmcFE/admin/user/default.tpl b/public/templates/mmcFE/admin/user/default.tpl index 4e3e0770..07b6e5f1 100644 --- a/public/templates/mmcFE/admin/user/default.tpl +++ b/public/templates/mmcFE/admin/user/default.tpl @@ -68,6 +68,10 @@ +{sectionelse} + + + {/section} From d85ded7c5c87384e3cc4ce2c9bf4925c95f0cde1 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 4 Jun 2013 13:14:25 +0200 Subject: [PATCH 010/168] Moving from user to statistics class * This fetches all users and joins with shares table Should speed up things a fair bit. --- public/include/classes/statistics.class.php | 30 ++++++++++++++++++- public/include/pages/admin/user.inc.php | 6 ++-- public/templates/mmcFE/admin/user/default.tpl | 28 +++++------------ 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index b8fa232c..c6687ee5 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -54,7 +54,7 @@ class Statistics { $stmt = $this->mysqli->prepare(" SELECT b.*, a.username as finder FROM " . $this->block->getTableName() . " AS b - LEFT JOIN accounts AS a + LEFT JOIN " . $this->user->getTableName() . " AS a ON b.account_id = a.id ORDER BY height DESC LIMIT ?"); if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result()) @@ -175,6 +175,34 @@ class Statistics { return false; } + /** + * Admin panel specific query + * @return data array invlid and valid shares for all accounts + **/ + public function getAllUserStats($filter='%') { + $this->debug->append("STA " . __METHOD__, 4); + if ($data = $this->memcache->get(__FUNCTION__ . $filter)) return $data; + $stmt = $this->mysqli->prepare(" + SELECT + a.id AS id, + a.username AS username, + a.donate_percent AS donate_percent, + a.email AS email, + COUNT(s.id) AS shares, + ROUND(COUNT(s.id) * POW(2," . $this->config['difficulty'] . ") / 600 / 1000,2) AS hashrate + FROM " . $this->user->getTableName() . " AS a + LEFT JOIN " . $this->share->getTableName() . " AS s + ON a.username = SUBSTRING_INDEX( s.username, '.', 1 ) + WHERE + a.username LIKE ? + GROUP BY username + ORDER BY username + "); + if ($this->checkStmt($stmt) && $stmt->bind_param('s', $filter) && $stmt->execute() && $result = $stmt->get_result()) { + return $this->memcache->setCache(__FUNCTION__ . $filter, $result->fetch_all(MYSQLI_ASSOC)); + } + } + /** * Same as getUserShares for Hashrate * @param account_id integer User ID diff --git a/public/include/pages/admin/user.inc.php b/public/include/pages/admin/user.inc.php index 17766283..cc961f44 100644 --- a/public/include/pages/admin/user.inc.php +++ b/public/include/pages/admin/user.inc.php @@ -14,16 +14,14 @@ $aRoundShares = $statistics->getRoundShares(); if ($_POST['query']) { // Fetch requested users - $aUsers = $user->getUsers($_POST['query']); + $aUsers = $statistics->getAllUserStats($_POST['query']); // Add additional stats to each user // This is not optimized yet, best is a proper SQL // Query against the stats table? Currently cached though. foreach ($aUsers as $iKey => $aUser) { $aUser['balance'] = $transaction->getBalance($aUser['id']); - $aUser['hashrate'] = $statistics->getUserHashrate($aUser['id']); - $aUser['shares'] = $statistics->getUserShares($aUser['id']); - $aUser['payout']['est_block'] = round(( (int)$aUser['shares']['valid'] / (int)$aRoundShares['valid'] ) * (int)$config['reward'], 3); + $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); diff --git a/public/templates/mmcFE/admin/user/default.tpl b/public/templates/mmcFE/admin/user/default.tpl index 07b6e5f1..6cd3e4fb 100644 --- a/public/templates/mmcFE/admin/user/default.tpl +++ b/public/templates/mmcFE/admin/user/default.tpl @@ -26,29 +26,16 @@
    ID Username E-Mail Hashrate
    {$USERS[user].id} {$USERS[user].username} {$USERS[user].email} {$USERS[user].hashrate}
    ID Username E-Mail Hashrate{$USERS[user].id} {$USERS[user].username} {$USERS[user].email}{$USERS[user].hashrate}{$USERS[user].shares.valid}{$USERS[user].shares.invalid}{$USERS[user].balance}{$USERS[user].payout.est_donation}{$USERS[user].payout.est_payout}{$USERS[user].hashrate|number_format}{$USERS[user].shares.valid|number_format}{$USERS[user].shares.invalid|number_format}{$USERS[user].balance|number_format:"8"}{$USERS[user].payout.est_donation|number_format:"8"}{$USERS[user].payout.est_payout|number_format:"8"} ID Username E-MailHashrateValidInvalidBalanceEst. DonationEst. PayoutHashrate  Valid  Invalid  Balance   Est. Donation  Est. Payout    Admin
    - - - - - - - - - - - - - - - + + @@ -59,18 +46,17 @@ - - - + + {sectionelse} - + {/section} @@ -80,10 +66,10 @@ - - + + From b19473ff427ed7c9e6bc8850066dd43a04b043c6 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 4 Jun 2013 13:23:00 +0200 Subject: [PATCH 011/168] sorting does not work with number_format --- public/templates/mmcFE/admin/user/default.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/templates/mmcFE/admin/user/default.tpl b/public/templates/mmcFE/admin/user/default.tpl index 6cd3e4fb..5386a84f 100644 --- a/public/templates/mmcFE/admin/user/default.tpl +++ b/public/templates/mmcFE/admin/user/default.tpl @@ -45,8 +45,8 @@ - - + + From 020ea2269b0783450b4fc0bcaa4db4fcf4f0bbf1 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 4 Jun 2013 13:46:56 +0200 Subject: [PATCH 012/168] Re-adding admin flags to user list for admin panel Fixes #99 --- public/include/classes/statistics.class.php | 1 + 1 file changed, 1 insertion(+) diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index c6687ee5..f9fd18c9 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -185,6 +185,7 @@ class Statistics { $stmt = $this->mysqli->prepare(" SELECT a.id AS id, + a.admin as admin, a.username AS username, a.donate_percent AS donate_percent, a.email AS email, From 91d225e340d4215fe9aca4c5e3809fb3e3e6e2a1 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 4 Jun 2013 14:03:46 +0200 Subject: [PATCH 013/168] Adding wallet information in Admin panel Fixes #63 and adds proper wallet information: * Wallet balance * Locked balance for users * Liquid assets available to pool owner --- public/include/pages/admin/wallet.inc.php | 25 +++++++++++++++++++ .../templates/mmcFE/admin/wallet/default.tpl | 16 ++++++++++++ public/templates/mmcFE/global/navigation.tpl | 1 + 3 files changed, 42 insertions(+) create mode 100644 public/include/pages/admin/wallet.inc.php create mode 100644 public/templates/mmcFE/admin/wallet/default.tpl diff --git a/public/include/pages/admin/wallet.inc.php b/public/include/pages/admin/wallet.inc.php new file mode 100644 index 00000000..cb094fe9 --- /dev/null +++ b/public/include/pages/admin/wallet.inc.php @@ -0,0 +1,25 @@ +isAdmin($_SESSION['USERDATA']['id'])) { + header("HTTP/1.1 404 Page not found"); + die(); +} + +if ($bitcoin->can_connect() === true){ + $dBalance = $bitcoin->query('getbalance'); +} else { + $dBalance = 0; + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg'); +} + +$smarty->assign("BALANCE", $dBalance); +$smarty->assign("LOCKED", $transaction->getLockedBalance()); + +// Tempalte specifics +$smarty->assign("CONTENT", "default.tpl"); +?> diff --git a/public/templates/mmcFE/admin/wallet/default.tpl b/public/templates/mmcFE/admin/wallet/default.tpl new file mode 100644 index 00000000..42b8c8b3 --- /dev/null +++ b/public/templates/mmcFE/admin/wallet/default.tpl @@ -0,0 +1,16 @@ +{include file="global/block_header.tpl" BLOCK_HEADER="Wallet Information"} +
    ID Username E-Mail Hashrate  Valid  Invalid  Balance   Shares   Est. Donation   Est. Payout   Balance    Admin
    {$USERS[user].username} {$USERS[user].email} {$USERS[user].hashrate|number_format}{$USERS[user].shares.valid|number_format}{$USERS[user].shares.invalid|number_format}{$USERS[user].balance|number_format:"8"}{$USERS[user].shares|number_format} {$USERS[user].payout.est_donation|number_format:"8"} {$USERS[user].payout.est_payout|number_format:"8"}{$USERS[user].balance|number_format:"8"}
    Username E-Mail HashrateSharesBalanceShares Est. Donation Est. PayoutBalance Admin
    {$USERS[user].id} {$USERS[user].username} {$USERS[user].email}{$USERS[user].hashrate|number_format}{$USERS[user].shares|number_format}{$USERS[user].hashrate / 1024}{$USERS[user].shares} {$USERS[user].payout.est_donation|number_format:"8"} {$USERS[user].payout.est_payout|number_format:"8"} {$USERS[user].balance|number_format:"8"}
    + + + + + + + + + + + + +
    Wallet Balance{$BALANCE|number_format:"8"}
    Locked for users{$LOCKED|number_format:"8"}
    Liquid Assets{($BALANCE - $LOCKED)|number_format:"8"}
    +{include file="global/block_footer.tpl"} diff --git a/public/templates/mmcFE/global/navigation.tpl b/public/templates/mmcFE/global/navigation.tpl index 13172478..6a0cdb37 100644 --- a/public/templates/mmcFE/global/navigation.tpl +++ b/public/templates/mmcFE/global/navigation.tpl @@ -14,6 +14,7 @@
  • Admin Panel
  • {/if} From af3252abb20aa7ec4cfacb03ff046e770fb25886 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 4 Jun 2013 14:28:34 +0200 Subject: [PATCH 014/168] Find ALL transactions, even unconfirmed * This ensures that credits are not unlocked and available to the pool --- public/include/classes/transaction.class.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index 15f29c77..020140d3 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -130,6 +130,7 @@ class Transaction { /** * Get total balance for all users locked in wallet + * This includes any outstanding unconfirmed transactions! * @param none * @return data double Amount locked for users **/ @@ -141,9 +142,7 @@ class Transaction { ( 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 = 'Credit' - AND b.confirmations >= ? ) AS t1, ( SELECT sum(t.amount) AS debit @@ -152,12 +151,10 @@ class Transaction { ) AS t2, ( SELECT sum(t.amount) AS other - FROM transactions AS t - LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id + FROM " . $this->table . " AS t WHERE t.type IN ('Donation','Fee') - AND b.confirmations >= ? ) AS t3"); - if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $this->config['confirmations'], $this->config['confirmations']) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch()) + if ($this->checkStmt($stmt) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch()) return $dBalance; // Catchall $this->setErrorMessage('Unable to find locked credits for all users'); From 4277fb26cc59ce0cd5d4cfb7c2b7a26f5efc6631 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 4 Jun 2013 15:10:07 +0200 Subject: [PATCH 015/168] Removed PHP Notice warning when running cron This fixes #102, we don't need to see this warning since it doesn't affect the job at all. --- 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 30d0b018..d22d1f19 100755 --- a/cronjobs/proportional_payout.php +++ b/cronjobs/proportional_payout.php @@ -32,7 +32,7 @@ if (empty($aAllBlocks)) { $count = 0; foreach ($aAllBlocks as $iIndex => $aBlock) { if (!$aBlock['accounted']) { - $iPreviousShareId = $aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0; + $iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0; $iCurrentUpstreamId = $aBlock['share_id']; $aAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']); $iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']); From 930bb508001811f856b8bf10b1313d3a8bbd7699 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 4 Jun 2013 15:49:52 +0200 Subject: [PATCH 016/168] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 66e3dcc2..b8b1f0a8 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,11 @@ The following feature have been implemented so far: * Manual payout with 0.1 LTC fee * Auto payout with 0.1 LTC fee * Transaction list (confirmed and unconfirmed) +* Admin Panel + * User Listing including statistics + * Wallet information + * (Planned) News Posts + * (Planned) Pool Donations Installation ============ From c1682e22030dd82a769c3fbe8704337cbff3baa4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 4 Jun 2013 15:58:51 +0200 Subject: [PATCH 017/168] Ignore rejected shares in top list calculations Fixes #104 --- public/include/classes/statistics.class.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index f9fd18c9..c261a884 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -263,6 +263,7 @@ class Statistics { COUNT(id) AS shares, SUBSTRING_INDEX( username, '.', 1 ) AS account FROM " . $this->share->getTableName() . " + WHERE our_result = 'Y' GROUP BY account ORDER BY shares DESC LIMIT ?"); @@ -279,6 +280,7 @@ class Statistics { SUBSTRING_INDEX( username, '.', 1 ) AS account FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) + AND our_result = 'Y' GROUP BY account ORDER BY hashrate DESC LIMIT ?"); if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result()) From e37fb70a46899d2849c0e11bee62bdd712094585 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 4 Jun 2013 20:51:24 +0200 Subject: [PATCH 018/168] Do not deduct fees from balance when transmitting Fixes #106, we want to transfer the entire balance and let the RPC server deduct any fees that might apply. --- cronjobs/auto_payout.php | 6 +++--- public/include/pages/account/edit.inc.php | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cronjobs/auto_payout.php b/cronjobs/auto_payout.php index 3454ad9c..68b60aa4 100755 --- a/cronjobs/auto_payout.php +++ b/cronjobs/auto_payout.php @@ -38,7 +38,7 @@ if (! empty($users)) { $dBalance = $transaction->getBalance($aUserData['id']); verbose($aUserData['id'] . "\t" . $aUserData['username'] . "\t" . $dBalance . "\t" . $aUserData['ap_threshold'] . "\t\t" . $aUserData['coin_address'] . "\t"); - // Only run if balance meets threshold and can pay the transaction fee + // Only run if balance meets threshold and can pay the potential transaction fee if ($dBalance > $aUserData['ap_threshold'] && $dBalance > 0.1) { // Validate address against RPC try { @@ -48,9 +48,9 @@ if (! empty($users)) { continue; } - // Send balance - 0.1 Fee to address + // Send balance, fees are reduced later try { - $bitcoin->sendtoaddress($aUserData['coin_address'], $dBalance - 0.1); + $bitcoin->sendtoaddress($aUserData['coin_address'], $dBalance); } catch (BitcoinClientException $e) { verbose("SEND FAILED\n"); continue; diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php index bb29cace..6308e271 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -16,6 +16,7 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST $continue = true; $dBalance = $transaction->getBalance($_SESSION['USERDATA']['id']); $sCoinAddress = $user->getCoinAddress($_SESSION['USERDATA']['id']); + // Ensure we can cover the potential transaction fee of 0.1 LTC with the balance if ($dBalance > 0.1) { if ($bitcoin->can_connect() === true) { try { @@ -25,9 +26,9 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST $continue = false; } if ($continue == true) { - // Remove the transfer fee and send to address + // Send balance to address, mind 0.1 fee for transaction! try { - $bitcoin->sendtoaddress($sCoinAddress, $dBalance - 0.1); + $bitcoin->sendtoaddress($sCoinAddress, $dBalance); } catch (BitcoinClientException $e) { $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to send LTC, please contact site support immidiately', 'TYPE' => 'errormsg'); $continue = false; From 6c4fb84ee909ccdd27e04cf9f60019a2b718bca3 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 5 Jun 2013 09:11:25 +0200 Subject: [PATCH 019/168] Adding min/max threshold configuration * Check for min/max values set for auto payouts * Display error messages to the user * Sanitize values just in case we fall through the validation * Updated template and class * New configuration option added! Update your local configs! Fixes #108 --- public/include/classes/user.class.php | 21 +++++++++++++++++++-- public/include/config/global.inc.dist.php | 4 ++++ public/include/pages/account/edit.inc.php | 2 +- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 2b5f093e..8db17a1e 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -241,10 +241,27 @@ class User { public function updateAccount($userID, $address, $threshold, $donate) { $this->debug->append("STA " . __METHOD__, 4); $bUser = false; - $threshold = min(250, max(0, floatval($threshold))); - if ($threshold < 1) $threshold = 0.0; + + // number validation checks + if ($threshold < $this->config['ap_threshold']['min'] && $threshold != 0) { + $this->setErrorMessage('Threshold below configured minimum of ' . $this->config['ap_threshold']['min']); + return false; + } else if ($threshold > $this->config['ap_threshold']['max']) { + $this->setErrorMessage('Threshold above configured maximum of ' . $this->config['ap_threshold']['max']); + return false; + } + if ($donate < 0) { + $this->setErrorMessage('Donation below allowed 0% limit'); + return false; + } else if ($donate > 100) { + $this->setErrorMessage('Donation above allowed 100% limit'); + return false; + } + // Number sanitizer, just in case we fall through above + $threshold = min($this->config['ap_threshold']['max'], max(0, floatval($threshold))); $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 = ? WHERE id = ?"); $stmt->bind_param('sddi', $address, $threshold, $donate, $userID); $stmt->execute(); diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 2bb6069f..306d5261 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -25,6 +25,10 @@ $config = array( 'url' => 'https://btc-e.com/api/2', 'target' => '/ltc_usd/ticker' ), + 'ap_threshold' => array( + 'min' => 1, + 'max' => 250 + ), 'website' => array( 'name' => 'The Pool', 'slogan' => 'Resistance is futile', diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php index 6308e271..84937115 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -49,7 +49,7 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST if ($user->updateAccount($_SESSION['USERDATA']['id'], $_POST['paymentAddress'], $_POST['payoutThreshold'], $_POST['donatePercent'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account details updated', 'TYPE' => 'success'); } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update your account', 'TYPE' => 'errormsg'); + $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update your account: ' . $user->getError(), 'TYPE' => 'errormsg'); } break; From 11338cedf3e3a8e09d8a24b6ee2a93debfc0011c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 5 Jun 2013 09:17:10 +0200 Subject: [PATCH 020/168] Add new threshold display to template Forgot to add the template files that adds configurable thresholds --- public/include/smarty_globals.inc.php | 8 +++++++- public/templates/mmcFE/account/edit/default.tpl | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index f34b3b4a..fa5c7923 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -25,7 +25,13 @@ $aGlobal = array( 'reward' => $config['reward'], 'price' => $setting->getValue('price'), 'blockexplorer' => $config['blockexplorer'], - 'chaininfo' => $config['chaininfo'] + 'chaininfo' => $config['chaininfo'], + 'config' => array( + 'ap_threshold' => array( + 'min' => $config['ap_threshold']['min'], + 'max' => $config['ap_threshold']['max'] + ) + ) ); // We don't want these session infos cached diff --git a/public/templates/mmcFE/account/edit/default.tpl b/public/templates/mmcFE/account/edit/default.tpl index 9d885b16..2fc9b4a9 100644 --- a/public/templates/mmcFE/account/edit/default.tpl +++ b/public/templates/mmcFE/account/edit/default.tpl @@ -9,7 +9,7 @@ API Key: {$GLOBAL.userdata.api_key} Payment Address: Donation %: [donation amount in percent (example: 0.5)] - Automatic Payout Threshold: [1-250 LTC. Set to '0' for no auto payout] + Automatic Payout Threshold: [{$GLOBAL.config.ap_threshold.min}-{$GLOBAL.config.ap_threshold.max} LTC. Set to '0' for no auto payout] 4 digit PIN: [The 4 digit PIN you chose when registering] From aff116849cb9a750f315f36b819de64a6907fdcc Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 5 Jun 2013 11:49:08 +0200 Subject: [PATCH 021/168] Adding support for public API polling * Does not require a token * Returns basic status as taken from default mmcfe Addresses #111 --- public/include/pages/api/public.inc.php | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 public/include/pages/api/public.inc.php diff --git a/public/include/pages/api/public.inc.php b/public/include/pages/api/public.inc.php new file mode 100644 index 00000000..162e9134 --- /dev/null +++ b/public/include/pages/api/public.inc.php @@ -0,0 +1,26 @@ +getLast(); +$aShares = $statistics->getRoundShares(); + +echo json_encode( + array( + 'pool_name' => $config['website']['name'], + 'hashrate' => $statistics->getCurrentHashrate(), + 'workers' => $worker->getCountAllActiveWorkers(), + 'shares_this_round' => $aShares['valid'], + 'last_block' => $aLastBlock['height'], + 'network_hashrate' => '0' + ) +); + +// Supress master template +$supress_master = 1; +?> From 8ee987a98d92430ede5cb47e79abb2b9c000a785 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 5 Jun 2013 15:31:53 +0200 Subject: [PATCH 022/168] Adding reference to API on public stats page Addresses #113 --- public/templates/mmcFE/statistics/default.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/public/templates/mmcFE/statistics/default.tpl b/public/templates/mmcFE/statistics/default.tpl index d1dfd202..a8e9a4c2 100644 --- a/public/templates/mmcFE/statistics/default.tpl +++ b/public/templates/mmcFE/statistics/default.tpl @@ -19,4 +19,5 @@ +
  • These stats are also available in JSON format HERE
  • {include file="global/block_footer.tpl"} From 7dc0736b777917bb535799ed224fff1a8853d398 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 5 Jun 2013 17:22:47 +0200 Subject: [PATCH 023/168] First version for new user graphs * Properly calculate hashrate * Remove number formatting, it breaks the graph * Not properly in order based on time but displays correct values Addresses #90 --- public/include/classes/statistics.class.php | 24 +++++++++---------- public/include/pages/statistics/user.inc.php | 10 -------- .../mmcFE/statistics/user/default.tpl | 2 +- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index c261a884..87969707 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -302,22 +302,22 @@ class Statistics { if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data; $stmt = $this->mysqli->prepare(" SELECT - ROUND(COUNT(s.id) * POW(2, 12)/600/1000) AS hashrate, + 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 + 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, 12)/600/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 ) - AND a.id = ? - GROUP BY HOUR(time)"); + 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 ) + AND a.id = ? + GROUP BY HOUR(time)"); if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $account_id, $account_id) && $stmt->execute() && $result = $stmt->get_result()) return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_all(MYSQLI_ASSOC), 3600); // Catchall diff --git a/public/include/pages/statistics/user.inc.php b/public/include/pages/statistics/user.inc.php index 41440b99..2a713925 100644 --- a/public/include/pages/statistics/user.inc.php +++ b/public/include/pages/statistics/user.inc.php @@ -4,16 +4,6 @@ if (!defined('SECURITY')) die('Hacking attempt'); -// Fetch data from litecoind -if ($bitcoin->can_connect() === true){ - $dDifficulty = $bitcoin->getdifficulty(); - $iBlock = $bitcoin->getblockcount(); -} else { - $iDifficulty = 1; - $iBlock = 0; - $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg'); -} - $aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA']['id']); // Propagate content our template diff --git a/public/templates/mmcFE/statistics/user/default.tpl b/public/templates/mmcFE/statistics/user/default.tpl index ffc5e8c8..dee8180e 100644 --- a/public/templates/mmcFE/statistics/user/default.tpl +++ b/public/templates/mmcFE/statistics/user/default.tpl @@ -14,7 +14,7 @@ {$GLOBAL.USERDATA.username} {section hashrate $YOURHASHRATES} - {$YOURHASHRATES[hashrate].hashrate|number_format} + {$YOURHASHRATES[hashrate].hashrate} {/section} From 52d079eaed514b30240bd768d1b734a6b8593bc8 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 10:45:37 +0200 Subject: [PATCH 024/168] do not include archive table for hashrates, better formatting --- public/include/classes/statistics.class.php | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index 87969707..be101068 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -302,22 +302,15 @@ class Statistics { if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data; $stmt = $this->mysqli->prepare(" SELECT - ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ")/3600/1000) AS hashrate, + 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 + 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 ) - AND a.id = ? - GROUP BY HOUR(time)"); + "); if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $account_id, $account_id) && $stmt->execute() && $result = $stmt->get_result()) return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_all(MYSQLI_ASSOC), 3600); // Catchall From 232e79f7ad6aeeac574d1ee83e0255f15b4e59d6 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 11:01:04 +0200 Subject: [PATCH 025/168] do not pass two arguments to SQL --- 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 be101068..edbc24a0 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -311,7 +311,7 @@ class Statistics { AND a.id = ? GROUP BY HOUR(time) "); - if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $account_id, $account_id) && $stmt->execute() && $result = $stmt->get_result()) + if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result()) return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_all(MYSQLI_ASSOC), 3600); // Catchall $this->debug->append("Failed to fetch hourly hashrate: " . $this->mysqli->error); From 1bf2e7cf181ca5b9e38c3a6a1a6ba2c24e79b8ef Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 11:47:20 +0200 Subject: [PATCH 026/168] Pre-sort SQL data in array for easy time access This allows us to access the array key as the time. This way the template can properly render the time axis according to current time. --- public/include/classes/statistics.class.php | 8 +++++-- public/include/pages/statistics/user.inc.php | 1 - .../mmcFE/statistics/user/default.tpl | 22 ++++++++++++++----- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index edbc24a0..a85cb145 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -311,8 +311,12 @@ class Statistics { AND a.id = ? GROUP BY HOUR(time) "); - if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result()) - return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_all(MYSQLI_ASSOC), 3600); + if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result()) { + while ($row = $result->fetch_assoc()) { + $aData[$row['hour']] = $row['hashrate']; + } + return $this->memcache->setCache(__FUNCTION__ . $account_id, $aData, 3600); + } // Catchall $this->debug->append("Failed to fetch hourly hashrate: " . $this->mysqli->error); return false; diff --git a/public/include/pages/statistics/user.inc.php b/public/include/pages/statistics/user.inc.php index 2a713925..2b0b0ed9 100644 --- a/public/include/pages/statistics/user.inc.php +++ b/public/include/pages/statistics/user.inc.php @@ -8,7 +8,6 @@ $aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA' // Propagate content our template $smarty->assign("YOURHASHRATES", $aHourlyHashRates); -$smarty->assign("DIFFICULTY", $dDifficulty); if ($_SESSION['AUTHENTICATED']) { $smarty->assign("CONTENT", "default.tpl"); diff --git a/public/templates/mmcFE/statistics/user/default.tpl b/public/templates/mmcFE/statistics/user/default.tpl index dee8180e..201c783c 100644 --- a/public/templates/mmcFE/statistics/user/default.tpl +++ b/public/templates/mmcFE/statistics/user/default.tpl @@ -1,23 +1,33 @@ {include file="global/block_header.tpl" BLOCK_HEADER="Your Average Hourly Hash Rate" BUTTONS=array(mine,pool,both)} +{if is_array($YOURHASHRATES)}
    -{section hashrate $YOURHASHRATES} - -{/section} +{for $i=date('G', time() + 60 * 60) to 23} + +{/for} +{for $i=0 to date('G')} + +{/for} -{section hashrate $YOURHASHRATES} - -{/section} +{for $i=date('G', time() + 60 * 60) to 23} + +{/for} +{for $i=0 to date('G')} + +{/for}
    Your Hashrate 
    {$YOURHASHRATES[hashrate].hour}{$i}{$i}
    {$GLOBAL.USERDATA.username}{$YOURHASHRATES[hashrate].hashrate}{$YOURHASHRATES.$i|default:"0"}{$YOURHASHRATES.$i|default:"0"}
    +{else} +

  • No shares available to start calculations
  • +{/if} {include file="global/block_footer.tpl"} From 8fccc8fe3a960a26ce07e541ead21c678ed9e7ea Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 11:57:46 +0200 Subject: [PATCH 027/168] proper time range --- public/templates/mmcFE/statistics/user/default.tpl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/templates/mmcFE/statistics/user/default.tpl b/public/templates/mmcFE/statistics/user/default.tpl index 201c783c..aec27d29 100644 --- a/public/templates/mmcFE/statistics/user/default.tpl +++ b/public/templates/mmcFE/statistics/user/default.tpl @@ -6,21 +6,21 @@ -{for $i=date('G', time() + 60 * 60) to 23} - {$i} +{for $i=date('G') to 23} + {$i}:00 {/for} -{for $i=0 to date('G')} - {$i} +{for $i=0 to date('G', time () - 60 * 60)} + {$i}:00 {/for} {$GLOBAL.USERDATA.username} -{for $i=date('G', time() + 60 * 60) to 23} +{for $i=date('G') to 23} {$YOURHASHRATES.$i|default:"0"} {/for} -{for $i=0 to date('G')} +{for $i=0 to date('G', time() - 60 * 60)} {$YOURHASHRATES.$i|default:"0"} {/for} From aebb97a1d86786568717baaeef77eaa0fb65e2eb Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 12:01:06 +0200 Subject: [PATCH 028/168] use default cashing times --- 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 a85cb145..4ed74bb4 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -315,7 +315,7 @@ class Statistics { while ($row = $result->fetch_assoc()) { $aData[$row['hour']] = $row['hashrate']; } - return $this->memcache->setCache(__FUNCTION__ . $account_id, $aData, 3600); + return $this->memcache->setCache(__FUNCTION__ . $account_id, $aData); } // Catchall $this->debug->append("Failed to fetch hourly hashrate: " . $this->mysqli->error); From 7e76bb4a63110ffff83d3d2dc4d9ed86c2a4549a Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 12:02:58 +0200 Subject: [PATCH 029/168] properly display username in graph --- public/templates/mmcFE/statistics/user/default.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/templates/mmcFE/statistics/user/default.tpl b/public/templates/mmcFE/statistics/user/default.tpl index aec27d29..dc285321 100644 --- a/public/templates/mmcFE/statistics/user/default.tpl +++ b/public/templates/mmcFE/statistics/user/default.tpl @@ -16,7 +16,7 @@ - {$GLOBAL.USERDATA.username} + {$smarty.session.USERDATA.username} {for $i=date('G') to 23} {$YOURHASHRATES.$i|default:"0"} {/for} From e3702a5804d6c6b5a5bb9bff7464b72d9e68d9b3 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 13:37:05 +0200 Subject: [PATCH 030/168] better looking worker table --- public/templates/mmcFE/account/workers/default.tpl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/templates/mmcFE/account/workers/default.tpl b/public/templates/mmcFE/account/workers/default.tpl index 90637260..f1b6e875 100644 --- a/public/templates/mmcFE/account/workers/default.tpl +++ b/public/templates/mmcFE/account/workers/default.tpl @@ -9,18 +9,18 @@ Worker Name Password - Active - Khash/s + Active + Khash/s     {section worker $WORKERS} {assign var="username" value="."|escape|explode:$WORKERS[worker].username:2} - {$username.0|escape}. + {$username.0|escape}. - {if $WORKERS[worker].active == 1}Y{else}N{/if} - {$WORKERS[worker].hashrate} + + {$WORKERS[worker].hashrate|number_format} {/section} From 2c56066763f86f6875b23a58cfd14f51b36d94e4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 15:15:27 +0200 Subject: [PATCH 031/168] changed time-range for graphs --- public/templates/mmcFE/statistics/user/default.tpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/templates/mmcFE/statistics/user/default.tpl b/public/templates/mmcFE/statistics/user/default.tpl index dc285321..235ec574 100644 --- a/public/templates/mmcFE/statistics/user/default.tpl +++ b/public/templates/mmcFE/statistics/user/default.tpl @@ -6,10 +6,10 @@ -{for $i=date('G') to 23} +{for $i=date('G', time() - 60 * 60) to 23} {$i}:00 {/for} -{for $i=0 to date('G', time () - 60 * 60)} +{for $i=0 to date('G', time () - 2 * 60 * 60)} {$i}:00 {/for} @@ -17,10 +17,10 @@ {$smarty.session.USERDATA.username} -{for $i=date('G') to 23} +{for $i=date('G', time() - 60 * 60) to 23} {$YOURHASHRATES.$i|default:"0"} {/for} -{for $i=0 to date('G', time() - 60 * 60)} +{for $i=0 to date('G', time() - 2 * 60 * 60)} {$YOURHASHRATES.$i|default:"0"} {/for} From 3094c9bb9d832bb41539285c817fcea02596a638 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 16:54:21 +0200 Subject: [PATCH 032/168] re-adjusting template again --- public/templates/mmcFE/statistics/user/default.tpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/templates/mmcFE/statistics/user/default.tpl b/public/templates/mmcFE/statistics/user/default.tpl index 235ec574..dc285321 100644 --- a/public/templates/mmcFE/statistics/user/default.tpl +++ b/public/templates/mmcFE/statistics/user/default.tpl @@ -6,10 +6,10 @@ -{for $i=date('G', time() - 60 * 60) to 23} +{for $i=date('G') to 23} {$i}:00 {/for} -{for $i=0 to date('G', time () - 2 * 60 * 60)} +{for $i=0 to date('G', time () - 60 * 60)} {$i}:00 {/for} @@ -17,10 +17,10 @@ {$smarty.session.USERDATA.username} -{for $i=date('G', time() - 60 * 60) to 23} +{for $i=date('G') to 23} {$YOURHASHRATES.$i|default:"0"} {/for} -{for $i=0 to date('G', time() - 2 * 60 * 60)} +{for $i=0 to date('G', time() - 60 * 60)} {$YOURHASHRATES.$i|default:"0"} {/for} From d5866207c13e9f6ef33fb9f4a7835876157c1df4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 17:18:55 +0200 Subject: [PATCH 033/168] Adding admin only API call: getuserworkers * Require valid token and admin access * Grab full worker information for a user * Matches the Worker List on the Account page * See Wiki for full documentation --- .../include/pages/api/getuserworkers.inc.php | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 public/include/pages/api/getuserworkers.inc.php diff --git a/public/include/pages/api/getuserworkers.inc.php b/public/include/pages/api/getuserworkers.inc.php new file mode 100644 index 00000000..23bdcf5d --- /dev/null +++ b/public/include/pages/api/getuserworkers.inc.php @@ -0,0 +1,24 @@ +checkApiKey($_REQUEST['api_key']); + +// We have to check if that user is admin too +if ( ! $user->isAdmin($id) ) { + header("HTTP/1.1 401 Unauthorized"); + die("Access denied"); +} + +// Is it a username or a user ID +ctype_digit($_REQUEST['id']) ? $id = $_REQUEST['id'] : $id = $user->getUserId($_REQUEST['id']); + +// Output JSON format +echo json_encode(array('getuserworkers' => $worker->getWorkers($id))); + +// Supress master template +$supress_master = 1; +?> From c7e9aaac0916bf9b35ae56a0dae1b946e2057518 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 17:28:41 +0200 Subject: [PATCH 034/168] adding proper credits since only the UI code is still the original --- public/templates/mmcFE/global/footer.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/templates/mmcFE/global/footer.tpl b/public/templates/mmcFE/global/footer.tpl index bf425d19..cef5c014 100644 --- a/public/templates/mmcFE/global/footer.tpl +++ b/public/templates/mmcFE/global/footer.tpl @@ -1,5 +1,5 @@
    - Litecoin Pool using litecoind, pushpoold
    - mmcfe-ng Website based on mmcfe, overhauled by TheSerapher, available on GitHub
    + Litecoin Pool using litecoind, pushpoold
    , stratum-mining + mmcfe-ng Website based on mmcfe by AnnihilaT overhauled by TheSerapher, available on GitHub
    LTC: Lge95QR2frp9y1wJufjUPCycVsg5gLJPW8


    From 9286508342e612c509368b3048b6e0b6d0a32f91 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 21:04:43 +0200 Subject: [PATCH 035/168] new database structure for future features --- sql/mmcfe_ng_structure.sql | 44 +++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/sql/mmcfe_ng_structure.sql b/sql/mmcfe_ng_structure.sql index 4b6bf27d..c3c626bd 100644 --- a/sql/mmcfe_ng_structure.sql +++ b/sql/mmcfe_ng_structure.sql @@ -3,7 +3,7 @@ -- http://www.phpmyadmin.net -- -- Host: localhost --- Generation Time: May 31, 2013 at 02:31 PM +-- Generation Time: Jun 06, 2013 at 09:01 PM -- Server version: 5.5.31-0ubuntu0.13.04.1 -- PHP Version: 5.4.9-4ubuntu2 @@ -65,7 +65,24 @@ CREATE TABLE IF NOT EXISTS `blocks` ( PRIMARY KEY (`id`), UNIQUE KEY `height` (`height`,`blockhash`), KEY `time` (`time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Discovered blocks persisted from Litecoin Service'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Discovered blocks persisted from Litecoin Service'; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `pool_worker` +-- + +CREATE TABLE IF NOT EXISTS `pool_worker` ( + `id` int(255) NOT NULL AUTO_INCREMENT, + `account_id` int(255) NOT NULL, + `username` char(50) DEFAULT NULL, + `password` char(255) DEFAULT NULL, + `monitor` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `username` (`username`), + KEY `account_id` (`account_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -------------------------------------------------------- @@ -100,7 +117,7 @@ CREATE TABLE IF NOT EXISTS `shares` ( KEY `upstream_result` (`upstream_result`), KEY `our_result` (`our_result`), KEY `username` (`username`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -------------------------------------------------------- @@ -147,7 +164,7 @@ CREATE TABLE IF NOT EXISTS `statistics_shares` ( CREATE TABLE IF NOT EXISTS `transactions` ( `id` int(255) NOT NULL AUTO_INCREMENT, `account_id` int(255) unsigned NOT NULL, - `type` enum('Credit','Debit_MP','Debit_AP','Donation','Fee','Orphan_Credit','Orphan_Fee','Orphan_Donation') DEFAULT NULL, + `type` enum('Credit','Debit_MP','Debit_AP','Donation','Fee','Orphan_Credit','Orphan_Fee','Orphan_Donation','Credit_PPS','Fee_PPS','Donation_PPS') DEFAULT NULL, `coin_address` varchar(255) DEFAULT NULL, `amount` double DEFAULT '0', `block_id` int(255) DEFAULT NULL, @@ -156,24 +173,7 @@ CREATE TABLE IF NOT EXISTS `transactions` ( KEY `block_id` (`block_id`), KEY `account_id` (`account_id`), KEY `type` (`type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `pool_worker` --- - -CREATE TABLE IF NOT EXISTS `pool_worker` ( - `id` int(255) NOT NULL AUTO_INCREMENT, - `account_id` int(255) NOT NULL, - `username` char(50) DEFAULT NULL, - `password` char(255) DEFAULT NULL, - `hashrate` int(11) DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `username` (`username`), - KEY `account_id` (`account_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; From 596b0eac9300769a2c72e631417414dc0368e9e6 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 21:31:05 +0200 Subject: [PATCH 036/168] Moving shares per block into findblock cron To better support more payout systems I have moved the share calculation for a block into the findblock cron. This way these statistics are always available and not depending on the actual payout method. --- cronjobs/findblock.php | 14 +++++++++++++- cronjobs/proportional_payout.php | 9 --------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/cronjobs/findblock.php b/cronjobs/findblock.php index 025c1d3d..64d32914 100755 --- a/cronjobs/findblock.php +++ b/cronjobs/findblock.php @@ -66,11 +66,15 @@ if (empty($aTransactions['transactions'])) { } } +verbose("\n\n"); // Now with our blocks added we can scan for their upstream shares $aAllBlocks = $block->getAllUnaccounted('ASC'); +if (empty($aAllBlocks)) { + verbose("No new unaccounted blocks found\n"); +} // Loop through our unaccounted blocks -verbose("Block ID\tBlock Height\tShare ID\tFinder\t\t\tStatus\n"); +verbose("Block ID\tBlock Height\tShare ID\tShares\tFinder\t\t\tStatus\n"); foreach ($aAllBlocks as $iIndex => $aBlock) { if (empty($aBlock['share_id'])) { // Fetch this blocks upstream ID @@ -82,16 +86,24 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { verbose($share->getError() . "\n"); continue; } + // Fetch share information + $iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0; + $iRoundShares = $share->getRoundShares($iPreviousShareId, $iCurrentUpstreamId); + // Store new information $strStatus = "OK"; if (!$block->setShareId($aBlock['id'], $iCurrentUpstreamId)) $strStatus = "Share ID Failed"; if (!$block->setFinder($aBlock['id'], $iAccountId)) $strStatus = "Finder Failed"; + if (!$block->setShares($aBlock['id'], $iRoundShares)) + $strStatus = "Shares Failed"; + verbose( $aBlock['id'] . "\t\t" . $aBlock['height'] . "\t\t" . $iCurrentUpstreamId . "\t\t" + . $iRoundShares . "\t" . "[$iAccountId] " . $user->getUserName($iAccountId) . "\t\t" . $strStatus . "\n" diff --git a/cronjobs/proportional_payout.php b/cronjobs/proportional_payout.php index d22d1f19..e99387f2 100755 --- a/cronjobs/proportional_payout.php +++ b/cronjobs/proportional_payout.php @@ -37,20 +37,11 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $aAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']); $iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']); - // Table header for block details - verbose("ID\tHeight\tTime\t\tShares\tFinder\t\tShare ID\tPrev Share\t\tStatus\n"); - verbose($aBlock['id'] . "\t" . $aBlock['height'] . "\t" . $aBlock['time'] . "\t" . $iRoundShares . "\t" . $user->getUserName($aBlock['account_id']) . "\t" . $iCurrentUpstreamId . "\t\t" . $iPreviousShareId); - if (empty($aAccountShares)) { verbose("\nNo shares found for this block\n\n"); sleep(2); continue; } - $strStatus = "OK"; - // Store share information for this block - if (!$block->setShares($aBlock['id'], $iRoundShares)) - $strStatus = "Shares Failed"; - verbose("\t\t$strStatus\n\n"); // Table header for account shares verbose("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee\t\tStatus\n"); From 4691e077e2a69a375d85a84c9a5ca12c018afe53 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 22:27:34 +0200 Subject: [PATCH 037/168] Ensure no old blocks shares are counter for new ones When finding more than a single block between runs it added shares to a new block from a previous one. Properly fetch the last highest share ID from the database prior to scanning for shares assigned to a block. Fixes #124 --- cronjobs/findblock.php | 6 +++++- public/include/classes/block.class.php | 12 ++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/cronjobs/findblock.php b/cronjobs/findblock.php index 64d32914..2f978eed 100755 --- a/cronjobs/findblock.php +++ b/cronjobs/findblock.php @@ -87,7 +87,11 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { continue; } // Fetch share information - $iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0; + 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"); + } $iRoundShares = $share->getRoundShares($iPreviousShareId, $iCurrentUpstreamId); // Store new information diff --git a/public/include/classes/block.class.php b/public/include/classes/block.class.php index aad32c08..743ce67e 100644 --- a/public/include/classes/block.class.php +++ b/public/include/classes/block.class.php @@ -43,6 +43,18 @@ class Block { return false; } + /** + * Get our last, highest share ID inserted for a block + * @param none + * @return int data Share ID + **/ + public function getLastShareId() { + $stmt = $this->mysqli->prepare("SELECT MAX(share_id) AS share_id FROM $this->table LIMIT 1"); + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_object()->share_id; + return false; + } + /** * Fetch all unaccounted blocks * @param order string Sort order, default ASC From 7c1d51cc7a33560318f535229a3f2960ad6123af Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 22:40:51 +0200 Subject: [PATCH 038/168] Add API call for getuserstatus Fixes #126, see Github Wiki for documentation --- .../include/pages/api/getuserstatus.inc.php | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 public/include/pages/api/getuserstatus.inc.php diff --git a/public/include/pages/api/getuserstatus.inc.php b/public/include/pages/api/getuserstatus.inc.php new file mode 100644 index 00000000..c91ade94 --- /dev/null +++ b/public/include/pages/api/getuserstatus.inc.php @@ -0,0 +1,29 @@ +checkApiKey($_REQUEST['api_key']); + +// We have to check if that user is admin too +if ( ! $user->isAdmin($id) ) { + header("HTTP/1.1 401 Unauthorized"); + die("Access denied"); +} + +// Is it a username or a user ID +ctype_digit($_REQUEST['id']) ? $username = $user->getUserName($_REQUEST['id']) : $username = $_REQUEST['id']; +ctype_digit($_REQUEST['id']) ? $id = $_REQUEST['id'] : $id = $user->getUserId($_REQUEST['id']); + +// Output JSON format +echo json_encode(array('getuserstatus' => array( + 'username' => $username, + 'shares' => $statistics->getUserShares($id), + 'hashrate' => $statistics->getUserHashrate($id) +))); + +// Supress master template +$supress_master = 1; +?> From 64d8d8abf32c9a89ca1039df3eff1358e9b639f8 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 21:37:14 +0200 Subject: [PATCH 039/168] Adding support for PPS payout method This commit changed a few things in the backend and classes code: * Any _PPS transaction does NOT need to be confirmed * Queries updated for added _PPS transactions * Template updated to properly display these transactions Cronjob * Added pps_payput cron to run payouts based on worker submitted shares * **IMPORTANT**: Can NOT be run with proportional_payout! Addresses #70 --- cronjobs/pps_payout.php | 87 +++++++++++++++++++ public/include/classes/share.class.php | 15 ++++ public/include/classes/transaction.class.php | 18 ++-- .../mmcFE/account/transactions/default.tpl | 5 +- 4 files changed, 118 insertions(+), 7 deletions(-) create mode 100755 cronjobs/pps_payout.php diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php new file mode 100755 index 00000000..a71c42a3 --- /dev/null +++ b/cronjobs/pps_payout.php @@ -0,0 +1,87 @@ +#!/usr/bin/php +can_connect() === true ){ + $dDifficulty = $bitcoin->getdifficulty(); +} else { + verbose("Aborted: " . $bitcoin->can_connect() . "\n"); + exit(1); +} + +// Value per share calculation +$pps_value = 50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']); + +// Find our last share accounted and last inserted share for PPS calculations +$iPreviousShareId = $setting->getValue('pps_last_share_id'); +$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\t\tPayout\t\tDonation\tFee\t\tStatus\n"); + +foreach ($aAccountShares as $aData) { + // Take our valid shares and multiply by per share value + $aData['payout'] = $aData['valid'] * $pps_value; + + // Defaults + $aData['fee' ] = 0; + $aData['donation'] = 0; + + // Calculate block fees + if ($config['fees'] > 0) + $aData['fee'] = number_format(round($config['fees'] / 100 * $aData['payout'], 8), 8); + // 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" . + $aData['username'] . "\t" . + $aData['invalid'] . "\t" . + $aData['valid'] . "\t*\t" . + $pps_value . "\t=\t" . + $aData['payout'] . "\t" . + $aData['donation'] . "\t" . + $aData['fee'] . "\t"); + + $strStatus = "OK"; + // Add new credit transaction + if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit_PPS')) + $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_PPS')) + $strStatus = "Fee Failed"; + // Add new donation debit + if ($aData['donation'] > 0) + if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation_PPS')) + $strStatus = "Donation Failed"; + verbose($strStatus . "\n"); +} + +// Store our last inserted ID for the next run +$setting->setValue('pps_last_share_id', $iLastShareId); + +verbose("\n\n------------------------------------------------------------------------------------\n\n"); +?> diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index b0a3b249..1033f92c 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -44,6 +44,21 @@ class Share { return $this->table; } + /** + * Get last inserted Share ID from Database + * Used for PPS calculations without moving to archive + **/ + public function getLastInsertedShareId() { + $stmt = $this->mysqli->prepare(" + SELECT MAX(id) AS id FROM $this->table + "); + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_object()->id; + // Catchall + $this->setErrorMessage('Failed to fetch last inserted share ID'); + return false; + } + /** * Get all valid shares for this round * @param previous_upstream int Previous found share accepted by upstream to limit results diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index 020140d3..711e893f 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -142,7 +142,7 @@ class Transaction { ( SELECT sum(t.amount) AS credit FROM $this->table AS t - WHERE t.type = 'Credit' + WHERE t.type IN ('Credit', 'Credit_PPS') ) AS t1, ( SELECT sum(t.amount) AS debit @@ -152,7 +152,7 @@ class Transaction { ( SELECT sum(t.amount) AS other FROM " . $this->table . " AS t - WHERE t.type IN ('Donation','Fee') + WHERE t.type IN ('Donation','Fee','Donation_PPS','Fee_PPS') ) AS t3"); if ($this->checkStmt($stmt) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch()) return $dBalance; @@ -176,8 +176,11 @@ class Transaction { 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 = 'Credit' - AND b.confirmations >= ? + WHERE + ( + ( t.type = 'Credit' AND b.confirmations >= ? ) OR + ( t.type = 'Credit_PPS' ) + ) AND t.account_id = ? ) AS t1, ( @@ -190,8 +193,11 @@ class Transaction { 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 >= ? + WHERE + ( + ( t.type IN ('Donation','Fee') AND b.confirmations >= ? ) OR + ( t.type IN ('Donation_PPS', 'Fee_PPS') ) + ) AND t.account_id = ? ) AS t3 "); diff --git a/public/templates/mmcFE/account/transactions/default.tpl b/public/templates/mmcFE/account/transactions/default.tpl index 8f5282e1..dd31723f 100644 --- a/public/templates/mmcFE/account/transactions/default.tpl +++ b/public/templates/mmcFE/account/transactions/default.tpl @@ -18,6 +18,9 @@ ($TRANSACTIONS[transaction].type == 'Credit' 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' )} @@ -27,7 +30,7 @@ {$TRANSACTIONS[transaction].type} {$TRANSACTIONS[transaction].coin_address} {if $TRANSACTIONS[transaction].height == 0}n/a{else}{$TRANSACTIONS[transaction].height}{/if} - {$TRANSACTIONS[transaction].amount} + {$TRANSACTIONS[transaction].amount} {/if} {/section} From 6a8979d20bc9d7dc0cf9bf64bfc85c3a09bdb775 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:23:54 +0200 Subject: [PATCH 040/168] Fixing critical issue with manual- and autopayouts This is a proposed fix for #128: * Mark auto_payout running via DB setting, unlock when done * Just before actually sending money, check for running cron Please refer to the ticket for details --- cronjobs/auto_payout.php | 8 ++++++++ public/include/pages/account/edit.inc.php | 7 ++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/cronjobs/auto_payout.php b/cronjobs/auto_payout.php index 68b60aa4..36c8a873 100755 --- a/cronjobs/auto_payout.php +++ b/cronjobs/auto_payout.php @@ -27,6 +27,9 @@ if ($bitcoin->can_connect() !== true) { exit(1); } +// Mark this job as active +$setting->setValue('auto_payout_active', 1); + // Fetch all users with setup AP $users = $user->getAllAutoPayout(); @@ -69,3 +72,8 @@ if (! empty($users)) { } else { verbose("No user has configured their AP > 0\n"); } + +// Mark this job as inactive +$setting->setValue('auto_payout_active', 0); + +?> diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php index 84937115..1e2941bb 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -28,7 +28,12 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST if ($continue == true) { // Send balance to address, mind 0.1 fee for transaction! try { - $bitcoin->sendtoaddress($sCoinAddress, $dBalance); + if ($setting->getValue('auto_payout_active') == 0) { + $bitcoin->sendtoaddress($sCoinAddress, $dBalance); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'Auto-payout active, please contact site support immidiately to revoke invalid transactions.', 'TYPE' => 'errormsg'); + $continue = false; + } } catch (BitcoinClientException $e) { $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to send LTC, please contact site support immidiately', 'TYPE' => 'errormsg'); $continue = false; From a3ddf0cfcca9b80a1e3e4d4c434f53fe1653eb6a Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:31:35 +0200 Subject: [PATCH 041/168] properly format payout and round it --- 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 a71c42a3..1dafa288 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -44,7 +44,7 @@ verbose("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\t\tPayout\t\tDonation\tFee\ foreach ($aAccountShares as $aData) { // Take our valid shares and multiply by per share value - $aData['payout'] = $aData['valid'] * $pps_value; + $aData['payout'] = number_format(round($aData['valid'] * $pps_value, 8)); // Defaults $aData['fee' ] = 0; From ad6051df1c0682addb4edebca39c414edee99b15 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:34:02 +0200 Subject: [PATCH 042/168] forgot 8 decimals --- 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 1dafa288..bef78f2a 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -44,7 +44,7 @@ verbose("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\t\tPayout\t\tDonation\tFee\ foreach ($aAccountShares as $aData) { // Take our valid shares and multiply by per share value - $aData['payout'] = number_format(round($aData['valid'] * $pps_value, 8)); + $aData['payout'] = number_format(round($aData['valid'] * $pps_value, 8), 8); // Defaults $aData['fee' ] = 0; From 663c427d4adaf8d96d51e98024c1dd19e83187e4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:45:53 +0200 Subject: [PATCH 043/168] properly format pps value to 12 digits --- 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 bef78f2a..e2804595 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -31,7 +31,7 @@ if ( $bitcoin->can_connect() === true ){ } // Value per share calculation -$pps_value = 50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']); +$pps_value = number_format(round(50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); // Find our last share accounted and last inserted share for PPS calculations $iPreviousShareId = $setting->getValue('pps_last_share_id'); From 87e721edb89429c2d96af17858d83e8712e9339d Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:48:09 +0200 Subject: [PATCH 044/168] moved table header to the left --- 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 e2804595..427035d5 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -40,7 +40,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\t\tPayout\t\tDonation\tFee\t\tStatus\n"); +verbose("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\tPayout\t\tDonation\tFee\t\tStatus\n"); foreach ($aAccountShares as $aData) { // Take our valid shares and multiply by per share value From 84f48efa41cd81ed5c4b7e1cb03457509fd5630c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 7 Jun 2013 00:51:25 +0200 Subject: [PATCH 045/168] minor template update for contributor hashrates --- .../mmcFE/statistics/pool/contributors_hashrate.tpl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl index b97c1515..232a5ab0 100644 --- a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl +++ b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl @@ -13,11 +13,12 @@ {assign var=rank value=1} {assign var=listed value=0} {section contrib $CONTRIBHASHES} + {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].hashrate|number_format} - {math equation="round(reward / ( diff * pow(2,32) / ( hashrate * 1000 ) / 3600 / 24), 3)" diff=$DIFFICULTY reward=$REWARD hashrate=$CONTRIBHASHES[contrib].hashrate} + {$estday|number_format:"3"} {/section} {if $listed != 1} @@ -25,7 +26,7 @@ n/a {$GLOBAL.userdata.username} {$GLOBAL.userdata.hashrate} - {math equation="round(reward / ( diff * pow(2,32) / ( hashrate * 1000 ) / 3600 / 24), 3)" diff=$DIFFICULTY reward=$REWARD hashrate=$GLOBAL.userdata.hashrate} + {$estday|number_format:"3"} {/if} From 1046bd113de324ea6a6f5434a7e787c8b610e0d1 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 7 Jun 2013 00:54:24 +0200 Subject: [PATCH 046/168] minor template update for contributor shares --- public/templates/mmcFE/statistics/pool/contributors_shares.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/templates/mmcFE/statistics/pool/contributors_shares.tpl b/public/templates/mmcFE/statistics/pool/contributors_shares.tpl index 04e9c3e0..444effa4 100644 --- a/public/templates/mmcFE/statistics/pool/contributors_shares.tpl +++ b/public/templates/mmcFE/statistics/pool/contributors_shares.tpl @@ -22,7 +22,7 @@ n/a {$GLOBAL.userdata.username} - {$GLOBAL.userdata.shares.valid} + {$GLOBAL.userdata.shares.valid|number_format} {/if} From f63485a53939333ae7ce2a1f7de960683ece890d Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 7 Jun 2013 08:33:14 +0200 Subject: [PATCH 047/168] Fixing admin panel user display * Fixes #130. Removed total shares in favor of the hashrate. * Fixes hashrate display. * Added pagination template file to include pagination on other pages in the future. --- public/include/classes/statistics.class.php | 2 +- public/templates/mmcFE/admin/user/default.tpl | 21 ++----------------- public/templates/mmcFE/global/pagination.tpl | 15 +++++++++++++ 3 files changed, 18 insertions(+), 20 deletions(-) create mode 100644 public/templates/mmcFE/global/pagination.tpl diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index 4ed74bb4..f3924f13 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -189,13 +189,13 @@ class Statistics { a.username AS username, a.donate_percent AS donate_percent, a.email AS email, - COUNT(s.id) AS shares, ROUND(COUNT(s.id) * POW(2," . $this->config['difficulty'] . ") / 600 / 1000,2) AS hashrate FROM " . $this->user->getTableName() . " AS a LEFT JOIN " . $this->share->getTableName() . " AS s ON a.username = SUBSTRING_INDEX( s.username, '.', 1 ) WHERE a.username LIKE ? + AND s.time > DATE_SUB(now(), INTERVAL 10 MINUTE) GROUP BY username ORDER BY username "); diff --git a/public/templates/mmcFE/admin/user/default.tpl b/public/templates/mmcFE/admin/user/default.tpl index 5386a84f..92809395 100644 --- a/public/templates/mmcFE/admin/user/default.tpl +++ b/public/templates/mmcFE/admin/user/default.tpl @@ -9,21 +9,7 @@ {include file="global/block_header.tpl" BLOCK_HEADER="User Information"}
    -
    -
    - - - - - - -
    -
    +{include file="global/pagination.tpl"}
    @@ -32,7 +18,6 @@ - @@ -45,8 +30,7 @@ - - + @@ -66,7 +50,6 @@ - diff --git a/public/templates/mmcFE/global/pagination.tpl b/public/templates/mmcFE/global/pagination.tpl new file mode 100644 index 00000000..0bae6db5 --- /dev/null +++ b/public/templates/mmcFE/global/pagination.tpl @@ -0,0 +1,15 @@ +
    +
    + + + + + + + +
    From 4b05846a78eb9ac0fd321d8c6a14c0e3f20a9ff7 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 7 Jun 2013 08:42:07 +0200 Subject: [PATCH 048/168] Re-Adding shares for admin user panel * Re-enables estimations * Still fixes #130 which had issues with hashrates, those are still fixed --- public/include/classes/statistics.class.php | 3 ++- public/templates/mmcFE/admin/user/default.tpl | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index f3924f13..b86acd72 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -189,7 +189,8 @@ class Statistics { a.username AS username, a.donate_percent AS donate_percent, a.email AS email, - ROUND(COUNT(s.id) * POW(2," . $this->config['difficulty'] . ") / 600 / 1000,2) AS hashrate + ROUND(COUNT(s.id) * POW(2," . $this->config['difficulty'] . ") / 600 / 1000,2) AS hashrate, + ( SELECT COUNT(id) FROM " . $this->share->getTableName() . " WHERE a.username = SUBSTRING_INDEX( s.username, '.', 1 ) ) AS shares FROM " . $this->user->getTableName() . " AS a LEFT JOIN " . $this->share->getTableName() . " AS s ON a.username = SUBSTRING_INDEX( s.username, '.', 1 ) diff --git a/public/templates/mmcFE/admin/user/default.tpl b/public/templates/mmcFE/admin/user/default.tpl index 92809395..86c52e48 100644 --- a/public/templates/mmcFE/admin/user/default.tpl +++ b/public/templates/mmcFE/admin/user/default.tpl @@ -17,6 +17,7 @@ + @@ -30,6 +31,7 @@ + @@ -49,6 +51,7 @@ + From 671a2d01cada9a72512de90963c3cafd27c6764e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 7 Jun 2013 09:02:58 +0200 Subject: [PATCH 049/168] Properly display both shares and hashrates * As proposed in #132 thi allows for both shares and hashsrates Fixes #132. --- public/include/classes/statistics.class.php | 6 ++---- public/include/pages/admin/user.inc.php | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index b86acd72..a63416dd 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -189,14 +189,12 @@ class Statistics { a.username AS username, a.donate_percent AS donate_percent, a.email AS email, - ROUND(COUNT(s.id) * POW(2," . $this->config['difficulty'] . ") / 600 / 1000,2) AS hashrate, - ( SELECT COUNT(id) FROM " . $this->share->getTableName() . " WHERE a.username = SUBSTRING_INDEX( s.username, '.', 1 ) ) AS shares + COUNT(s.id) AS shares FROM " . $this->user->getTableName() . " AS a LEFT JOIN " . $this->share->getTableName() . " AS s ON a.username = SUBSTRING_INDEX( s.username, '.', 1 ) WHERE - a.username LIKE ? - AND s.time > DATE_SUB(now(), INTERVAL 10 MINUTE) + a.username LIKE ? GROUP BY username ORDER BY username "); diff --git a/public/include/pages/admin/user.inc.php b/public/include/pages/admin/user.inc.php index cc961f44..b6dab89b 100644 --- a/public/include/pages/admin/user.inc.php +++ b/public/include/pages/admin/user.inc.php @@ -21,6 +21,7 @@ if ($_POST['query']) { // Query against the stats table? Currently cached though. foreach ($aUsers as $iKey => $aUser) { $aUser['balance'] = $transaction->getBalance($aUser['id']); + $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); From bf225fb0de230c8425755b0abce0266e9772bf60 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 14:25:36 +0200 Subject: [PATCH 050/168] Add worker montioring to template and worker class * Add configurable monitoring for workers into template * Store monitoring status in DB First commit for #116 --- public/include/classes/worker.class.php | 18 ++++++++++-------- .../mmcFE/account/workers/default.tpl | 2 ++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/public/include/classes/worker.class.php b/public/include/classes/worker.class.php index 5a5c19c4..6052eaac 100644 --- a/public/include/classes/worker.class.php +++ b/public/include/classes/worker.class.php @@ -43,17 +43,19 @@ class Worker { public function updateWorkers($account_id, $data) { $this->debug->append("STA " . __METHOD__, 4); $username = $this->user->getUserName($account_id); + $iFailed = 0; foreach ($data as $key => $value) { // Prefix the WebUser to Worker name $value['username'] = "$username." . $value['username']; - $stmt = $this->mysqli->prepare("UPDATE $this->table SET password = ?, username = ? WHERE account_id = ? AND id = ?"); - if ($this->checkStmt($stmt)) { - if (!$stmt->bind_param('ssii', $value['password'], $value['username'], $account_id, $key)) return false; - if (!$stmt->execute()) return false; - $stmt->close(); - } + $stmt = $this->mysqli->prepare("UPDATE $this->table SET password = ?, username = ?, monitor = ? WHERE account_id = ? AND id = ?"); + if ( ! ( $this->checkStmt($stmt) && $stmt->bind_param('ssiii', $value['password'], $value['username'], $value['monitor'], $account_id, $key) && $stmt->execute()) ) + $iFailed++; } - return true; + if ($iFailed == 0) + return true; + // Catchall + $this->setErrorMessage('Failed to update ' . $iFailed . ' worker.'); + return false; } /** @@ -64,7 +66,7 @@ class Worker { public function getWorkers($account_id) { $this->debug->append("STA " . __METHOD__, 4); $stmt = $this->mysqli->prepare(" - SELECT id, username, password, + SELECT id, username, password, monitor, ( SELECT SIGN(COUNT(id)) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS active, ( SELECT ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ")/600/1000) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS hashrate FROM $this->table diff --git a/public/templates/mmcFE/account/workers/default.tpl b/public/templates/mmcFE/account/workers/default.tpl index f1b6e875..612bd227 100644 --- a/public/templates/mmcFE/account/workers/default.tpl +++ b/public/templates/mmcFE/account/workers/default.tpl @@ -10,6 +10,7 @@ + @@ -20,6 +21,7 @@ {$username.0|escape}. + From d7e65c7b3797a15d7bc3fbd86ab23852989a2961 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 7 Jun 2013 14:42:29 +0200 Subject: [PATCH 051/168] Progress on notifications * Added main mail class * notification class extends mail class * Added mail template for IDLE workers * Added notification table to SQL structure This works already but once notified the status is not reset as soon as a worker is active again. Need to think of a system to do that automatically. Addresses #116 --- cronjobs/notifications.php | 42 ++++++++++++++ public/include/autoloader.inc.php | 2 + public/include/classes/mail.class.php | 58 +++++++++++++++++++ public/include/classes/notification.class.php | 45 ++++++++++++++ public/include/classes/worker.class.php | 20 +++++++ public/templates/mail/idle_worker.tpl | 9 +++ sql/mmcfe_ng_structure.sql | 20 ++++++- 7 files changed, 193 insertions(+), 3 deletions(-) create mode 100755 cronjobs/notifications.php create mode 100644 public/include/classes/mail.class.php create mode 100644 public/include/classes/notification.class.php create mode 100644 public/templates/mail/idle_worker.tpl diff --git a/cronjobs/notifications.php b/cronjobs/notifications.php new file mode 100755 index 00000000..7bca08ec --- /dev/null +++ b/cronjobs/notifications.php @@ -0,0 +1,42 @@ +#!/usr/bin/php +getAllIdleWorkers(); +if (empty($aWorkers)) { + verbose("No idle workers found\n"); + exit; +} + +foreach ($aWorkers as $aWorker) { + $aData = $aWorker; + $aData['username'] = $user->getUserName($aWorker['account_id']); + $aData['email'] = $user->getUserEmail($aData['username']); + if (!$notification->isNotified($aData)) { + if (!$notification->addNotification('idle_worker', $aData) && $notification->sendMail('sebastian@grewe.ca', 'idle_worker', $aData)) + verbose("Unable to send notification: " . $notification->getError() . "\n"); + } else { + verbose("Already notified for this worker\n"); + } +} +?> diff --git a/public/include/autoloader.inc.php b/public/include/autoloader.inc.php index ea16564e..a78d4a62 100644 --- a/public/include/autoloader.inc.php +++ b/public/include/autoloader.inc.php @@ -14,3 +14,5 @@ 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 . '/setting.class.php'); +require_once(CLASS_DIR . '/mail.class.php'); +require_once(CLASS_DIR . '/notification.class.php'); diff --git a/public/include/classes/mail.class.php b/public/include/classes/mail.class.php new file mode 100644 index 00000000..6c7cc0f1 --- /dev/null +++ b/public/include/classes/mail.class.php @@ -0,0 +1,58 @@ +debug = $debug; + } + public function setMysql($mysqli) { + $this->mysqli = $mysqli; + } + public function setSmarty($smarty) { + $this->smarty = $smarty; + } + public function setConfig($config) { + $this->config = $config; + } + + private function checkStmt($bState) { + $this->debug->append("STA " . __METHOD__, 4); + if ($bState ===! true) { + $this->debug->append("Failed to prepare statement: " . $this->mysqli->error); + $this->setErrorMessage('Internal application Error'); + return false; + } + return true; + } + + public function sendMail($email, $template, $vars) { + $this->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, + $this->smarty->fetch(BASEPATH . 'templates/mail/subject.tpl'), + $this->smarty->fetch(BASEPATH . 'templates/mail/' . $template . '.tpl'), + $headers)) { + return true; + } else { + $this->setErrorMessage("Unable to send mail"); + return false; + } + return false; + } +} + +// Make our class available automatically +$mail = new Mail (); +$mail->setDebug($debug); +$mail->setMysql($mysqli); +$mail->setSmarty($smarty); +$mail->setConfig($config); + +?> diff --git a/public/include/classes/notification.class.php b/public/include/classes/notification.class.php new file mode 100644 index 00000000..a9eca1af --- /dev/null +++ b/public/include/classes/notification.class.php @@ -0,0 +1,45 @@ +mysqli->prepare("SELECT id FROM $this->table WHERE data = ? LIMIT 1"); + if ($stmt && $stmt->bind_param('s', $data) && $stmt->execute() && $stmt->store_result() && $stmt->num_rows == 1) + return true; + // Catchall + // Does not seem to have a notification set + return false; + } + + /** + * Add a new notification to the table + * @param type string Type of the notification + * @return bool + **/ + public function addNotification($type, $data) { + // Store notification data as json + $data = json_encode($data); + $stmt = $this->mysqli->prepare("INSERT INTO $this->table (type, data) VALUES (?,?)"); + if ($stmt && $stmt->bind_param('ss', $type, $data) && $stmt->execute()) + return true; + $this->debug->append("Failed to add notification for $type with $data: " . $this->mysqli->error); + $this->setErrorMessage("Unable to add new notification"); + return false; + } +} + +$notification = new Notification(); +$notification->setDebug($debug); +$notification->setMysql($mysqli); +$notification->setSmarty($smarty); +$notification->setConfig($config); diff --git a/public/include/classes/worker.class.php b/public/include/classes/worker.class.php index 6052eaac..a5052ba9 100644 --- a/public/include/classes/worker.class.php +++ b/public/include/classes/worker.class.php @@ -58,6 +58,26 @@ class Worker { return false; } + /** + * Fetch all IDLE workers that have monitoring enabled + * @param none + * @return data array Workers in IDLE state and monitoring enabled + **/ + public function getAllIdleWorkers() { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare(" + SELECT account_id, id, username + FROM " . $this->table . " + WHERE monitor = 1 AND ( SELECT SIGN(COUNT(id)) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) = 0"); + + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_all(MYSQLI_ASSOC); + // Catchall + $this->setErrorMessage("Unable to fetch IDLE, monitored workers"); + echo $this->mysqli->error; + return false; + } + /** * Fetch all workers for an account * @param account_id int User ID diff --git a/public/templates/mail/idle_worker.tpl b/public/templates/mail/idle_worker.tpl new file mode 100644 index 00000000..a21aaac3 --- /dev/null +++ b/public/templates/mail/idle_worker.tpl @@ -0,0 +1,9 @@ + + +

    One of your workers is currently IDLE.

    +

    Since monitoring is enabled for this worker, this notification was sent.

    +

    Please check your workers!

    +
    +
    + + diff --git a/sql/mmcfe_ng_structure.sql b/sql/mmcfe_ng_structure.sql index c3c626bd..a971d455 100644 --- a/sql/mmcfe_ng_structure.sql +++ b/sql/mmcfe_ng_structure.sql @@ -3,7 +3,7 @@ -- http://www.phpmyadmin.net -- -- Host: localhost --- Generation Time: Jun 06, 2013 at 09:01 PM +-- Generation Time: Jun 07, 2013 at 02:42 PM -- Server version: 5.5.31-0ubuntu0.13.04.1 -- PHP Version: 5.4.9-4ubuntu2 @@ -69,6 +69,20 @@ CREATE TABLE IF NOT EXISTS `blocks` ( -- -------------------------------------------------------- +-- +-- Table structure for table `notifications` +-- + +CREATE TABLE IF NOT EXISTS `notifications` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `type` varchar(25) NOT NULL, + `data` text NOT NULL, + `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + -- -- Table structure for table `pool_worker` -- @@ -136,7 +150,7 @@ CREATE TABLE IF NOT EXISTS `shares_archive` ( PRIMARY KEY (`id`), UNIQUE KEY `share_id` (`share_id`), KEY `time` (`time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes'; -- -------------------------------------------------------- @@ -153,7 +167,7 @@ CREATE TABLE IF NOT EXISTS `statistics_shares` ( PRIMARY KEY (`id`), KEY `account_id` (`account_id`), KEY `block_id` (`block_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -------------------------------------------------------- From 4966f64a5912698694297d3a9738575d05999af3 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 7 Jun 2013 15:07:26 +0200 Subject: [PATCH 052/168] Adding notification reset once worker is active Go through all active notifications (active means they are not notified again) and check if their state has changed. If so, mark as inactive and allow for re-notification of the same type and data. --- cronjobs/notifications.php | 34 +++++++++++------ public/include/classes/mail.class.php | 2 +- public/include/classes/notification.class.php | 38 ++++++++++++++++++- public/include/classes/worker.class.php | 21 ++++++++++ 4 files changed, 80 insertions(+), 15 deletions(-) diff --git a/cronjobs/notifications.php b/cronjobs/notifications.php index 7bca08ec..e70d2b07 100755 --- a/cronjobs/notifications.php +++ b/cronjobs/notifications.php @@ -22,21 +22,31 @@ limitations under the License. // Include all settings and classes require_once('shared.inc.php'); +// Find all IDLE workers $aWorkers = $worker->getAllIdleWorkers(); if (empty($aWorkers)) { verbose("No idle workers found\n"); - exit; -} - -foreach ($aWorkers as $aWorker) { - $aData = $aWorker; - $aData['username'] = $user->getUserName($aWorker['account_id']); - $aData['email'] = $user->getUserEmail($aData['username']); - if (!$notification->isNotified($aData)) { - if (!$notification->addNotification('idle_worker', $aData) && $notification->sendMail('sebastian@grewe.ca', 'idle_worker', $aData)) - verbose("Unable to send notification: " . $notification->getError() . "\n"); - } else { - verbose("Already notified for this worker\n"); +} else { + foreach ($aWorkers as $aWorker) { + $aData = $aWorker; + $aData['username'] = $user->getUserName($aWorker['account_id']); + $aData['email'] = $user->getUserEmail($aData['username']); + if (!$notification->isNotified($aData)) { + if (!$notification->addNotification('idle_worker', $aData) && $notification->sendMail('sebastian@grewe.ca', 'idle_worker', $aData)) + verbose("Unable to send notification: " . $notification->getError() . "\n"); + } else { + verbose("Already notified for this worker\n"); + } } } +// We notified, lets check which recovered +$aNotifications = $notification->getAllActive(); +foreach ($aNotifications as $aNotification) { + $aData = json_decode($aNotification['data'], true); + $aWorker = $worker->getWorker($aData['id']); + if ($aWorker['active'] == 1) + if (!$notification->setInactive($aNotification['id'])) + verbose("Failed to set notification inactive for " . $aWorker['username'] . "\n"); +} + ?> diff --git a/public/include/classes/mail.class.php b/public/include/classes/mail.class.php index 6c7cc0f1..a1cf2137 100644 --- a/public/include/classes/mail.class.php +++ b/public/include/classes/mail.class.php @@ -20,7 +20,7 @@ class Mail { $this->config = $config; } - private function checkStmt($bState) { + function checkStmt($bState) { $this->debug->append("STA " . __METHOD__, 4); if ($bState ===! true) { $this->debug->append("Failed to prepare statement: " . $this->mysqli->error); diff --git a/public/include/classes/notification.class.php b/public/include/classes/notification.class.php index a9eca1af..9a6dcf3b 100644 --- a/public/include/classes/notification.class.php +++ b/public/include/classes/notification.class.php @@ -7,13 +7,36 @@ if (!defined('SECURITY')) class Notification extends Mail { var $table = 'notifications'; + public function setInactive($id) { + $field = array( + 'name' => 'active', + 'type' => 'i', + 'value' => 0 + ); + return $this->updateSingle($id, $field); + } + + /** + * Update a single row in a table + * @param userID int Account ID + * @param field string Field to update + * @return bool + **/ + private function updateSingle($id, $field) { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare("UPDATE $this->table SET " . $field['name'] . " = ? WHERE id = ? LIMIT 1"); + if ($this->checkStmt($stmt) && $stmt->bind_param($field['type'].'i', $field['value'], $id) && $stmt->execute()) + return true; + $this->debug->append("Unable to update " . $field['name'] . " with " . $field['value'] . " for ID $id"); + return false; + } /** * We check our notification table for existing data * so we can avoid duplicate entries **/ public function isNotified($aData) { $data = json_encode($aData); - $stmt = $this->mysqli->prepare("SELECT id FROM $this->table WHERE data = ? LIMIT 1"); + $stmt = $this->mysqli->prepare("SELECT id FROM $this->table WHERE data = ? AND active = 1 LIMIT 1"); if ($stmt && $stmt->bind_param('s', $data) && $stmt->execute() && $stmt->store_result() && $stmt->num_rows == 1) return true; // Catchall @@ -21,6 +44,17 @@ class Notification extends Mail { return false; } + /** + * Get all active notifications + **/ + public function getAllActive() { + $stmt =$this->mysqli->prepare("SELECT id, data FROM $this->table WHERE active = 1 LIMIT 1"); + if ($stmt && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_all(MYSQLI_ASSOC); + // Catchall + return false; + } + /** * Add a new notification to the table * @param type string Type of the notification @@ -29,7 +63,7 @@ class Notification extends Mail { public function addNotification($type, $data) { // Store notification data as json $data = json_encode($data); - $stmt = $this->mysqli->prepare("INSERT INTO $this->table (type, data) VALUES (?,?)"); + $stmt = $this->mysqli->prepare("INSERT INTO $this->table (type, data, active) VALUES (?,?,1)"); if ($stmt && $stmt->bind_param('ss', $type, $data) && $stmt->execute()) return true; $this->debug->append("Failed to add notification for $type with $data: " . $this->mysqli->error); diff --git a/public/include/classes/worker.class.php b/public/include/classes/worker.class.php index a5052ba9..6a9f6524 100644 --- a/public/include/classes/worker.class.php +++ b/public/include/classes/worker.class.php @@ -78,6 +78,27 @@ class Worker { return false; } + /** + * Fetch a specific worker and its status + * @param id int Worker ID + * @return mixed array Worker details + **/ + public function getWorker($id) { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare(" + SELECT id, username, password, monitor, + ( SELECT SIGN(COUNT(id)) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS active, + ( SELECT ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ")/600/1000) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS hashrate + FROM $this->table + WHERE id = ? + "); + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $id) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_assoc(); + // Catchall + echo $this->mysqli->error; + return false; + } + /** * Fetch all workers for an account * @param account_id int User ID From 432540335fd966ee000b385b5082fa3e71a59be1 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 7 Jun 2013 15:35:58 +0200 Subject: [PATCH 053/168] Working notification system * Added things to mail templates * Modified user password reset call for new mail template * Added BASEPATH to smarty code to ensure templates are compiled in the proper directory * Updated mail and notification class * Updated notification cron * Added notification cron to run-cron list --- cronjobs/notifications.php | 23 +++++++++++++------ cronjobs/run-crons.sh | 2 +- public/include/classes/mail.class.php | 11 +++++++-- public/include/classes/notification.class.php | 1 + public/include/classes/user.class.php | 1 + public/include/smarty.inc.php | 6 ++--- public/templates/mail/idle_worker.tpl | 2 +- public/templates/mail/subject.tpl | 2 +- 8 files changed, 33 insertions(+), 15 deletions(-) diff --git a/cronjobs/notifications.php b/cronjobs/notifications.php index e70d2b07..45bda82a 100755 --- a/cronjobs/notifications.php +++ b/cronjobs/notifications.php @@ -30,23 +30,32 @@ if (empty($aWorkers)) { foreach ($aWorkers as $aWorker) { $aData = $aWorker; $aData['username'] = $user->getUserName($aWorker['account_id']); + $aData['subject'] = 'IDLE Worker : ' . $aWorker['username']; $aData['email'] = $user->getUserEmail($aData['username']); - if (!$notification->isNotified($aData)) { - if (!$notification->addNotification('idle_worker', $aData) && $notification->sendMail('sebastian@grewe.ca', 'idle_worker', $aData)) - verbose("Unable to send notification: " . $notification->getError() . "\n"); - } else { - verbose("Already notified for this worker\n"); + if ( $notification->isNotified($aData) ) { + verbose("Worker already notified\n"); + continue; } + if ($notification->addNotification('idle_worker', $aData) && $notification->sendMail($aData['email'], 'idle_worker', $aData)) { + verbose ("Notified " . $aData['email'] . " for IDLE worker " . $aWorker['username'] . "\n"); + } else { + verbose("Unable to send notification: " . $notification->getError() . "\n"); + } } } + // We notified, lets check which recovered $aNotifications = $notification->getAllActive(); foreach ($aNotifications as $aNotification) { $aData = json_decode($aNotification['data'], true); $aWorker = $worker->getWorker($aData['id']); - if ($aWorker['active'] == 1) - if (!$notification->setInactive($aNotification['id'])) + if ($aWorker['active'] == 1) { + if ($notification->setInactive($aNotification['id'])) { + verbose("Marked notification " . $aNotification['id'] . " as inactive\n"); + } else { verbose("Failed to set notification inactive for " . $aWorker['username'] . "\n"); + } + } } ?> diff --git a/cronjobs/run-crons.sh b/cronjobs/run-crons.sh index 730a9063..5f9179dc 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 blockupdate.php auto_payout.php tickerupdate.php" +CRONS="findblock.php proportional_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php" # Additional arguments to pass to cronjobs CRONARGS="-v" diff --git a/public/include/classes/mail.class.php b/public/include/classes/mail.class.php index a1cf2137..2800500c 100644 --- a/public/include/classes/mail.class.php +++ b/public/include/classes/mail.class.php @@ -19,7 +19,12 @@ class Mail { public function setConfig($config) { $this->config = $config; } - + public function setErrorMessage($msg) { + $this->sError = $msg; + } + public function getError() { + return $this->sError; + } function checkStmt($bState) { $this->debug->append("STA " . __METHOD__, 4); if ($bState ===! true) { @@ -30,8 +35,10 @@ class Mail { return true; } - public function sendMail($email, $template, $vars) { + public function sendMail($email, $template, $aData) { $this->smarty->assign('WEBSITENAME', $this->config['website']['name']); + $this->smarty->assign('SUBJECT', $aData['subject']); + $this->smarty->assign('DATA', $aData); $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"; diff --git a/public/include/classes/notification.class.php b/public/include/classes/notification.class.php index 9a6dcf3b..983db437 100644 --- a/public/include/classes/notification.class.php +++ b/public/include/classes/notification.class.php @@ -41,6 +41,7 @@ class Notification extends Mail { return true; // Catchall // Does not seem to have a notification set + $this->setErrorMessage("Unable to run query: " . $this->mysqli->error); return false; } diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 8db17a1e..b4627729 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -486,6 +486,7 @@ class User { } $smarty->assign('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"; diff --git a/public/include/smarty.inc.php b/public/include/smarty.inc.php index b2e67f37..5668e1d3 100644 --- a/public/include/smarty.inc.php +++ b/public/include/smarty.inc.php @@ -16,10 +16,10 @@ $smarty = new Smarty; // Assign our local paths $debug->append('Define Smarty Paths', 3); -$smarty->template_dir = 'templates/' . THEME . '/'; -$smarty->compile_dir = 'templates/compile/'; +$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->cache_dir = "templates/cache"; +$smarty->cache_dir = BASEPATH . "templates/cache"; ?> diff --git a/public/templates/mail/idle_worker.tpl b/public/templates/mail/idle_worker.tpl index a21aaac3..6d1c282c 100644 --- a/public/templates/mail/idle_worker.tpl +++ b/public/templates/mail/idle_worker.tpl @@ -1,6 +1,6 @@ -

    One of your workers is currently IDLE.

    +

    One of your workers is currently IDLE: {$DATA.username}

    Since monitoring is enabled for this worker, this notification was sent.

    Please check your workers!


    diff --git a/public/templates/mail/subject.tpl b/public/templates/mail/subject.tpl index 665e26f5..94fd6a28 100644 --- a/public/templates/mail/subject.tpl +++ b/public/templates/mail/subject.tpl @@ -1 +1 @@ -[ {$WEBSITENAME} ] Password Reset Request +[ {$WEBSITENAME} ] {$SUBJECT} From c69fbe4fc416f93e42fd1998dc562a06830d8be0 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 14:25:36 +0200 Subject: [PATCH 054/168] Add worker montioring to template and worker class * Add configurable monitoring for workers into template * Store monitoring status in DB First commit for #116 --- public/include/classes/worker.class.php | 18 ++++++++++-------- .../mmcFE/account/workers/default.tpl | 2 ++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/public/include/classes/worker.class.php b/public/include/classes/worker.class.php index 5a5c19c4..6052eaac 100644 --- a/public/include/classes/worker.class.php +++ b/public/include/classes/worker.class.php @@ -43,17 +43,19 @@ class Worker { public function updateWorkers($account_id, $data) { $this->debug->append("STA " . __METHOD__, 4); $username = $this->user->getUserName($account_id); + $iFailed = 0; foreach ($data as $key => $value) { // Prefix the WebUser to Worker name $value['username'] = "$username." . $value['username']; - $stmt = $this->mysqli->prepare("UPDATE $this->table SET password = ?, username = ? WHERE account_id = ? AND id = ?"); - if ($this->checkStmt($stmt)) { - if (!$stmt->bind_param('ssii', $value['password'], $value['username'], $account_id, $key)) return false; - if (!$stmt->execute()) return false; - $stmt->close(); - } + $stmt = $this->mysqli->prepare("UPDATE $this->table SET password = ?, username = ?, monitor = ? WHERE account_id = ? AND id = ?"); + if ( ! ( $this->checkStmt($stmt) && $stmt->bind_param('ssiii', $value['password'], $value['username'], $value['monitor'], $account_id, $key) && $stmt->execute()) ) + $iFailed++; } - return true; + if ($iFailed == 0) + return true; + // Catchall + $this->setErrorMessage('Failed to update ' . $iFailed . ' worker.'); + return false; } /** @@ -64,7 +66,7 @@ class Worker { public function getWorkers($account_id) { $this->debug->append("STA " . __METHOD__, 4); $stmt = $this->mysqli->prepare(" - SELECT id, username, password, + SELECT id, username, password, monitor, ( SELECT SIGN(COUNT(id)) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS active, ( SELECT ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ")/600/1000) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS hashrate FROM $this->table diff --git a/public/templates/mmcFE/account/workers/default.tpl b/public/templates/mmcFE/account/workers/default.tpl index f1b6e875..612bd227 100644 --- a/public/templates/mmcFE/account/workers/default.tpl +++ b/public/templates/mmcFE/account/workers/default.tpl @@ -10,6 +10,7 @@
    + @@ -20,6 +21,7 @@ {$username.0|escape}. + From 9ac2dadd973d1f2d465a81cb4addaec2333bc043 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 7 Jun 2013 14:42:29 +0200 Subject: [PATCH 055/168] Progress on notifications * Added main mail class * notification class extends mail class * Added mail template for IDLE workers * Added notification table to SQL structure This works already but once notified the status is not reset as soon as a worker is active again. Need to think of a system to do that automatically. Addresses #116 --- cronjobs/notifications.php | 42 ++++++++++++++ public/include/autoloader.inc.php | 2 + public/include/classes/mail.class.php | 58 +++++++++++++++++++ public/include/classes/notification.class.php | 45 ++++++++++++++ public/include/classes/worker.class.php | 20 +++++++ public/templates/mail/idle_worker.tpl | 9 +++ sql/mmcfe_ng_structure.sql | 20 ++++++- 7 files changed, 193 insertions(+), 3 deletions(-) create mode 100755 cronjobs/notifications.php create mode 100644 public/include/classes/mail.class.php create mode 100644 public/include/classes/notification.class.php create mode 100644 public/templates/mail/idle_worker.tpl diff --git a/cronjobs/notifications.php b/cronjobs/notifications.php new file mode 100755 index 00000000..7bca08ec --- /dev/null +++ b/cronjobs/notifications.php @@ -0,0 +1,42 @@ +#!/usr/bin/php +getAllIdleWorkers(); +if (empty($aWorkers)) { + verbose("No idle workers found\n"); + exit; +} + +foreach ($aWorkers as $aWorker) { + $aData = $aWorker; + $aData['username'] = $user->getUserName($aWorker['account_id']); + $aData['email'] = $user->getUserEmail($aData['username']); + if (!$notification->isNotified($aData)) { + if (!$notification->addNotification('idle_worker', $aData) && $notification->sendMail('sebastian@grewe.ca', 'idle_worker', $aData)) + verbose("Unable to send notification: " . $notification->getError() . "\n"); + } else { + verbose("Already notified for this worker\n"); + } +} +?> diff --git a/public/include/autoloader.inc.php b/public/include/autoloader.inc.php index ea16564e..a78d4a62 100644 --- a/public/include/autoloader.inc.php +++ b/public/include/autoloader.inc.php @@ -14,3 +14,5 @@ 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 . '/setting.class.php'); +require_once(CLASS_DIR . '/mail.class.php'); +require_once(CLASS_DIR . '/notification.class.php'); diff --git a/public/include/classes/mail.class.php b/public/include/classes/mail.class.php new file mode 100644 index 00000000..6c7cc0f1 --- /dev/null +++ b/public/include/classes/mail.class.php @@ -0,0 +1,58 @@ +debug = $debug; + } + public function setMysql($mysqli) { + $this->mysqli = $mysqli; + } + public function setSmarty($smarty) { + $this->smarty = $smarty; + } + public function setConfig($config) { + $this->config = $config; + } + + private function checkStmt($bState) { + $this->debug->append("STA " . __METHOD__, 4); + if ($bState ===! true) { + $this->debug->append("Failed to prepare statement: " . $this->mysqli->error); + $this->setErrorMessage('Internal application Error'); + return false; + } + return true; + } + + public function sendMail($email, $template, $vars) { + $this->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, + $this->smarty->fetch(BASEPATH . 'templates/mail/subject.tpl'), + $this->smarty->fetch(BASEPATH . 'templates/mail/' . $template . '.tpl'), + $headers)) { + return true; + } else { + $this->setErrorMessage("Unable to send mail"); + return false; + } + return false; + } +} + +// Make our class available automatically +$mail = new Mail (); +$mail->setDebug($debug); +$mail->setMysql($mysqli); +$mail->setSmarty($smarty); +$mail->setConfig($config); + +?> diff --git a/public/include/classes/notification.class.php b/public/include/classes/notification.class.php new file mode 100644 index 00000000..a9eca1af --- /dev/null +++ b/public/include/classes/notification.class.php @@ -0,0 +1,45 @@ +mysqli->prepare("SELECT id FROM $this->table WHERE data = ? LIMIT 1"); + if ($stmt && $stmt->bind_param('s', $data) && $stmt->execute() && $stmt->store_result() && $stmt->num_rows == 1) + return true; + // Catchall + // Does not seem to have a notification set + return false; + } + + /** + * Add a new notification to the table + * @param type string Type of the notification + * @return bool + **/ + public function addNotification($type, $data) { + // Store notification data as json + $data = json_encode($data); + $stmt = $this->mysqli->prepare("INSERT INTO $this->table (type, data) VALUES (?,?)"); + if ($stmt && $stmt->bind_param('ss', $type, $data) && $stmt->execute()) + return true; + $this->debug->append("Failed to add notification for $type with $data: " . $this->mysqli->error); + $this->setErrorMessage("Unable to add new notification"); + return false; + } +} + +$notification = new Notification(); +$notification->setDebug($debug); +$notification->setMysql($mysqli); +$notification->setSmarty($smarty); +$notification->setConfig($config); diff --git a/public/include/classes/worker.class.php b/public/include/classes/worker.class.php index 6052eaac..a5052ba9 100644 --- a/public/include/classes/worker.class.php +++ b/public/include/classes/worker.class.php @@ -58,6 +58,26 @@ class Worker { return false; } + /** + * Fetch all IDLE workers that have monitoring enabled + * @param none + * @return data array Workers in IDLE state and monitoring enabled + **/ + public function getAllIdleWorkers() { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare(" + SELECT account_id, id, username + FROM " . $this->table . " + WHERE monitor = 1 AND ( SELECT SIGN(COUNT(id)) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) = 0"); + + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_all(MYSQLI_ASSOC); + // Catchall + $this->setErrorMessage("Unable to fetch IDLE, monitored workers"); + echo $this->mysqli->error; + return false; + } + /** * Fetch all workers for an account * @param account_id int User ID diff --git a/public/templates/mail/idle_worker.tpl b/public/templates/mail/idle_worker.tpl new file mode 100644 index 00000000..a21aaac3 --- /dev/null +++ b/public/templates/mail/idle_worker.tpl @@ -0,0 +1,9 @@ + + +

    One of your workers is currently IDLE.

    +

    Since monitoring is enabled for this worker, this notification was sent.

    +

    Please check your workers!

    +
    +
    + + diff --git a/sql/mmcfe_ng_structure.sql b/sql/mmcfe_ng_structure.sql index c3c626bd..a971d455 100644 --- a/sql/mmcfe_ng_structure.sql +++ b/sql/mmcfe_ng_structure.sql @@ -3,7 +3,7 @@ -- http://www.phpmyadmin.net -- -- Host: localhost --- Generation Time: Jun 06, 2013 at 09:01 PM +-- Generation Time: Jun 07, 2013 at 02:42 PM -- Server version: 5.5.31-0ubuntu0.13.04.1 -- PHP Version: 5.4.9-4ubuntu2 @@ -69,6 +69,20 @@ CREATE TABLE IF NOT EXISTS `blocks` ( -- -------------------------------------------------------- +-- +-- Table structure for table `notifications` +-- + +CREATE TABLE IF NOT EXISTS `notifications` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `type` varchar(25) NOT NULL, + `data` text NOT NULL, + `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + -- -- Table structure for table `pool_worker` -- @@ -136,7 +150,7 @@ CREATE TABLE IF NOT EXISTS `shares_archive` ( PRIMARY KEY (`id`), UNIQUE KEY `share_id` (`share_id`), KEY `time` (`time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes'; -- -------------------------------------------------------- @@ -153,7 +167,7 @@ CREATE TABLE IF NOT EXISTS `statistics_shares` ( PRIMARY KEY (`id`), KEY `account_id` (`account_id`), KEY `block_id` (`block_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -------------------------------------------------------- From 69a3761be58e81868c2bb511b88e7f9c20af719d Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 7 Jun 2013 15:07:26 +0200 Subject: [PATCH 056/168] Adding notification reset once worker is active Go through all active notifications (active means they are not notified again) and check if their state has changed. If so, mark as inactive and allow for re-notification of the same type and data. --- cronjobs/notifications.php | 34 +++++++++++------ public/include/classes/mail.class.php | 2 +- public/include/classes/notification.class.php | 38 ++++++++++++++++++- public/include/classes/worker.class.php | 21 ++++++++++ 4 files changed, 80 insertions(+), 15 deletions(-) diff --git a/cronjobs/notifications.php b/cronjobs/notifications.php index 7bca08ec..e70d2b07 100755 --- a/cronjobs/notifications.php +++ b/cronjobs/notifications.php @@ -22,21 +22,31 @@ limitations under the License. // Include all settings and classes require_once('shared.inc.php'); +// Find all IDLE workers $aWorkers = $worker->getAllIdleWorkers(); if (empty($aWorkers)) { verbose("No idle workers found\n"); - exit; -} - -foreach ($aWorkers as $aWorker) { - $aData = $aWorker; - $aData['username'] = $user->getUserName($aWorker['account_id']); - $aData['email'] = $user->getUserEmail($aData['username']); - if (!$notification->isNotified($aData)) { - if (!$notification->addNotification('idle_worker', $aData) && $notification->sendMail('sebastian@grewe.ca', 'idle_worker', $aData)) - verbose("Unable to send notification: " . $notification->getError() . "\n"); - } else { - verbose("Already notified for this worker\n"); +} else { + foreach ($aWorkers as $aWorker) { + $aData = $aWorker; + $aData['username'] = $user->getUserName($aWorker['account_id']); + $aData['email'] = $user->getUserEmail($aData['username']); + if (!$notification->isNotified($aData)) { + if (!$notification->addNotification('idle_worker', $aData) && $notification->sendMail('sebastian@grewe.ca', 'idle_worker', $aData)) + verbose("Unable to send notification: " . $notification->getError() . "\n"); + } else { + verbose("Already notified for this worker\n"); + } } } +// We notified, lets check which recovered +$aNotifications = $notification->getAllActive(); +foreach ($aNotifications as $aNotification) { + $aData = json_decode($aNotification['data'], true); + $aWorker = $worker->getWorker($aData['id']); + if ($aWorker['active'] == 1) + if (!$notification->setInactive($aNotification['id'])) + verbose("Failed to set notification inactive for " . $aWorker['username'] . "\n"); +} + ?> diff --git a/public/include/classes/mail.class.php b/public/include/classes/mail.class.php index 6c7cc0f1..a1cf2137 100644 --- a/public/include/classes/mail.class.php +++ b/public/include/classes/mail.class.php @@ -20,7 +20,7 @@ class Mail { $this->config = $config; } - private function checkStmt($bState) { + function checkStmt($bState) { $this->debug->append("STA " . __METHOD__, 4); if ($bState ===! true) { $this->debug->append("Failed to prepare statement: " . $this->mysqli->error); diff --git a/public/include/classes/notification.class.php b/public/include/classes/notification.class.php index a9eca1af..9a6dcf3b 100644 --- a/public/include/classes/notification.class.php +++ b/public/include/classes/notification.class.php @@ -7,13 +7,36 @@ if (!defined('SECURITY')) class Notification extends Mail { var $table = 'notifications'; + public function setInactive($id) { + $field = array( + 'name' => 'active', + 'type' => 'i', + 'value' => 0 + ); + return $this->updateSingle($id, $field); + } + + /** + * Update a single row in a table + * @param userID int Account ID + * @param field string Field to update + * @return bool + **/ + private function updateSingle($id, $field) { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare("UPDATE $this->table SET " . $field['name'] . " = ? WHERE id = ? LIMIT 1"); + if ($this->checkStmt($stmt) && $stmt->bind_param($field['type'].'i', $field['value'], $id) && $stmt->execute()) + return true; + $this->debug->append("Unable to update " . $field['name'] . " with " . $field['value'] . " for ID $id"); + return false; + } /** * We check our notification table for existing data * so we can avoid duplicate entries **/ public function isNotified($aData) { $data = json_encode($aData); - $stmt = $this->mysqli->prepare("SELECT id FROM $this->table WHERE data = ? LIMIT 1"); + $stmt = $this->mysqli->prepare("SELECT id FROM $this->table WHERE data = ? AND active = 1 LIMIT 1"); if ($stmt && $stmt->bind_param('s', $data) && $stmt->execute() && $stmt->store_result() && $stmt->num_rows == 1) return true; // Catchall @@ -21,6 +44,17 @@ class Notification extends Mail { return false; } + /** + * Get all active notifications + **/ + public function getAllActive() { + $stmt =$this->mysqli->prepare("SELECT id, data FROM $this->table WHERE active = 1 LIMIT 1"); + if ($stmt && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_all(MYSQLI_ASSOC); + // Catchall + return false; + } + /** * Add a new notification to the table * @param type string Type of the notification @@ -29,7 +63,7 @@ class Notification extends Mail { public function addNotification($type, $data) { // Store notification data as json $data = json_encode($data); - $stmt = $this->mysqli->prepare("INSERT INTO $this->table (type, data) VALUES (?,?)"); + $stmt = $this->mysqli->prepare("INSERT INTO $this->table (type, data, active) VALUES (?,?,1)"); if ($stmt && $stmt->bind_param('ss', $type, $data) && $stmt->execute()) return true; $this->debug->append("Failed to add notification for $type with $data: " . $this->mysqli->error); diff --git a/public/include/classes/worker.class.php b/public/include/classes/worker.class.php index a5052ba9..6a9f6524 100644 --- a/public/include/classes/worker.class.php +++ b/public/include/classes/worker.class.php @@ -78,6 +78,27 @@ class Worker { return false; } + /** + * Fetch a specific worker and its status + * @param id int Worker ID + * @return mixed array Worker details + **/ + public function getWorker($id) { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare(" + SELECT id, username, password, monitor, + ( SELECT SIGN(COUNT(id)) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS active, + ( SELECT ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ")/600/1000) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS hashrate + FROM $this->table + WHERE id = ? + "); + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $id) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_assoc(); + // Catchall + echo $this->mysqli->error; + return false; + } + /** * Fetch all workers for an account * @param account_id int User ID From 4da9fd2369088d9ed3852b8f3465b7582895a1c4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 7 Jun 2013 15:35:58 +0200 Subject: [PATCH 057/168] Working notification system * Added things to mail templates * Modified user password reset call for new mail template * Added BASEPATH to smarty code to ensure templates are compiled in the proper directory * Updated mail and notification class * Updated notification cron * Added notification cron to run-cron list --- cronjobs/notifications.php | 23 +++++++++++++------ cronjobs/run-crons.sh | 2 +- public/include/classes/mail.class.php | 11 +++++++-- public/include/classes/notification.class.php | 1 + public/include/classes/user.class.php | 1 + public/include/smarty.inc.php | 6 ++--- public/templates/mail/idle_worker.tpl | 2 +- public/templates/mail/subject.tpl | 2 +- 8 files changed, 33 insertions(+), 15 deletions(-) diff --git a/cronjobs/notifications.php b/cronjobs/notifications.php index e70d2b07..45bda82a 100755 --- a/cronjobs/notifications.php +++ b/cronjobs/notifications.php @@ -30,23 +30,32 @@ if (empty($aWorkers)) { foreach ($aWorkers as $aWorker) { $aData = $aWorker; $aData['username'] = $user->getUserName($aWorker['account_id']); + $aData['subject'] = 'IDLE Worker : ' . $aWorker['username']; $aData['email'] = $user->getUserEmail($aData['username']); - if (!$notification->isNotified($aData)) { - if (!$notification->addNotification('idle_worker', $aData) && $notification->sendMail('sebastian@grewe.ca', 'idle_worker', $aData)) - verbose("Unable to send notification: " . $notification->getError() . "\n"); - } else { - verbose("Already notified for this worker\n"); + if ( $notification->isNotified($aData) ) { + verbose("Worker already notified\n"); + continue; } + if ($notification->addNotification('idle_worker', $aData) && $notification->sendMail($aData['email'], 'idle_worker', $aData)) { + verbose ("Notified " . $aData['email'] . " for IDLE worker " . $aWorker['username'] . "\n"); + } else { + verbose("Unable to send notification: " . $notification->getError() . "\n"); + } } } + // We notified, lets check which recovered $aNotifications = $notification->getAllActive(); foreach ($aNotifications as $aNotification) { $aData = json_decode($aNotification['data'], true); $aWorker = $worker->getWorker($aData['id']); - if ($aWorker['active'] == 1) - if (!$notification->setInactive($aNotification['id'])) + if ($aWorker['active'] == 1) { + if ($notification->setInactive($aNotification['id'])) { + verbose("Marked notification " . $aNotification['id'] . " as inactive\n"); + } else { verbose("Failed to set notification inactive for " . $aWorker['username'] . "\n"); + } + } } ?> diff --git a/cronjobs/run-crons.sh b/cronjobs/run-crons.sh index 730a9063..5f9179dc 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 blockupdate.php auto_payout.php tickerupdate.php" +CRONS="findblock.php proportional_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php" # Additional arguments to pass to cronjobs CRONARGS="-v" diff --git a/public/include/classes/mail.class.php b/public/include/classes/mail.class.php index a1cf2137..2800500c 100644 --- a/public/include/classes/mail.class.php +++ b/public/include/classes/mail.class.php @@ -19,7 +19,12 @@ class Mail { public function setConfig($config) { $this->config = $config; } - + public function setErrorMessage($msg) { + $this->sError = $msg; + } + public function getError() { + return $this->sError; + } function checkStmt($bState) { $this->debug->append("STA " . __METHOD__, 4); if ($bState ===! true) { @@ -30,8 +35,10 @@ class Mail { return true; } - public function sendMail($email, $template, $vars) { + public function sendMail($email, $template, $aData) { $this->smarty->assign('WEBSITENAME', $this->config['website']['name']); + $this->smarty->assign('SUBJECT', $aData['subject']); + $this->smarty->assign('DATA', $aData); $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"; diff --git a/public/include/classes/notification.class.php b/public/include/classes/notification.class.php index 9a6dcf3b..983db437 100644 --- a/public/include/classes/notification.class.php +++ b/public/include/classes/notification.class.php @@ -41,6 +41,7 @@ class Notification extends Mail { return true; // Catchall // Does not seem to have a notification set + $this->setErrorMessage("Unable to run query: " . $this->mysqli->error); return false; } diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 8db17a1e..b4627729 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -486,6 +486,7 @@ class User { } $smarty->assign('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"; diff --git a/public/include/smarty.inc.php b/public/include/smarty.inc.php index b2e67f37..5668e1d3 100644 --- a/public/include/smarty.inc.php +++ b/public/include/smarty.inc.php @@ -16,10 +16,10 @@ $smarty = new Smarty; // Assign our local paths $debug->append('Define Smarty Paths', 3); -$smarty->template_dir = 'templates/' . THEME . '/'; -$smarty->compile_dir = 'templates/compile/'; +$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->cache_dir = "templates/cache"; +$smarty->cache_dir = BASEPATH . "templates/cache"; ?> diff --git a/public/templates/mail/idle_worker.tpl b/public/templates/mail/idle_worker.tpl index a21aaac3..6d1c282c 100644 --- a/public/templates/mail/idle_worker.tpl +++ b/public/templates/mail/idle_worker.tpl @@ -1,6 +1,6 @@ -

    One of your workers is currently IDLE.

    +

    One of your workers is currently IDLE: {$DATA.username}

    Since monitoring is enabled for this worker, this notification was sent.

    Please check your workers!


    diff --git a/public/templates/mail/subject.tpl b/public/templates/mail/subject.tpl index 665e26f5..94fd6a28 100644 --- a/public/templates/mail/subject.tpl +++ b/public/templates/mail/subject.tpl @@ -1 +1 @@ -[ {$WEBSITENAME} ] Password Reset Request +[ {$WEBSITENAME} ] {$SUBJECT} From d5d0b5470542514664dd8c1ca61ccbdd35253c58 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 7 Jun 2013 15:39:43 +0200 Subject: [PATCH 058/168] adding new structure for notifications --- sql/mmcfe_ng_structure.sql | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/sql/mmcfe_ng_structure.sql b/sql/mmcfe_ng_structure.sql index a971d455..039b9a04 100644 --- a/sql/mmcfe_ng_structure.sql +++ b/sql/mmcfe_ng_structure.sql @@ -3,7 +3,7 @@ -- http://www.phpmyadmin.net -- -- Host: localhost --- Generation Time: Jun 07, 2013 at 02:42 PM +-- Generation Time: Jun 07, 2013 at 03:39 PM -- Server version: 5.5.31-0ubuntu0.13.04.1 -- PHP Version: 5.4.9-4ubuntu2 @@ -65,7 +65,7 @@ CREATE TABLE IF NOT EXISTS `blocks` ( PRIMARY KEY (`id`), UNIQUE KEY `height` (`height`,`blockhash`), KEY `time` (`time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Discovered blocks persisted from Litecoin Service'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Discovered blocks persisted from Litecoin Service'; -- -------------------------------------------------------- @@ -76,10 +76,13 @@ CREATE TABLE IF NOT EXISTS `blocks` ( CREATE TABLE IF NOT EXISTS `notifications` ( `id` int(11) NOT NULL AUTO_INCREMENT, `type` varchar(25) NOT NULL, - `data` text NOT NULL, + `data` varchar(255) NOT NULL, + `active` tinyint(1) NOT NULL DEFAULT '1', `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; + PRIMARY KEY (`id`), + KEY `active` (`active`), + KEY `data` (`data`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -------------------------------------------------------- @@ -150,7 +153,7 @@ CREATE TABLE IF NOT EXISTS `shares_archive` ( PRIMARY KEY (`id`), UNIQUE KEY `share_id` (`share_id`), KEY `time` (`time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes'; -- -------------------------------------------------------- @@ -167,7 +170,7 @@ CREATE TABLE IF NOT EXISTS `statistics_shares` ( PRIMARY KEY (`id`), KEY `account_id` (`account_id`), KEY `block_id` (`block_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -------------------------------------------------------- @@ -187,7 +190,7 @@ CREATE TABLE IF NOT EXISTS `transactions` ( KEY `block_id` (`block_id`), KEY `account_id` (`account_id`), KEY `type` (`type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; From 531e28cee85f9b96136cfe128f8cfaf4860ee68b Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 21:37:14 +0200 Subject: [PATCH 059/168] Adding support for PPS payout method This commit changed a few things in the backend and classes code: * Any _PPS transaction does NOT need to be confirmed * Queries updated for added _PPS transactions * Template updated to properly display these transactions Cronjob * Added pps_payput cron to run payouts based on worker submitted shares * **IMPORTANT**: Can NOT be run with proportional_payout! Addresses #70 --- cronjobs/pps_payout.php | 87 +++++++++++++++++++ public/include/classes/share.class.php | 15 ++++ public/include/classes/transaction.class.php | 18 ++-- .../mmcFE/account/transactions/default.tpl | 5 +- 4 files changed, 118 insertions(+), 7 deletions(-) create mode 100755 cronjobs/pps_payout.php diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php new file mode 100755 index 00000000..a71c42a3 --- /dev/null +++ b/cronjobs/pps_payout.php @@ -0,0 +1,87 @@ +#!/usr/bin/php +can_connect() === true ){ + $dDifficulty = $bitcoin->getdifficulty(); +} else { + verbose("Aborted: " . $bitcoin->can_connect() . "\n"); + exit(1); +} + +// Value per share calculation +$pps_value = 50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']); + +// Find our last share accounted and last inserted share for PPS calculations +$iPreviousShareId = $setting->getValue('pps_last_share_id'); +$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\t\tPayout\t\tDonation\tFee\t\tStatus\n"); + +foreach ($aAccountShares as $aData) { + // Take our valid shares and multiply by per share value + $aData['payout'] = $aData['valid'] * $pps_value; + + // Defaults + $aData['fee' ] = 0; + $aData['donation'] = 0; + + // Calculate block fees + if ($config['fees'] > 0) + $aData['fee'] = number_format(round($config['fees'] / 100 * $aData['payout'], 8), 8); + // 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" . + $aData['username'] . "\t" . + $aData['invalid'] . "\t" . + $aData['valid'] . "\t*\t" . + $pps_value . "\t=\t" . + $aData['payout'] . "\t" . + $aData['donation'] . "\t" . + $aData['fee'] . "\t"); + + $strStatus = "OK"; + // Add new credit transaction + if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit_PPS')) + $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_PPS')) + $strStatus = "Fee Failed"; + // Add new donation debit + if ($aData['donation'] > 0) + if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation_PPS')) + $strStatus = "Donation Failed"; + verbose($strStatus . "\n"); +} + +// Store our last inserted ID for the next run +$setting->setValue('pps_last_share_id', $iLastShareId); + +verbose("\n\n------------------------------------------------------------------------------------\n\n"); +?> diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index b0a3b249..1033f92c 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -44,6 +44,21 @@ class Share { return $this->table; } + /** + * Get last inserted Share ID from Database + * Used for PPS calculations without moving to archive + **/ + public function getLastInsertedShareId() { + $stmt = $this->mysqli->prepare(" + SELECT MAX(id) AS id FROM $this->table + "); + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_object()->id; + // Catchall + $this->setErrorMessage('Failed to fetch last inserted share ID'); + return false; + } + /** * Get all valid shares for this round * @param previous_upstream int Previous found share accepted by upstream to limit results diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index 020140d3..711e893f 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -142,7 +142,7 @@ class Transaction { ( SELECT sum(t.amount) AS credit FROM $this->table AS t - WHERE t.type = 'Credit' + WHERE t.type IN ('Credit', 'Credit_PPS') ) AS t1, ( SELECT sum(t.amount) AS debit @@ -152,7 +152,7 @@ class Transaction { ( SELECT sum(t.amount) AS other FROM " . $this->table . " AS t - WHERE t.type IN ('Donation','Fee') + WHERE t.type IN ('Donation','Fee','Donation_PPS','Fee_PPS') ) AS t3"); if ($this->checkStmt($stmt) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch()) return $dBalance; @@ -176,8 +176,11 @@ class Transaction { 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 = 'Credit' - AND b.confirmations >= ? + WHERE + ( + ( t.type = 'Credit' AND b.confirmations >= ? ) OR + ( t.type = 'Credit_PPS' ) + ) AND t.account_id = ? ) AS t1, ( @@ -190,8 +193,11 @@ class Transaction { 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 >= ? + WHERE + ( + ( t.type IN ('Donation','Fee') AND b.confirmations >= ? ) OR + ( t.type IN ('Donation_PPS', 'Fee_PPS') ) + ) AND t.account_id = ? ) AS t3 "); diff --git a/public/templates/mmcFE/account/transactions/default.tpl b/public/templates/mmcFE/account/transactions/default.tpl index 8f5282e1..dd31723f 100644 --- a/public/templates/mmcFE/account/transactions/default.tpl +++ b/public/templates/mmcFE/account/transactions/default.tpl @@ -18,6 +18,9 @@ ($TRANSACTIONS[transaction].type == 'Credit' 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' )} @@ -27,7 +30,7 @@
    - + {/if} {/section} From dcfbd83270292261e2a0c57964055a6feb57faa8 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:31:35 +0200 Subject: [PATCH 060/168] properly format payout and round it --- 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 a71c42a3..1dafa288 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -44,7 +44,7 @@ verbose("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\t\tPayout\t\tDonation\tFee\ foreach ($aAccountShares as $aData) { // Take our valid shares and multiply by per share value - $aData['payout'] = $aData['valid'] * $pps_value; + $aData['payout'] = number_format(round($aData['valid'] * $pps_value, 8)); // Defaults $aData['fee' ] = 0; From 5f1e52767e88db2dd8dff843b8463b8b2a9b8ca5 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:34:02 +0200 Subject: [PATCH 061/168] forgot 8 decimals --- 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 1dafa288..bef78f2a 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -44,7 +44,7 @@ verbose("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\t\tPayout\t\tDonation\tFee\ foreach ($aAccountShares as $aData) { // Take our valid shares and multiply by per share value - $aData['payout'] = number_format(round($aData['valid'] * $pps_value, 8)); + $aData['payout'] = number_format(round($aData['valid'] * $pps_value, 8), 8); // Defaults $aData['fee' ] = 0; From 4a36479fe22205d959e80e17e314da294aa5adbe Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:45:53 +0200 Subject: [PATCH 062/168] properly format pps value to 12 digits --- 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 bef78f2a..e2804595 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -31,7 +31,7 @@ if ( $bitcoin->can_connect() === true ){ } // Value per share calculation -$pps_value = 50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']); +$pps_value = number_format(round(50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); // Find our last share accounted and last inserted share for PPS calculations $iPreviousShareId = $setting->getValue('pps_last_share_id'); From b6da195da5954e790d5deb521d2a090094a2c2ef Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:48:09 +0200 Subject: [PATCH 063/168] moved table header to the left --- 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 e2804595..427035d5 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -40,7 +40,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\t\tPayout\t\tDonation\tFee\t\tStatus\n"); +verbose("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\tPayout\t\tDonation\tFee\t\tStatus\n"); foreach ($aAccountShares as $aData) { // Take our valid shares and multiply by per share value From efdbff8e5329b456b9d0a7481d5533fd6178d8c4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 7 Jun 2013 20:07:55 +0200 Subject: [PATCH 064/168] Do not include unconfirmed transactions in balance view This will fix #139 showing wrong liquid asset counts. Since that was confusing I ensured confirmations are included in the calculations. --- public/include/classes/transaction.class.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index 020140d3..e4ff684e 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -142,7 +142,9 @@ class Transaction { ( 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 = 'Credit' + AND b.confirmations >= " . $this->config['confirmations'] . " ) AS t1, ( SELECT sum(t.amount) AS debit @@ -152,7 +154,9 @@ class Transaction { ( 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 >= " . $this->config['confirmations'] . " ) AS t3"); if ($this->checkStmt($stmt) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch()) return $dBalance; From 02eb400de9dbbeac1860a95d40e3e645448ce074 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 7 Jun 2013 20:09:54 +0200 Subject: [PATCH 065/168] proper whitespacing --- public/include/classes/transaction.class.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index e4ff684e..a487e5ec 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -142,9 +142,9 @@ class Transaction { ( SELECT sum(t.amount) AS credit FROM $this->table AS t - LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id + LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id WHERE t.type = 'Credit' - AND b.confirmations >= " . $this->config['confirmations'] . " + AND b.confirmations >= " . $this->config['confirmations'] . " ) AS t1, ( SELECT sum(t.amount) AS debit @@ -154,9 +154,9 @@ class Transaction { ( SELECT sum(t.amount) AS other FROM " . $this->table . " AS t - LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id + LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id WHERE t.type IN ('Donation','Fee') - AND b.confirmations >= " . $this->config['confirmations'] . " + AND b.confirmations >= " . $this->config['confirmations'] . " ) AS t3"); if ($this->checkStmt($stmt) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch()) return $dBalance; From 9eeb088734b112684295d22fc0961adc03ef3db5 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 7 Jun 2013 20:15:23 +0200 Subject: [PATCH 066/168] check if notifications exist at all --- cronjobs/notifications.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/cronjobs/notifications.php b/cronjobs/notifications.php index 45bda82a..aa2bf8a9 100755 --- a/cronjobs/notifications.php +++ b/cronjobs/notifications.php @@ -46,16 +46,17 @@ if (empty($aWorkers)) { // We notified, lets check which recovered $aNotifications = $notification->getAllActive(); -foreach ($aNotifications as $aNotification) { - $aData = json_decode($aNotification['data'], true); - $aWorker = $worker->getWorker($aData['id']); - if ($aWorker['active'] == 1) { - if ($notification->setInactive($aNotification['id'])) { - verbose("Marked notification " . $aNotification['id'] . " as inactive\n"); - } else { - verbose("Failed to set notification inactive for " . $aWorker['username'] . "\n"); +if (!empty($aNotifications)) { + foreach ($aNotifications as $aNotification) { + $aData = json_decode($aNotification['data'], true); + $aWorker = $worker->getWorker($aData['id']); + if ($aWorker['active'] == 1) { + if ($notification->setInactive($aNotification['id'])) { + verbose("Marked notification " . $aNotification['id'] . " as inactive\n"); + } else { + verbose("Failed to set notification inactive for " . $aWorker['username'] . "\n"); + } } } } - ?> From 88ade9cfa3028a9335a58b2f12b35cb0bb20448f Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 9 Jun 2013 13:10:58 +0200 Subject: [PATCH 067/168] Adding support for various notifications * Adding new SQL upgrade for notifications * Added support for per user notification settings * Added account_id to notifications table * Added new notification_settings table * Added new account page: notifications Addresses #144 --- cronjobs/notifications.php | 2 +- public/include/classes/notification.class.php | 83 +++++++++++++++++-- .../pages/account/notifications.inc.php | 25 ++++++ .../mmcFE/account/notifications/default.tpl | 60 ++++++++++++++ public/templates/mmcFE/global/navigation.tpl | 1 + sql/issue_144_notification_upgrade.sql | 6 ++ 6 files changed, 171 insertions(+), 6 deletions(-) create mode 100644 public/include/pages/account/notifications.inc.php create mode 100644 public/templates/mmcFE/account/notifications/default.tpl create mode 100644 sql/issue_144_notification_upgrade.sql diff --git a/cronjobs/notifications.php b/cronjobs/notifications.php index aa2bf8a9..eddef3de 100755 --- a/cronjobs/notifications.php +++ b/cronjobs/notifications.php @@ -36,7 +36,7 @@ if (empty($aWorkers)) { verbose("Worker already notified\n"); continue; } - if ($notification->addNotification('idle_worker', $aData) && $notification->sendMail($aData['email'], 'idle_worker', $aData)) { + if ($notification->addNotification($aWorker['account_id'], 'idle_worker', $aData) && $notification->sendMail($aData['email'], 'idle_worker', $aData)) { verbose ("Notified " . $aData['email'] . " for IDLE worker " . $aWorker['username'] . "\n"); } else { verbose("Unable to send notification: " . $notification->getError() . "\n"); diff --git a/public/include/classes/notification.class.php b/public/include/classes/notification.class.php index 983db437..cff05abb 100644 --- a/public/include/classes/notification.class.php +++ b/public/include/classes/notification.class.php @@ -6,6 +6,7 @@ if (!defined('SECURITY')) class Notification extends Mail { var $table = 'notifications'; + var $tableSettings = 'notification_settings'; public function setInactive($id) { $field = array( @@ -22,9 +23,10 @@ class Notification extends Mail { * @param field string Field to update * @return bool **/ - private function updateSingle($id, $field) { + private function updateSingle($id, $field, $table='') { + if (empty($table)) $table = $this->table; $this->debug->append("STA " . __METHOD__, 4); - $stmt = $this->mysqli->prepare("UPDATE $this->table SET " . $field['name'] . " = ? WHERE id = ? LIMIT 1"); + $stmt = $this->mysqli->prepare("UPDATE $table SET " . $field['name'] . " = ? WHERE id = ? LIMIT 1"); if ($this->checkStmt($stmt) && $stmt->bind_param($field['type'].'i', $field['value'], $id) && $stmt->execute()) return true; $this->debug->append("Unable to update " . $field['name'] . " with " . $field['value'] . " for ID $id"); @@ -35,6 +37,7 @@ class Notification extends Mail { * so we can avoid duplicate entries **/ public function isNotified($aData) { + $this->debug->append("STA " . __METHOD__, 4); $data = json_encode($aData); $stmt = $this->mysqli->prepare("SELECT id FROM $this->table WHERE data = ? AND active = 1 LIMIT 1"); if ($stmt && $stmt->bind_param('s', $data) && $stmt->execute() && $stmt->store_result() && $stmt->num_rows == 1) @@ -49,6 +52,7 @@ class Notification extends Mail { * Get all active notifications **/ public function getAllActive() { + $this->debug->append("STA " . __METHOD__, 4); $stmt =$this->mysqli->prepare("SELECT id, data FROM $this->table WHERE active = 1 LIMIT 1"); if ($stmt && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); @@ -61,16 +65,85 @@ class Notification extends Mail { * @param type string Type of the notification * @return bool **/ - public function addNotification($type, $data) { + public function addNotification($account_id, $type, $data) { + $this->debug->append("STA " . __METHOD__, 4); // Store notification data as json $data = json_encode($data); - $stmt = $this->mysqli->prepare("INSERT INTO $this->table (type, data, active) VALUES (?,?,1)"); - if ($stmt && $stmt->bind_param('ss', $type, $data) && $stmt->execute()) + $stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, type, data, active) VALUES (?, ?,?,1)"); + if ($stmt && $stmt->bind_param('iss', $account_id, $type, $data) && $stmt->execute()) return true; $this->debug->append("Failed to add notification for $type with $data: " . $this->mysqli->error); $this->setErrorMessage("Unable to add new notification"); return false; } + + /** + * Fetch notifications for a user account + * @param id int Account ID + * @return array Notification data + **/ + public function getNofifications($account_id) { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE account_id = ? ORDER BY time DESC"); + if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_all(MYSQLI_ASSOC); + // Catchall + return false; + } + + /** + * Fetch notification settings for user account + * @param id int Account ID + * @return array Notification settings + **/ + public function getNotificationSettings($account_id) { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare("SELECT * FROM $this->tableSettings WHERE account_id = ?"); + if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result()) { + while ($row = $result->fetch_assoc()) { + $aData[$row['type']] = $row['active']; + } + return $aData; + } + // Catchall + return false; + } + + /** + * Update accounts notification settings + * @param account_id int Account ID + * @param data array Data array + * @return bool + **/ + public function updateSettings($account_id, $data) { + $this->debug->append("STA " . __METHOD__, 4); + $failed = $ok = 0; + foreach ($data as $type => $active) { + // Does an entry exist already + $stmt = $this->mysqli->prepare("SELECT * FROM $this->tableSettings WHERE account_id = ? AND type = ?"); + if ($stmt && $stmt->bind_param('is', $account_id, $type) && $stmt->execute() && $stmt->store_result() && $stmt->num_rows() > 0) { + // We found a matching row + $stmt = $this->mysqli->prepare("UPDATE $this->tableSettings SET active = ? WHERE type = ? AND account_id = ?"); + if ($stmt && $stmt->bind_param('isi', $active, $type, $account_id) && $stmt->execute() && $stmt->close()) { + $ok++; + } else { + $failed++; + } + } else { + $stmt = $this->mysqli->prepare("INSERT INTO $this->tableSettings (active, type, account_id) VALUES (?,?,?)"); + if ($stmt && $stmt->bind_param('isi', $active, $type, $account_id) && $stmt->execute()) { + $ok++; + } else { + $failed++; + } + } + } + if ($failed > 0) { + $this->setErrorMessage('Failed to update ' . $failed . ' settings'); + return false; + } + return true; + } } $notification = new Notification(); diff --git a/public/include/pages/account/notifications.inc.php b/public/include/pages/account/notifications.inc.php new file mode 100644 index 00000000..2ab9c0d0 --- /dev/null +++ b/public/include/pages/account/notifications.inc.php @@ -0,0 +1,25 @@ +updateSettings($_SESSION['USERDATA']['id'], $_REQUEST['data'])) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Updated notification settings'); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update settings', 'TYPE' => 'errormsg'); + } +} + +// Fetch notifications +$aNotifications = $notification->getNofifications($_SESSION['USERDATA']['id']); +if (!$aNotifications) $_SESSION['POPUP'][] = array('CONTENT' => 'Could not find any notifications', 'TYPE' => 'errormsg'); + +// Fetch user notification settings +$aSettings = $notification->getNotificationSettings($_SESSION['USERDATA']['id']); + +$smarty->assign('NOTIFICATIONS', $aNotifications); +$smarty->assign('SETTINGS', $aSettings); +$smarty->assign('CONTENT', 'default.tpl'); +?> diff --git a/public/templates/mmcFE/account/notifications/default.tpl b/public/templates/mmcFE/account/notifications/default.tpl new file mode 100644 index 00000000..1b737f79 --- /dev/null +++ b/public/templates/mmcFE/account/notifications/default.tpl @@ -0,0 +1,60 @@ +{include file="global/block_header.tpl" ALIGN="left" BLOCK_HEADER="Notification Settings"} +
    + + + +
    Username E-Mail Hashrate  Shares   Est. Donation   Est. Payout    Balance   {$USERS[user].id} {$USERS[user].username} {$USERS[user].email}{$USERS[user].hashrate / 1024}{$USERS[user].shares}{$USERS[user].hashrate} {$USERS[user].payout.est_donation|number_format:"8"} {$USERS[user].payout.est_payout|number_format:"8"} {$USERS[user].balance|number_format:"8"}Username E-Mail HashrateShares Est. Donation Est. Payout BalanceID Username E-MailShares   Hashrate   Est. Donation   Est. Payout   {$USERS[user].id} {$USERS[user].username} {$USERS[user].email}{$USERS[user].shares} {$USERS[user].hashrate} {$USERS[user].payout.est_donation|number_format:"8"} {$USERS[user].payout.est_payout|number_format:"8"}ID Username E-MailShares   Hashrate Est. Donation Est. PayoutWorker Name Password ActiveMonitor Khash/s     {$WORKERS[worker].hashrate|number_format}
    Worker Name Password ActiveMonitor Khash/s     {$WORKERS[worker].hashrate|number_format}
    {$TRANSACTIONS[transaction].type} {$TRANSACTIONS[transaction].coin_address} {if $TRANSACTIONS[transaction].height == 0}n/a{else}{$TRANSACTIONS[transaction].height}{/if}{$TRANSACTIONS[transaction].amount}{$TRANSACTIONS[transaction].amount}
    + + + + + + + + + + + + + + + +
    TypeActive
    New Blocks + + +
    Auto Payout + + +
    + +
    + +{include file="global/block_footer.tpl"} + +{include file="global/block_header.tpl" ALIGN="right" BLOCK_HEADER="Notification History"} +
    + {include file="global/pagination.tpl"} + + + + + + + + + + +{section notification $NOTIFICATIONS} + + + + + + +{/section} + +
    IDTimeTypeActive
    {$NOTIFICATIONS[notification].id}{$NOTIFICATIONS[notification].time}{$NOTIFICATIONS[notification].type} + +
    +
    +{include file="global/block_footer.tpl"} diff --git a/public/templates/mmcFE/global/navigation.tpl b/public/templates/mmcFE/global/navigation.tpl index 6a0cdb37..3f26387a 100644 --- a/public/templates/mmcFE/global/navigation.tpl +++ b/public/templates/mmcFE/global/navigation.tpl @@ -7,6 +7,7 @@
  • My Workers
  • My Graphs
  • Transactions
  • +
  • Notifications
  • {/if} diff --git a/sql/issue_144_notification_upgrade.sql b/sql/issue_144_notification_upgrade.sql new file mode 100644 index 00000000..e2884e9e --- /dev/null +++ b/sql/issue_144_notification_upgrade.sql @@ -0,0 +1,6 @@ +ALTER TABLE `notifications` ADD `account_id` INT UNSIGNED NULL DEFAULT NULL , ADD INDEX ( `account_id` ) +CREATE TABLE IF NOT EXISTS `notification_settings` ( + `type` varchar(15) NOT NULL, + `account_id` int(11) NOT NULL, + `active` tinyint(1) NOT NULL DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; From 4ea8b6c6950a4cf7d2b8246d410f82ddb2de414c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 9 Jun 2013 14:26:18 +0200 Subject: [PATCH 068/168] Adding new notification system for new blocks * Modified findblocks cron * Modified notifications cron for new structure * Improved notification class * Added new template for new_block type * Moved idle_worker type template * Added new_block type to notification settings --- cronjobs/findblock.php | 10 +++++ cronjobs/notifications.php | 11 +---- public/include/classes/mail.class.php | 8 ++-- public/include/classes/notification.class.php | 42 ++++++++++++++++++- .../mail/{ => notifications}/idle_worker.tpl | 0 .../mail/notifications/new_block.tpl | 7 ++++ .../mmcFE/account/notifications/default.tpl | 7 ++++ 7 files changed, 71 insertions(+), 14 deletions(-) rename public/templates/mail/{ => notifications}/idle_worker.tpl (100%) create mode 100644 public/templates/mail/notifications/new_block.tpl diff --git a/cronjobs/findblock.php b/cronjobs/findblock.php index 2f978eed..d52c1cc9 100755 --- a/cronjobs/findblock.php +++ b/cronjobs/findblock.php @@ -112,6 +112,16 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { . $strStatus . "\n" ); + + // Notify users + $aAccounts = $notification->getNotificationByType('new_block'); + foreach ($aAccounts as $account_id) { + $aMailData = $aBlock; + $aMailData['subject'] = 'New Block'; + $aMailData['email'] = $user->getUserEmail($user->getUserName($account_id)); + $aMailData['shares'] = $iRoundShares; + $notification->sendNotification($account_id, 'new_block', $aMailData); + } } } ?> diff --git a/cronjobs/notifications.php b/cronjobs/notifications.php index eddef3de..cd8c020a 100755 --- a/cronjobs/notifications.php +++ b/cronjobs/notifications.php @@ -32,15 +32,8 @@ if (empty($aWorkers)) { $aData['username'] = $user->getUserName($aWorker['account_id']); $aData['subject'] = 'IDLE Worker : ' . $aWorker['username']; $aData['email'] = $user->getUserEmail($aData['username']); - if ( $notification->isNotified($aData) ) { - verbose("Worker already notified\n"); - continue; - } - if ($notification->addNotification($aWorker['account_id'], 'idle_worker', $aData) && $notification->sendMail($aData['email'], 'idle_worker', $aData)) { - verbose ("Notified " . $aData['email'] . " for IDLE worker " . $aWorker['username'] . "\n"); - } else { - verbose("Unable to send notification: " . $notification->getError() . "\n"); - } + if (!$notification->sendNotification($aWorker['account_id'], 'idle_worker', $aData)) + verbose($notification->getError() . "\n"); } } diff --git a/public/include/classes/mail.class.php b/public/include/classes/mail.class.php index 2800500c..e34423c5 100644 --- a/public/include/classes/mail.class.php +++ b/public/include/classes/mail.class.php @@ -16,6 +16,9 @@ class Mail { public function setSmarty($smarty) { $this->smarty = $smarty; } + public function setUser($user) { + $this->user = $user; + } public function setConfig($config) { $this->config = $config; } @@ -35,14 +38,14 @@ class Mail { return true; } - public function sendMail($email, $template, $aData) { + public function sendMail($template, $aData) { $this->smarty->assign('WEBSITENAME', $this->config['website']['name']); $this->smarty->assign('SUBJECT', $aData['subject']); $this->smarty->assign('DATA', $aData); $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, + if (mail($aData['email'], $this->smarty->fetch(BASEPATH . 'templates/mail/subject.tpl'), $this->smarty->fetch(BASEPATH . 'templates/mail/' . $template . '.tpl'), $headers)) { @@ -61,5 +64,4 @@ $mail->setDebug($debug); $mail->setMysql($mysqli); $mail->setSmarty($smarty); $mail->setConfig($config); - ?> diff --git a/public/include/classes/notification.class.php b/public/include/classes/notification.class.php index cff05abb..d324d4a7 100644 --- a/public/include/classes/notification.class.php +++ b/public/include/classes/notification.class.php @@ -44,7 +44,6 @@ class Notification extends Mail { return true; // Catchall // Does not seem to have a notification set - $this->setErrorMessage("Unable to run query: " . $this->mysqli->error); return false; } @@ -73,7 +72,7 @@ class Notification extends Mail { if ($stmt && $stmt->bind_param('iss', $account_id, $type, $data) && $stmt->execute()) return true; $this->debug->append("Failed to add notification for $type with $data: " . $this->mysqli->error); - $this->setErrorMessage("Unable to add new notification"); + $this->setErrorMessage("Unable to add new notification " . $this->mysqli->error); return false; } @@ -109,6 +108,21 @@ class Notification extends Mail { return false; } + /** + * Get all accounts that wish to receive a specific notification + * @param strType string Notification type + * @return data array User Accounts + **/ + public function getNotificationAccountIdByType($strType) { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare("SELECT account_id FROM $this->tableSettings WHERE type = ? AND active = 1"); + if ($stmt && $stmt->bind_param('s', $strType) && $stmt->execute() && $result = $stmt->get_result()) { + return $result->fetch_all(MYSQLI_ASSOC); + } + // Catchall + return false; + } + /** * Update accounts notification settings * @param account_id int Account ID @@ -144,6 +158,28 @@ class Notification extends Mail { } return true; } + + /** + * Send a specific notification setup in notification_settings + * @param type string Notification type + * @return bool + **/ + public function sendNotification($account_id, $strType, $aMailData) { + // Check if we notified for this event already + if ( $this->isNotified($aMailData) ) { + $this->setErrorMessage('A notification for this event has been sent already'); + return false; + } + // Check if this user wants strType notifications + $stmt = $this->mysqli->prepare("SELECT account_id FROM $this->tableSettings WHERE type = ? AND active = 1 AND account_id = ?"); + if ($stmt && $stmt->bind_param('si', $strType, $account_id) && $stmt->execute() && $stmt->bind_result($id) && $stmt->fetch()) { + if ($stmt->close() && $this->sendMail('notifications/' . $strType, $aMailData) && $this->addNotification($account_id, $strType, $aMailData)) + return true; + } else { + $this->setErrorMessage('User disabled ' . $strType . ' notifications'); + } + return false; + } } $notification = new Notification(); @@ -151,3 +187,5 @@ $notification->setDebug($debug); $notification->setMysql($mysqli); $notification->setSmarty($smarty); $notification->setConfig($config); + +?> diff --git a/public/templates/mail/idle_worker.tpl b/public/templates/mail/notifications/idle_worker.tpl similarity index 100% rename from public/templates/mail/idle_worker.tpl rename to public/templates/mail/notifications/idle_worker.tpl diff --git a/public/templates/mail/notifications/new_block.tpl b/public/templates/mail/notifications/new_block.tpl new file mode 100644 index 00000000..9d17d06e --- /dev/null +++ b/public/templates/mail/notifications/new_block.tpl @@ -0,0 +1,7 @@ + + +

    A new block has been discovered!

    +
    +
    + + diff --git a/public/templates/mmcFE/account/notifications/default.tpl b/public/templates/mmcFE/account/notifications/default.tpl index 1b737f79..94c0c2b0 100644 --- a/public/templates/mmcFE/account/notifications/default.tpl +++ b/public/templates/mmcFE/account/notifications/default.tpl @@ -8,6 +8,13 @@ Type Active + + IDLE Worker + + + + + New Blocks From 79d9c9714dfbfe590b894bd21dc5c445faea6d6e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 9 Jun 2013 14:31:00 +0200 Subject: [PATCH 069/168] wrong method call --- cronjobs/findblock.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cronjobs/findblock.php b/cronjobs/findblock.php index d52c1cc9..ead4b61c 100755 --- a/cronjobs/findblock.php +++ b/cronjobs/findblock.php @@ -114,9 +114,9 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { ); // Notify users - $aAccounts = $notification->getNotificationByType('new_block'); + $aAccounts = $notification->getNotificationAccountIdByType('new_block'); foreach ($aAccounts as $account_id) { - $aMailData = $aBlock; + $aMailData['height'] = $aBlock['height']; $aMailData['subject'] = 'New Block'; $aMailData['email'] = $user->getUserEmail($user->getUserName($account_id)); $aMailData['shares'] = $iRoundShares; From d723f4e8ef36e55fccfacece4d3dbe8fa143c6a9 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 9 Jun 2013 15:17:14 +0200 Subject: [PATCH 070/168] Adding notification on automatic payout * Added new mail template * Added notification code to auto_payout cron --- cronjobs/auto_payout.php | 15 ++++++++++++--- .../templates/mail/notifications/auto_payout.tpl | 8 ++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 public/templates/mail/notifications/auto_payout.tpl diff --git a/cronjobs/auto_payout.php b/cronjobs/auto_payout.php index 36c8a873..48861c86 100755 --- a/cronjobs/auto_payout.php +++ b/cronjobs/auto_payout.php @@ -52,19 +52,28 @@ if (! empty($users)) { } // Send balance, fees are reduced later - try { +/* try { $bitcoin->sendtoaddress($aUserData['coin_address'], $dBalance); } catch (BitcoinClientException $e) { verbose("SEND FAILED\n"); continue; } - + */ // Create transaction record if ($transaction->addTransaction($aUserData['id'], $dBalance, 'Debit_AP', NULL, $aUserData['coin_address'], 0.1)) { - verbose("OK\n"); + // Notify user via mail + $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"); + } } else { verbose("FAILED\n"); } + } else { verbose("SKIPPED\n"); } diff --git a/public/templates/mail/notifications/auto_payout.tpl b/public/templates/mail/notifications/auto_payout.tpl new file mode 100644 index 00000000..6d045357 --- /dev/null +++ b/public/templates/mail/notifications/auto_payout.tpl @@ -0,0 +1,8 @@ + + +

    An automated payout completed.

    +

    Amount: {$DATA.amount}

    +
    +
    + + From b2b853d3e0d693289c0f4482d6ebb2b73f259590 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 9 Jun 2013 15:24:58 +0200 Subject: [PATCH 071/168] Added manual payout notification * Added mail template for manual payouts * Added code to account page to notify via mail on payout * Added new option to notification setting template Adds another feature to #144 --- public/include/pages/account/edit.inc.php | 9 +++++++-- .../mail/notifications/manual_payout.tpl | 8 ++++++++ .../mmcFE/account/notifications/default.tpl | 15 ++++++++++++++- 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 public/templates/mail/notifications/manual_payout.tpl diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php index 1e2941bb..446c13f1 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -36,12 +36,17 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST } } catch (BitcoinClientException $e) { $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to send LTC, please contact site support immidiately', 'TYPE' => 'errormsg'); - $continue = false; +// $continue = false; } } // Set balance to 0, add to paid out, insert to ledger - if ($continue == true && $transaction->addTransaction($_SESSION['USERDATA']['id'], $dBalance, 'Debit_MP', NULL, $sCoinAddress)) + if ($continue == true && $transaction->addTransaction($_SESSION['USERDATA']['id'], $dBalance, 'Debit_MP', NULL, $sCoinAddress)) { $_SESSION['POPUP'][] = array('CONTENT' => 'Transaction completed', 'TYPE' => 'success'); + $aMailData['email'] = $user->getUserEmail($user->getUserName($_SESSION['USERDATA']['id'])); + $aMailData['amount'] = $dBalance; + $aMailData['subject'] = 'Manual Payout Completed'; + $notification->sendNotification($_SESSION['USERDATA']['id'], 'manual_payout', $aMailData); + } } else { $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service', 'TYPE' => 'errormsg'); } diff --git a/public/templates/mail/notifications/manual_payout.tpl b/public/templates/mail/notifications/manual_payout.tpl new file mode 100644 index 00000000..75198d7b --- /dev/null +++ b/public/templates/mail/notifications/manual_payout.tpl @@ -0,0 +1,8 @@ + + +

    An manual payout request completed.

    +

    Amount: {$DATA.amount}

    +
    +
    + + diff --git a/public/templates/mmcFE/account/notifications/default.tpl b/public/templates/mmcFE/account/notifications/default.tpl index 94c0c2b0..676807d3 100644 --- a/public/templates/mmcFE/account/notifications/default.tpl +++ b/public/templates/mmcFE/account/notifications/default.tpl @@ -29,6 +29,13 @@ + + Manual Payout + + + + + @@ -55,7 +62,13 @@ {$NOTIFICATIONS[notification].id} {$NOTIFICATIONS[notification].time} - {$NOTIFICATIONS[notification].type} + + {if $NOTIFICATIONS[notification].type == new_block}New Block + {else if $NOTIFICATIONS[notification].type == auto_payout}Auto Payout + {else if $NOTIFICATIONS[notification].type == idle_worker}IDLE Worker + {else if $NOTIFICATIONS[notification].type == manual_payout}Manual Payout + {/if} + From af9d2aed9534fc535546c42a4c9a6c614343e737 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 9 Jun 2013 15:31:38 +0200 Subject: [PATCH 072/168] wrong syntax in upgrade SQL --- sql/issue_144_notification_upgrade.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/issue_144_notification_upgrade.sql b/sql/issue_144_notification_upgrade.sql index e2884e9e..a47f3bbf 100644 --- a/sql/issue_144_notification_upgrade.sql +++ b/sql/issue_144_notification_upgrade.sql @@ -1,4 +1,4 @@ -ALTER TABLE `notifications` ADD `account_id` INT UNSIGNED NULL DEFAULT NULL , ADD INDEX ( `account_id` ) +ALTER TABLE `notifications` ADD `account_id` INT UNSIGNED NULL DEFAULT NULL , ADD INDEX ( `account_id` ); CREATE TABLE IF NOT EXISTS `notification_settings` ( `type` varchar(15) NOT NULL, `account_id` int(11) NOT NULL, From 3bc5c68403f5de3d977ab9405c3242e7bc43e42c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 9 Jun 2013 16:09:32 +0200 Subject: [PATCH 073/168] Updated SQL Files * Updated upgrade SQL * Added full new structure --- sql/issue_144_notification_upgrade.sql | 1 + sql/mmcfe_ng_structure.sql | 30 +++++++++++++++++++------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/sql/issue_144_notification_upgrade.sql b/sql/issue_144_notification_upgrade.sql index a47f3bbf..fd7ae177 100644 --- a/sql/issue_144_notification_upgrade.sql +++ b/sql/issue_144_notification_upgrade.sql @@ -1,4 +1,5 @@ ALTER TABLE `notifications` ADD `account_id` INT UNSIGNED NULL DEFAULT NULL , ADD INDEX ( `account_id` ); +ALTER TABLE `notifications` CHANGE `time` `time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; CREATE TABLE IF NOT EXISTS `notification_settings` ( `type` varchar(15) NOT NULL, `account_id` int(11) NOT NULL, diff --git a/sql/mmcfe_ng_structure.sql b/sql/mmcfe_ng_structure.sql index 039b9a04..1e21bbf4 100644 --- a/sql/mmcfe_ng_structure.sql +++ b/sql/mmcfe_ng_structure.sql @@ -3,7 +3,7 @@ -- http://www.phpmyadmin.net -- -- Host: localhost --- Generation Time: Jun 07, 2013 at 03:39 PM +-- Generation Time: Jun 09, 2013 at 04:08 PM -- Server version: 5.5.31-0ubuntu0.13.04.1 -- PHP Version: 5.4.9-4ubuntu2 @@ -65,7 +65,7 @@ CREATE TABLE IF NOT EXISTS `blocks` ( PRIMARY KEY (`id`), UNIQUE KEY `height` (`height`,`blockhash`), KEY `time` (`time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Discovered blocks persisted from Litecoin Service'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Discovered blocks persisted from Litecoin Service'; -- -------------------------------------------------------- @@ -74,14 +74,28 @@ CREATE TABLE IF NOT EXISTS `blocks` ( -- CREATE TABLE IF NOT EXISTS `notifications` ( - `id` int(11) NOT NULL AUTO_INCREMENT, + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `type` varchar(25) NOT NULL, `data` varchar(255) NOT NULL, `active` tinyint(1) NOT NULL DEFAULT '1', - `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `account_id` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`id`), KEY `active` (`active`), - KEY `data` (`data`) + KEY `data` (`data`), + KEY `account_id` (`account_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `notification_settings` +-- + +CREATE TABLE IF NOT EXISTS `notification_settings` ( + `type` varchar(15) NOT NULL, + `account_id` int(11) NOT NULL, + `active` tinyint(1) NOT NULL DEFAULT '0' ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -------------------------------------------------------- @@ -153,7 +167,7 @@ CREATE TABLE IF NOT EXISTS `shares_archive` ( PRIMARY KEY (`id`), UNIQUE KEY `share_id` (`share_id`), KEY `time` (`time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes'; -- -------------------------------------------------------- @@ -170,7 +184,7 @@ CREATE TABLE IF NOT EXISTS `statistics_shares` ( PRIMARY KEY (`id`), KEY `account_id` (`account_id`), KEY `block_id` (`block_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -------------------------------------------------------- @@ -190,7 +204,7 @@ CREATE TABLE IF NOT EXISTS `transactions` ( KEY `block_id` (`block_id`), KEY `account_id` (`account_id`), KEY `type` (`type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; From bbffdeb689bca15b96abf07027df9558965da71e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 9 Jun 2013 16:38:12 +0200 Subject: [PATCH 074/168] Allow pagination on Transaction page * Added pager to all three transaction tables * Added two additional pagination definitions to JS * Added pagination ID to pagination template --- public/site_assets/mmcFE/js/custom.js | 6 ++++++ public/templates/mmcFE/account/transactions/default.tpl | 9 ++++++--- public/templates/mmcFE/global/pagination.tpl | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/public/site_assets/mmcFE/js/custom.js b/public/site_assets/mmcFE/js/custom.js index 2070bd82..fcecd8e7 100644 --- a/public/site_assets/mmcFE/js/custom.js +++ b/public/site_assets/mmcFE/js/custom.js @@ -69,6 +69,12 @@ $(function () { $("table.pagesort") .tablesorter({ widgets: ['zebra'] }) .tablesorterPager({ positionFixed: false, container: $("#pager") }); + $("table.pagesort2") + .tablesorter({ widgets: ['zebra'] }) + .tablesorterPager({ positionFixed: false, container: $("#pager2") }); + $("table.pagesort4") + .tablesorter({ widgets: ['zebra'] }) + .tablesorterPager({ positionFixed: false, container: $("#pager3") }); $('.block table tr th.header').css('cursor', 'pointer'); diff --git a/public/templates/mmcFE/account/transactions/default.tpl b/public/templates/mmcFE/account/transactions/default.tpl index 8f5282e1..88f749c1 100644 --- a/public/templates/mmcFE/account/transactions/default.tpl +++ b/public/templates/mmcFE/account/transactions/default.tpl @@ -1,7 +1,8 @@ {include file="global/block_header.tpl" BLOCK_HEADER="Transaction Log" BUTTONS=array(Confirmed,Unconfirmed,Orphan)}
    - + {include file="global/pagination.tpl"} +
    @@ -42,7 +43,8 @@
    -
    TX #
    + {include file="global/pagination.tpl" ID=2} +
    @@ -86,7 +88,8 @@
    -
    TX #
    + {include file="global/pagination.tpl"} +
    diff --git a/public/templates/mmcFE/global/pagination.tpl b/public/templates/mmcFE/global/pagination.tpl index 0bae6db5..22bdb0e3 100644 --- a/public/templates/mmcFE/global/pagination.tpl +++ b/public/templates/mmcFE/global/pagination.tpl @@ -1,4 +1,4 @@ -
    +
    From 6eeff19ff58cd8b1dcba5ccb22c6da47be260dd3 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 9 Jun 2013 16:44:32 +0200 Subject: [PATCH 075/168] Fixing issue with notification cron * Updated getAllActive to search by type --- cronjobs/notifications.php | 2 +- public/include/classes/notification.class.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cronjobs/notifications.php b/cronjobs/notifications.php index cd8c020a..a6b8b101 100755 --- a/cronjobs/notifications.php +++ b/cronjobs/notifications.php @@ -38,7 +38,7 @@ if (empty($aWorkers)) { } // We notified, lets check which recovered -$aNotifications = $notification->getAllActive(); +$aNotifications = $notification->getAllActive('idle_worker'); if (!empty($aNotifications)) { foreach ($aNotifications as $aNotification) { $aData = json_decode($aNotification['data'], true); diff --git a/public/include/classes/notification.class.php b/public/include/classes/notification.class.php index d324d4a7..b38bac2e 100644 --- a/public/include/classes/notification.class.php +++ b/public/include/classes/notification.class.php @@ -50,10 +50,10 @@ class Notification extends Mail { /** * Get all active notifications **/ - public function getAllActive() { + public function getAllActive($strType) { $this->debug->append("STA " . __METHOD__, 4); - $stmt =$this->mysqli->prepare("SELECT id, data FROM $this->table WHERE active = 1 LIMIT 1"); - if ($stmt && $stmt->execute() && $result = $stmt->get_result()) + $stmt =$this->mysqli->prepare("SELECT id, data FROM $this->table WHERE active = 1 AND type = ?"); + if ($stmt && $stmt->bind_param('s', $strType) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); // Catchall return false; From c926eabf8dbed1ec13194dbdcc3b745e2526485d Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 21:37:14 +0200 Subject: [PATCH 076/168] Adding support for PPS payout method This commit changed a few things in the backend and classes code: * Any _PPS transaction does NOT need to be confirmed * Queries updated for added _PPS transactions * Template updated to properly display these transactions Cronjob * Added pps_payput cron to run payouts based on worker submitted shares * **IMPORTANT**: Can NOT be run with proportional_payout! Addresses #70 --- cronjobs/pps_payout.php | 87 +++++++++++++++++++ public/include/classes/share.class.php | 15 ++++ public/include/classes/transaction.class.php | 18 ++-- .../mmcFE/account/transactions/default.tpl | 5 +- 4 files changed, 118 insertions(+), 7 deletions(-) create mode 100755 cronjobs/pps_payout.php diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php new file mode 100755 index 00000000..a71c42a3 --- /dev/null +++ b/cronjobs/pps_payout.php @@ -0,0 +1,87 @@ +#!/usr/bin/php +can_connect() === true ){ + $dDifficulty = $bitcoin->getdifficulty(); +} else { + verbose("Aborted: " . $bitcoin->can_connect() . "\n"); + exit(1); +} + +// Value per share calculation +$pps_value = 50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']); + +// Find our last share accounted and last inserted share for PPS calculations +$iPreviousShareId = $setting->getValue('pps_last_share_id'); +$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\t\tPayout\t\tDonation\tFee\t\tStatus\n"); + +foreach ($aAccountShares as $aData) { + // Take our valid shares and multiply by per share value + $aData['payout'] = $aData['valid'] * $pps_value; + + // Defaults + $aData['fee' ] = 0; + $aData['donation'] = 0; + + // Calculate block fees + if ($config['fees'] > 0) + $aData['fee'] = number_format(round($config['fees'] / 100 * $aData['payout'], 8), 8); + // 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" . + $aData['username'] . "\t" . + $aData['invalid'] . "\t" . + $aData['valid'] . "\t*\t" . + $pps_value . "\t=\t" . + $aData['payout'] . "\t" . + $aData['donation'] . "\t" . + $aData['fee'] . "\t"); + + $strStatus = "OK"; + // Add new credit transaction + if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit_PPS')) + $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_PPS')) + $strStatus = "Fee Failed"; + // Add new donation debit + if ($aData['donation'] > 0) + if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation_PPS')) + $strStatus = "Donation Failed"; + verbose($strStatus . "\n"); +} + +// Store our last inserted ID for the next run +$setting->setValue('pps_last_share_id', $iLastShareId); + +verbose("\n\n------------------------------------------------------------------------------------\n\n"); +?> diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index b0a3b249..1033f92c 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -44,6 +44,21 @@ class Share { return $this->table; } + /** + * Get last inserted Share ID from Database + * Used for PPS calculations without moving to archive + **/ + public function getLastInsertedShareId() { + $stmt = $this->mysqli->prepare(" + SELECT MAX(id) AS id FROM $this->table + "); + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_object()->id; + // Catchall + $this->setErrorMessage('Failed to fetch last inserted share ID'); + return false; + } + /** * Get all valid shares for this round * @param previous_upstream int Previous found share accepted by upstream to limit results diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index a487e5ec..939684c4 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -143,7 +143,7 @@ class Transaction { 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 = 'Credit' + WHERE t.type IN ('Credit', 'Credit_PPS') AND b.confirmations >= " . $this->config['confirmations'] . " ) AS t1, ( @@ -155,7 +155,7 @@ class Transaction { 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') + WHERE t.type IN ('Donation','Fee','Donation_PPS','Fee_PPS') AND b.confirmations >= " . $this->config['confirmations'] . " ) AS t3"); if ($this->checkStmt($stmt) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch()) @@ -180,8 +180,11 @@ class Transaction { 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 = 'Credit' - AND b.confirmations >= ? + WHERE + ( + ( t.type = 'Credit' AND b.confirmations >= ? ) OR + ( t.type = 'Credit_PPS' ) + ) AND t.account_id = ? ) AS t1, ( @@ -194,8 +197,11 @@ class Transaction { 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 >= ? + WHERE + ( + ( t.type IN ('Donation','Fee') AND b.confirmations >= ? ) OR + ( t.type IN ('Donation_PPS', 'Fee_PPS') ) + ) AND t.account_id = ? ) AS t3 "); diff --git a/public/templates/mmcFE/account/transactions/default.tpl b/public/templates/mmcFE/account/transactions/default.tpl index 88f749c1..c1433f3c 100644 --- a/public/templates/mmcFE/account/transactions/default.tpl +++ b/public/templates/mmcFE/account/transactions/default.tpl @@ -19,6 +19,9 @@ ($TRANSACTIONS[transaction].type == 'Credit' 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' )} @@ -28,7 +31,7 @@
    - + {/if} {/section} From 2c4bf7d4aa237deca0c760f212371b9389bb3c21 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:31:35 +0200 Subject: [PATCH 077/168] properly format payout and round it --- 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 a71c42a3..1dafa288 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -44,7 +44,7 @@ verbose("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\t\tPayout\t\tDonation\tFee\ foreach ($aAccountShares as $aData) { // Take our valid shares and multiply by per share value - $aData['payout'] = $aData['valid'] * $pps_value; + $aData['payout'] = number_format(round($aData['valid'] * $pps_value, 8)); // Defaults $aData['fee' ] = 0; From e62a3527dbeae989ba9a34c7b211b777df1c188c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:34:02 +0200 Subject: [PATCH 078/168] forgot 8 decimals --- 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 1dafa288..bef78f2a 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -44,7 +44,7 @@ verbose("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\t\tPayout\t\tDonation\tFee\ foreach ($aAccountShares as $aData) { // Take our valid shares and multiply by per share value - $aData['payout'] = number_format(round($aData['valid'] * $pps_value, 8)); + $aData['payout'] = number_format(round($aData['valid'] * $pps_value, 8), 8); // Defaults $aData['fee' ] = 0; From 638e33e52534840afadebd4be6a46f66ddffb79f Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:45:53 +0200 Subject: [PATCH 079/168] properly format pps value to 12 digits --- 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 bef78f2a..e2804595 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -31,7 +31,7 @@ if ( $bitcoin->can_connect() === true ){ } // Value per share calculation -$pps_value = 50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']); +$pps_value = number_format(round(50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); // Find our last share accounted and last inserted share for PPS calculations $iPreviousShareId = $setting->getValue('pps_last_share_id'); From 20c3d771e6e3e88f42015c3b9dbd970bffa4eb21 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:48:09 +0200 Subject: [PATCH 080/168] moved table header to the left --- 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 e2804595..427035d5 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -40,7 +40,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\t\tPayout\t\tDonation\tFee\t\tStatus\n"); +verbose("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\tPayout\t\tDonation\tFee\t\tStatus\n"); foreach ($aAccountShares as $aData) { // Take our valid shares and multiply by per share value From ae0252d927153d08e4338fca34c7f26765eae783 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 9 Jun 2013 17:29:38 +0200 Subject: [PATCH 081/168] adding PPS value to sidebard --- public/include/smarty_globals.inc.php | 6 ++++++ public/templates/mmcFE/global/sidebar.tpl | 13 +++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index fa5c7923..d37e45cc 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -12,12 +12,18 @@ $aRoundShares = $statistics->getRoundShares(); $iCurrentActiveWorkers = $worker->getCountAllActiveWorkers(); $iCurrentPoolHashrate = $statistics->getCurrentHashrate(); $iCurrentPoolShareRate = $statistics->getCurrentShareRate(); +if ($bitcoin->can_connect() === true){ + $dDifficulty = $bitcoin->query('getdifficulty'); +} else { + $dDifficulty = 1; +} $aGlobal = array( 'slogan' => $config['website']['slogan'], 'websitename' => $config['website']['name'], 'hashrate' => $iCurrentPoolHashrate, 'sharerate' => $iCurrentPoolShareRate, + 'ppsvalue' => number_format(round(50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12), 'workers' => $iCurrentActiveWorkers, 'roundshares' => $aRoundShares, 'fees' => $config['fees'], diff --git a/public/templates/mmcFE/global/sidebar.tpl b/public/templates/mmcFE/global/sidebar.tpl index 21a8e0aa..05f6e00e 100644 --- a/public/templates/mmcFE/global/sidebar.tpl +++ b/public/templates/mmcFE/global/sidebar.tpl @@ -6,8 +6,17 @@
    TX #{$TRANSACTIONS[transaction].type} {$TRANSACTIONS[transaction].coin_address} {if $TRANSACTIONS[transaction].height == 0}n/a{else}{$TRANSACTIONS[transaction].height}{/if}{$TRANSACTIONS[transaction].amount}{$TRANSACTIONS[transaction].amount}
    - - + + + + + + + + + + + From 8ed4638057fac16b7f40b4eef6e7d54e45091d6b Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 08:51:07 +0200 Subject: [PATCH 082/168] removed commented section to re-enable auto-payout --- cronjobs/auto_payout.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cronjobs/auto_payout.php b/cronjobs/auto_payout.php index 48861c86..63b06d08 100755 --- a/cronjobs/auto_payout.php +++ b/cronjobs/auto_payout.php @@ -52,13 +52,13 @@ if (! empty($users)) { } // Send balance, fees are reduced later -/* try { + try { $bitcoin->sendtoaddress($aUserData['coin_address'], $dBalance); } catch (BitcoinClientException $e) { verbose("SEND FAILED\n"); continue; } - */ + // Create transaction record if ($transaction->addTransaction($aUserData['id'], $dBalance, 'Debit_AP', NULL, $aUserData['coin_address'], 0.1)) { // Notify user via mail From 37fc9fa1471113f0211a35b83015e54238c05ba8 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 08:51:24 +0200 Subject: [PATCH 083/168] adding full notification SQL for regular installations --- sql/issue_144_notification_upgrade.sql | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/sql/issue_144_notification_upgrade.sql b/sql/issue_144_notification_upgrade.sql index fd7ae177..73985979 100644 --- a/sql/issue_144_notification_upgrade.sql +++ b/sql/issue_144_notification_upgrade.sql @@ -1,7 +1,17 @@ -ALTER TABLE `notifications` ADD `account_id` INT UNSIGNED NULL DEFAULT NULL , ADD INDEX ( `account_id` ); -ALTER TABLE `notifications` CHANGE `time` `time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; +CREATE TABLE IF NOT EXISTS `notifications` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `type` varchar(25) NOT NULL, + `data` varchar(255) NOT NULL, + `active` tinyint(1) NOT NULL DEFAULT '1', + `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `account_id` int(10) unsigned DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `active` (`active`), + KEY `data` (`data`), + KEY `account_id` (`account_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `notification_settings` ( - `type` varchar(15) NOT NULL, - `account_id` int(11) NOT NULL, - `active` tinyint(1) NOT NULL DEFAULT '0' + `type` varchar(15) NOT NULL, + `account_id` int(11) NOT NULL, + `active` tinyint(1) NOT NULL DEFAULT '0' ) ENGINE=InnoDB DEFAULT CHARSET=utf8; From 4e284895a8547403d5f8a8cf41ae61537f3fc8fb Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 10:11:56 +0200 Subject: [PATCH 084/168] Adding support for block finder bonus * Added new configuration option `block_bonus`, see `global.inc.dist.php`, default 0 * Added new transaction types: `Bonus` and `Orphan_Bonus` * Changes transaction table structure, added upgrade SQL * Changed findblock cron to credit bonus to finder * Modified transactions class to reflect changes Fixes #148 --- cronjobs/findblock.php | 18 ++++--- public/include/classes/transaction.class.php | 50 +++++++------------ public/include/config/global.inc.dist.php | 1 + .../mmcFE/account/transactions/default.tpl | 15 +++--- sql/issue_148_transactions_upgrade.sql | 1 + 5 files changed, 41 insertions(+), 44 deletions(-) create mode 100644 sql/issue_148_transactions_upgrade.sql diff --git a/cronjobs/findblock.php b/cronjobs/findblock.php index ead4b61c..b5fdca28 100755 --- a/cronjobs/findblock.php +++ b/cronjobs/findblock.php @@ -102,6 +102,9 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $strStatus = "Finder Failed"; if (!$block->setShares($aBlock['id'], $iRoundShares)) $strStatus = "Shares Failed"; + if ($config['block_bonus'] > 0 && !$transaction->addTransaction($iAccountId, $config['block_bonus'], 'Bonus', $aBlock['id'])) { + $strStatus = "Bonus Failed"; + } verbose( $aBlock['id'] . "\t\t" @@ -115,13 +118,16 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { // Notify users $aAccounts = $notification->getNotificationAccountIdByType('new_block'); - foreach ($aAccounts as $account_id) { - $aMailData['height'] = $aBlock['height']; - $aMailData['subject'] = 'New Block'; - $aMailData['email'] = $user->getUserEmail($user->getUserName($account_id)); - $aMailData['shares'] = $iRoundShares; - $notification->sendNotification($account_id, 'new_block', $aMailData); + if (is_array($aAccounts)) { + foreach ($aAccounts as $account_id) { + $aMailData['height'] = $aBlock['height']; + $aMailData['subject'] = 'New Block'; + $aMailData['email'] = $user->getUserEmail($user->getUserName($account_id)); + $aMailData['shares'] = $iRoundShares; + $notification->sendNotification($account_id, 'new_block', $aMailData); + } } + break; } } ?> diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index a487e5ec..d12d14ee 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -55,35 +55,23 @@ class Transaction { **/ public function setOrphan($block_id) { $this->debug->append("STA " . __METHOD__, 4); - $stmt = $this->mysqli->prepare(" - UPDATE $this->table - SET type = 'Orphan_Credit' - WHERE type = 'Credit' - AND block_id = ? - "); - if (!($this->checkStmt($stmt) && $stmt->bind_param('i', $block_id) && $stmt->execute())) { - $this->debug->append("Failed to set orphan credit transactions for $block_id"); - return false; - } - $stmt = $this->mysqli->prepare(" - UPDATE $this->table - SET type = 'Orphan_Fee' - WHERE type = 'Fee' - AND block_id = ? - "); - if (!($this->checkStmt($stmt) && $stmt->bind_param('i', $block_id) && $stmt->execute())) { - $this->debug->append("Failed to set orphan fee transactions for $block_id"); - return false; - } - $stmt = $this->mysqli->prepare(" - UPDATE $this->table - SET type = 'Orphan_Donation' - WHERE type = 'Donation' - AND block_id = ? - "); - if (!($this->checkStmt($stmt) && $stmt->bind_param('i', $block_id) && $stmt->execute())) { - $this->debug->append("Failed to set orphan donation transactions for $block_id"); - return false; + $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; } @@ -143,7 +131,7 @@ class Transaction { 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 = 'Credit' + WHERE t.type IN ('Credit','Bonus') AND b.confirmations >= " . $this->config['confirmations'] . " ) AS t1, ( @@ -180,7 +168,7 @@ class Transaction { 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 = 'Credit' + WHERE t.type IN ('Credit','Bonus') AND b.confirmations >= ? AND t.account_id = ? ) AS t1, diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 306d5261..f1cb0cbe 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -34,6 +34,7 @@ $config = array( 'slogan' => 'Resistance is futile', 'email' => 'test@example.com', // Mail address used for notifications ), + 'block_bonus' => 0, 'archive_shares' => true, // Store accounted shares in archive table? 'blockexplorer' => 'http://explorer.litecoin.net/search?q=', // URL for block searches, prefixed to each block number 'chaininfo' => 'http://allchains.info', // Link to Allchains for Difficulty information diff --git a/public/templates/mmcFE/account/transactions/default.tpl b/public/templates/mmcFE/account/transactions/default.tpl index 88f749c1..2e6ae074 100644 --- a/public/templates/mmcFE/account/transactions/default.tpl +++ b/public/templates/mmcFE/account/transactions/default.tpl @@ -16,7 +16,7 @@ {section transaction $TRANSACTIONS} {if ( - ($TRANSACTIONS[transaction].type == 'Credit' and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations) + (($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 == 'Debit_AP' @@ -28,7 +28,7 @@ - + {/if} {/section} @@ -58,7 +58,7 @@ {section transaction $TRANSACTIONS} {if ( - $TRANSACTIONS[transaction].type == 'Credit' && $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations + ($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) )} @@ -68,9 +68,9 @@ - + - {if $TRANSACTIONS[transaction].type == Credit} + {if $TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus'} {assign var="credits" value="`$credits+$TRANSACTIONS[transaction].amount`"} {else} {assign var="debits" value="`$debits+$TRANSACTIONS[transaction].amount`"} @@ -106,6 +106,7 @@ $TRANSACTIONS[transaction].type == 'Orphan_Credit' or $TRANSACTIONS[transaction].type == 'Orphan_Donation' or $TRANSACTIONS[transaction].type == 'Orphan_Fee' + or $TRANSACTIONS[transaction].type == 'Orphan_Bonus' )} @@ -113,9 +114,9 @@ - + - {if $TRANSACTIONS[transaction].type == Orphan_Credit} + {if $TRANSACTIONS[transaction].type == 'Orphan_Credit' or $TRANSACTIONS[transaction].type == 'Orphan_Bonus'} {assign var="orphan_credits" value="`$orphan_credits+$TRANSACTIONS[transaction].amount`"} {else} {assign var="orphan_debits" value="`$orphan_debits+$TRANSACTIONS[transaction].amount`"} diff --git a/sql/issue_148_transactions_upgrade.sql b/sql/issue_148_transactions_upgrade.sql new file mode 100644 index 00000000..2c0a47a7 --- /dev/null +++ b/sql/issue_148_transactions_upgrade.sql @@ -0,0 +1 @@ +ALTER TABLE `transactions` CHANGE `type` `type` ENUM( 'Credit', 'Debit_MP', 'Debit_AP', 'Donation', 'Fee', 'Orphan_Credit', 'Orphan_Fee', 'Orphan_Donation', 'Bonus', 'Orphan_Bonus' ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL; From 37beca938543bbb6f5c59b1b921ba9b83047e2e0 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 10:27:39 +0200 Subject: [PATCH 085/168] removing left over debug in findblock --- cronjobs/findblock.php | 1 - 1 file changed, 1 deletion(-) diff --git a/cronjobs/findblock.php b/cronjobs/findblock.php index b5fdca28..0538d94c 100755 --- a/cronjobs/findblock.php +++ b/cronjobs/findblock.php @@ -127,7 +127,6 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $notification->sendNotification($account_id, 'new_block', $aMailData); } } - break; } } ?> From 463f199040683e112bc36c7b46933008759b96b9 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 11:31:49 +0300 Subject: [PATCH 086/168] Update edit.inc.php Emergency fix: Transactions are not aborted if RPC transfer failed. --- 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 446c13f1..014bad0b 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -36,7 +36,7 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST } } catch (BitcoinClientException $e) { $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to send LTC, please contact site support immidiately', 'TYPE' => 'errormsg'); -// $continue = false; + $continue = false; } } // Set balance to 0, add to paid out, insert to ledger From b1f3c9bece12cf00cca2196e681f906ae523b326 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 10:38:05 +0200 Subject: [PATCH 087/168] Fixing manual payout race condition * Mark manual payout active * Run payout logics * Reset manual payout This ensures only one manual transaction can be run at a time. If any users starts a manual payout others have to wait until the site completed loading and finished the transaction process. As long as we don't have too many users doing a manual payout at the same time this should not be an issue. Best for users is using auto payouts anyway. This addresses #149 --- public/include/pages/account/edit.inc.php | 70 ++++++++++++----------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php index 014bad0b..e47ad6dd 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -13,45 +13,51 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST } else { switch ($_POST['do']) { case 'cashOut': - $continue = true; - $dBalance = $transaction->getBalance($_SESSION['USERDATA']['id']); - $sCoinAddress = $user->getCoinAddress($_SESSION['USERDATA']['id']); - // Ensure we can cover the potential transaction fee of 0.1 LTC with the balance - if ($dBalance > 0.1) { - if ($bitcoin->can_connect() === true) { - try { - $bitcoin->validateaddress($sCoinAddress); - } catch (BitcoinClientException $e) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Invalid payment address: ' . $sUserSendAddress, 'TYPE' => 'errormsg'); - $continue = false; - } - if ($continue == true) { - // Send balance to address, mind 0.1 fee for transaction! + if ($setting->getValue('manual_payout_active') == 1) { + $_SESSION['POPUP'][] = array('CONTENT' => 'A manual payout is in progress. Please try again later.', 'TYPE' => 'errormsg'); + } else { + $setting->setValue('manual_payout_active', 1); + $continue = true; + $dBalance = $transaction->getBalance($_SESSION['USERDATA']['id']); + $sCoinAddress = $user->getCoinAddress($_SESSION['USERDATA']['id']); + // Ensure we can cover the potential transaction fee of 0.1 LTC with the balance + if ($dBalance > 0.1) { + if ($bitcoin->can_connect() === true) { try { - if ($setting->getValue('auto_payout_active') == 0) { - $bitcoin->sendtoaddress($sCoinAddress, $dBalance); - } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Auto-payout active, please contact site support immidiately to revoke invalid transactions.', 'TYPE' => 'errormsg'); - $continue = false; - } + $bitcoin->validateaddress($sCoinAddress); } catch (BitcoinClientException $e) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to send LTC, please contact site support immidiately', 'TYPE' => 'errormsg'); + $_SESSION['POPUP'][] = array('CONTENT' => 'Invalid payment address: ' . $sUserSendAddress, 'TYPE' => 'errormsg'); $continue = false; } + if ($continue == true) { + // Send balance to address, mind 0.1 fee for transaction! + try { + if ($setting->getValue('auto_payout_active') == 0) { + $bitcoin->sendtoaddress($sCoinAddress, $dBalance); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'Auto-payout active, please contact site support immidiately to revoke invalid transactions.', 'TYPE' => 'errormsg'); + $continue = false; + } + } catch (BitcoinClientException $e) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to send LTC, please contact site support immidiately', 'TYPE' => 'errormsg'); + $continue = false; + } + } + // Set balance to 0, add to paid out, insert to ledger + if ($continue == true && $transaction->addTransaction($_SESSION['USERDATA']['id'], $dBalance, 'Debit_MP', NULL, $sCoinAddress)) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Transaction completed', 'TYPE' => 'success'); + $aMailData['email'] = $user->getUserEmail($user->getUserName($_SESSION['USERDATA']['id'])); + $aMailData['amount'] = $dBalance; + $aMailData['subject'] = 'Manual Payout Completed'; + $notification->sendNotification($_SESSION['USERDATA']['id'], 'manual_payout', $aMailData); + } + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service', 'TYPE' => 'errormsg'); } - // Set balance to 0, add to paid out, insert to ledger - if ($continue == true && $transaction->addTransaction($_SESSION['USERDATA']['id'], $dBalance, 'Debit_MP', NULL, $sCoinAddress)) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Transaction completed', 'TYPE' => 'success'); - $aMailData['email'] = $user->getUserEmail($user->getUserName($_SESSION['USERDATA']['id'])); - $aMailData['amount'] = $dBalance; - $aMailData['subject'] = 'Manual Payout Completed'; - $notification->sendNotification($_SESSION['USERDATA']['id'], 'manual_payout', $aMailData); - } } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service', 'TYPE' => 'errormsg'); + $_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than 0.1 LTC to cover transaction fees', 'TYPE' => 'errormsg'); } - } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than 0.1 LTC to cover transaction fees', 'TYPE' => 'errormsg'); + $setting->setValue('manual_payout_active', 0); } break; From 9e74175ca3f1211c75ca2bf7699c856652d1fd56 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 10:45:46 +0200 Subject: [PATCH 088/168] Make email mandatory during registration Partially addresses #142 --- public/include/classes/user.class.php | 2 +- public/templates/mmcFE/register/default.tpl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index b4627729..7f919502 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -394,7 +394,7 @@ class User { $this->setErrorMessage( 'Password do not match' ); return false; } - if (!empty($email1) && !filter_var($email1, FILTER_VALIDATE_EMAIL)) { + if (empty($email1) || !filter_var($email1, FILTER_VALIDATE_EMAIL)) { $this->setErrorMessage( 'Invalid e-mail address' ); return false; } diff --git a/public/templates/mmcFE/register/default.tpl b/public/templates/mmcFE/register/default.tpl index 93289d32..d4440f1c 100644 --- a/public/templates/mmcFE/register/default.tpl +++ b/public/templates/mmcFE/register/default.tpl @@ -6,8 +6,8 @@ - - + + From b1d698e6864d2a9c0386c430b147e09fb1d5e8bc Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 10:54:57 +0200 Subject: [PATCH 089/168] Allow users to change their email address * Added email to account page * No confirmation required as of yet Fixes #142 --- public/include/classes/user.class.php | 19 +++++++++++-------- public/include/pages/account/edit.inc.php | 2 +- .../templates/mmcFE/account/edit/default.tpl | 1 + 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 7f919502..feb0466f 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -238,7 +238,7 @@ class User { * @param donat float donation % of income * @return bool **/ - public function updateAccount($userID, $address, $threshold, $donate) { + public function updateAccount($userID, $address, $threshold, $donate, $email) { $this->debug->append("STA " . __METHOD__, 4); $bUser = false; @@ -257,18 +257,21 @@ class User { $this->setErrorMessage('Donation above allowed 100% limit'); return false; } + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + $this->setErrorMessage('Invalid email address'); + return false; + } // Number sanitizer, just in case we fall through above $threshold = min($this->config['ap_threshold']['max'], max(0, floatval($threshold))); $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 = ? WHERE id = ?"); - $stmt->bind_param('sddi', $address, $threshold, $donate, $userID); - $stmt->execute(); - if ( $stmt->errno == 0 ) { - $stmt->close(); + $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()) return true; - } + // Catchall + $this->setErrorMessage('Failed to update your account'); + $this->debug->append('Account update failed: ' . $this->mysqli->error); return false; } @@ -356,7 +359,7 @@ class User { $this->debug->append("Fetching user information for user id: $userID"); $stmt = $this->mysqli->prepare(" SELECT - id, username, pin, api_key, admin, + id, username, pin, api_key, admin, 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 e47ad6dd..8d8b1a3d 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -62,7 +62,7 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST break; case 'updateAccount': - if ($user->updateAccount($_SESSION['USERDATA']['id'], $_POST['paymentAddress'], $_POST['payoutThreshold'], $_POST['donatePercent'])) { + if ($user->updateAccount($_SESSION['USERDATA']['id'], $_POST['paymentAddress'], $_POST['payoutThreshold'], $_POST['donatePercent'], $_POST['email'])) { $_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/account/edit/default.tpl b/public/templates/mmcFE/account/edit/default.tpl index 2fc9b4a9..31352e54 100644 --- a/public/templates/mmcFE/account/edit/default.tpl +++ b/public/templates/mmcFE/account/edit/default.tpl @@ -7,6 +7,7 @@ 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)] Automatic Payout Threshold: [{$GLOBAL.config.ap_threshold.min}-{$GLOBAL.config.ap_threshold.max} LTC. Set to '0' for no auto payout] From 31de069533811ebb478079de708088ffc2aafd4e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 11:28:20 +0200 Subject: [PATCH 090/168] Changed getLockedBalance and added SQL * New SQL file for upgrade includes next changes * Properly calculate getLockedBalance based on shares Further addresses #70 --- public/include/classes/transaction.class.php | 16 +++++++++++----- sql/issue_70_transactions_upgrade.sql | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 sql/issue_70_transactions_upgrade.sql diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index 939684c4..4b304797 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -143,8 +143,11 @@ class Transaction { 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', 'Credit_PPS') - AND b.confirmations >= " . $this->config['confirmations'] . " + WHERE + ( + ( t.type = 'Credit' AND b.confirmations >= ? ) OR + ( t.type = 'Credit_PPS' ) + ) ) AS t1, ( SELECT sum(t.amount) AS debit @@ -155,10 +158,13 @@ class Transaction { 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','Donation_PPS','Fee_PPS') - AND b.confirmations >= " . $this->config['confirmations'] . " + WHERE + ( + ( t.type IN ('Donation','Fee') AND b.confirmations >= ? ) OR + ( t.type IN ('Donation_PPS', 'Fee_PPS') ) + ) ) AS t3"); - if ($this->checkStmt($stmt) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch()) + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $this->config['confirmations'], $this->config['confirmations']) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch()) return $dBalance; // Catchall $this->setErrorMessage('Unable to find locked credits for all users'); diff --git a/sql/issue_70_transactions_upgrade.sql b/sql/issue_70_transactions_upgrade.sql new file mode 100644 index 00000000..90561397 --- /dev/null +++ b/sql/issue_70_transactions_upgrade.sql @@ -0,0 +1 @@ +ALTER TABLE `transactions` CHANGE `type` `type` ENUM( 'Credit', 'Debit_MP', 'Debit_AP', 'Donation', 'Fee', 'Orphan_Credit', 'Orphan_Fee', 'Orphan_Donation', 'Bonus', 'Orphan_Bonus', 'Credit_PPS', 'Debit_PPS', 'Donation_PPS' ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL; From ea9f6b2c9a88269801c811135ca3155237671575 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 11:34:20 +0200 Subject: [PATCH 091/168] Adding new config option `payout_system` * Change templates based on the payout system used * Modified sidebar for new PPS method Further addresses #70 --- public/include/config/global.inc.dist.php | 1 + public/include/smarty_globals.inc.php | 1 + public/templates/mmcFE/global/sidebar.tpl | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 306d5261..7e3415f9 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -34,6 +34,7 @@ $config = array( 'slogan' => 'Resistance is futile', 'email' => 'test@example.com', // Mail address used for notifications ), + 'payout_system' => 'pps', // Set your payout here so template changes are activated 'archive_shares' => true, // Store accounted shares in archive table? 'blockexplorer' => 'http://explorer.litecoin.net/search?q=', // URL for block searches, prefixed to each block number 'chaininfo' => 'http://allchains.info', // Link to Allchains for Difficulty information diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index d37e45cc..209f6a71 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -33,6 +33,7 @@ $aGlobal = array( 'blockexplorer' => $config['blockexplorer'], 'chaininfo' => $config['chaininfo'], 'config' => array( + 'payout_system' => $config['payout_system'], 'ap_threshold' => array( 'min' => $config['ap_threshold']['min'], 'max' => $config['ap_threshold']['max'] diff --git a/public/templates/mmcFE/global/sidebar.tpl b/public/templates/mmcFE/global/sidebar.tpl index 05f6e00e..e6736e1a 100644 --- a/public/templates/mmcFE/global/sidebar.tpl +++ b/public/templates/mmcFE/global/sidebar.tpl @@ -13,10 +13,13 @@ Hashrate {$GLOBAL.userdata.hashrate|number_format} KH/s +{if $GLOBAL.config.payout_system == 'pps'} PPS Value {$GLOBAL.ppsvalue} +{/if} +{if $GLOBAL.config.payout_system != 'pps'} Unpaid Shares @@ -28,6 +31,7 @@ Pool Valid {$GLOBAL.roundshares.valid|number_format} +{/if} Round Shares @@ -43,6 +47,7 @@ Your Invalid {$GLOBAL.userdata.shares.invalid|number_format} +{if $GLOBAL.config.payout_system != 'pps'} LTC Round Estimate @@ -62,6 +67,7 @@ Payout {$GLOBAL.userdata.est_payout|number_format:"3"} +{/if}   Account Balance {$GLOBAL.userdata.balance|default:"0"} LTC From 31e51061f7f6fca8f961377c225c0bf836b6383b Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 11:53:44 +0200 Subject: [PATCH 092/168] archive/purge accounted PPS shares --- cronjobs/pps_payout.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php index 427035d5..efdbfc94 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -84,4 +84,22 @@ foreach ($aAccountShares as $aData) { $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"); +} + +// Go through blocks and archive/delete shares that have been accounted for +foreach ($aAllBlocks as $iIndex => $aBlock) { + $dummy = $iIndex - 1; + if ($config['archive_shares'] && $aBlock['share_id'] < $iLastShareId) { + $share->moveArchive($aBlock['share_id'], $aBlock['id'], @$aAllBlocks[$dummy]['share_id']); + } + if ($aBlock['share_id'] < $iLastShareId && !$share->deleteAccountedShares($aBlock['share_id'], @$aAllBlocks[$dummy]['share_id'])) { + verbose("\nERROR : Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . @$aAllBlocks[$dummy]['share_id'] . ", aborting!\n"); + exit(1); + } +} ?> From 56f3d57c35b82f041801f94dfdb242b7e4a43761 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 18:37:14 +0200 Subject: [PATCH 093/168] further improved checkboxes --- public/site_assets/mmcFE/css/style.css | 22 +++++++++++++++++++ .../mmcFE/account/workers/default.tpl | 5 ++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/public/site_assets/mmcFE/css/style.css b/public/site_assets/mmcFE/css/style.css index 8133150a..6243fbfc 100644 --- a/public/site_assets/mmcFE/css/style.css +++ b/public/site_assets/mmcFE/css/style.css @@ -1038,3 +1038,25 @@ a:hover { padding-left:5px; } +/* Custom checkboxes */ +input[type=checkbox] { + display:none; +} + +input[type=checkbox] + label +{ + background: url('../images/error.gif'); + height: 16px; + width: 16px; + display:inline-block; + padding: 0 0 0 0px; +} + +input[type=checkbox]:checked + label +{ + background: url('../images/success.gif'); + height: 16px; + width: 16px; + display:inline-block; + padding: 0 0 0 0px; +} diff --git a/public/templates/mmcFE/account/workers/default.tpl b/public/templates/mmcFE/account/workers/default.tpl index 612bd227..8c127400 100644 --- a/public/templates/mmcFE/account/workers/default.tpl +++ b/public/templates/mmcFE/account/workers/default.tpl @@ -21,7 +21,10 @@ {$username.0|escape}. - + + + + {$WORKERS[worker].hashrate|number_format} From 4745a2f6f8cbd168bad6c8cd9efeab17aefa7725 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 21:37:14 +0200 Subject: [PATCH 094/168] Adding support for PPS payout method This commit changed a few things in the backend and classes code: * Any _PPS transaction does NOT need to be confirmed * Queries updated for added _PPS transactions * Template updated to properly display these transactions Cronjob * Added pps_payput cron to run payouts based on worker submitted shares * **IMPORTANT**: Can NOT be run with proportional_payout! Addresses #70 --- cronjobs/pps_payout.php | 87 +++++++++++++++++++ public/include/classes/share.class.php | 15 ++++ public/include/classes/transaction.class.php | 28 ++++-- .../mmcFE/account/transactions/default.tpl | 5 +- 4 files changed, 125 insertions(+), 10 deletions(-) create mode 100755 cronjobs/pps_payout.php diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php new file mode 100755 index 00000000..a71c42a3 --- /dev/null +++ b/cronjobs/pps_payout.php @@ -0,0 +1,87 @@ +#!/usr/bin/php +can_connect() === true ){ + $dDifficulty = $bitcoin->getdifficulty(); +} else { + verbose("Aborted: " . $bitcoin->can_connect() . "\n"); + exit(1); +} + +// Value per share calculation +$pps_value = 50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']); + +// Find our last share accounted and last inserted share for PPS calculations +$iPreviousShareId = $setting->getValue('pps_last_share_id'); +$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\t\tPayout\t\tDonation\tFee\t\tStatus\n"); + +foreach ($aAccountShares as $aData) { + // Take our valid shares and multiply by per share value + $aData['payout'] = $aData['valid'] * $pps_value; + + // Defaults + $aData['fee' ] = 0; + $aData['donation'] = 0; + + // Calculate block fees + if ($config['fees'] > 0) + $aData['fee'] = number_format(round($config['fees'] / 100 * $aData['payout'], 8), 8); + // 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" . + $aData['username'] . "\t" . + $aData['invalid'] . "\t" . + $aData['valid'] . "\t*\t" . + $pps_value . "\t=\t" . + $aData['payout'] . "\t" . + $aData['donation'] . "\t" . + $aData['fee'] . "\t"); + + $strStatus = "OK"; + // Add new credit transaction + if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit_PPS')) + $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_PPS')) + $strStatus = "Fee Failed"; + // Add new donation debit + if ($aData['donation'] > 0) + if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation_PPS')) + $strStatus = "Donation Failed"; + verbose($strStatus . "\n"); +} + +// Store our last inserted ID for the next run +$setting->setValue('pps_last_share_id', $iLastShareId); + +verbose("\n\n------------------------------------------------------------------------------------\n\n"); +?> diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index b0a3b249..1033f92c 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -44,6 +44,21 @@ class Share { return $this->table; } + /** + * Get last inserted Share ID from Database + * Used for PPS calculations without moving to archive + **/ + public function getLastInsertedShareId() { + $stmt = $this->mysqli->prepare(" + SELECT MAX(id) AS id FROM $this->table + "); + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_object()->id; + // Catchall + $this->setErrorMessage('Failed to fetch last inserted share ID'); + return false; + } + /** * Get all valid shares for this round * @param previous_upstream int Previous found share accepted by upstream to limit results diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index d12d14ee..f1c9907c 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -131,8 +131,10 @@ class Transaction { 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 >= " . $this->config['confirmations'] . " + WHERE ( + ( t.type IN ('Credit','Bonus') AND b.confirmations >= ? ) OR + t.type = 'Credit_PPS' + ) ) AS t1, ( SELECT sum(t.amount) AS debit @@ -143,10 +145,12 @@ class Transaction { 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 >= " . $this->config['confirmations'] . " + WHERE ( + ( t.type IN ('Donation','Fee') AND b.confirmations >= ? ) OR + t.type IN ('Donation_PPS','Fee_PPS') + ) ) AS t3"); - if ($this->checkStmt($stmt) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch()) + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $this->config['confirmations'], $this->config['confirmations']) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch()) return $dBalance; // Catchall $this->setErrorMessage('Unable to find locked credits for all users'); @@ -168,8 +172,11 @@ class Transaction { 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 >= ? + WHERE + ( + ( t.type IN ('Credit','Bonus') AND b.confirmations >= ? ) OR + ( t.type = 'Credit_PPS' ) + ) AND t.account_id = ? ) AS t1, ( @@ -182,8 +189,11 @@ class Transaction { 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 >= ? + WHERE + ( + ( t.type IN ('Donation','Fee') AND b.confirmations >= ? ) OR + ( t.type IN ('Donation_PPS', 'Fee_PPS') ) + ) AND t.account_id = ? ) AS t3 "); diff --git a/public/templates/mmcFE/account/transactions/default.tpl b/public/templates/mmcFE/account/transactions/default.tpl index 2e6ae074..d2393450 100644 --- a/public/templates/mmcFE/account/transactions/default.tpl +++ b/public/templates/mmcFE/account/transactions/default.tpl @@ -19,6 +19,9 @@ (($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' )} @@ -28,7 +31,7 @@ {$TRANSACTIONS[transaction].type} {$TRANSACTIONS[transaction].coin_address} {if $TRANSACTIONS[transaction].height == 0}n/a{else}{$TRANSACTIONS[transaction].height}{/if} - {$TRANSACTIONS[transaction].amount} + {$TRANSACTIONS[transaction].amount} {/if} {/section} From 4acf1194184a4753dc01c1dd9adae7ef117efa94 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:31:35 +0200 Subject: [PATCH 095/168] properly format payout and round it --- 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 a71c42a3..1dafa288 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -44,7 +44,7 @@ verbose("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\t\tPayout\t\tDonation\tFee\ foreach ($aAccountShares as $aData) { // Take our valid shares and multiply by per share value - $aData['payout'] = $aData['valid'] * $pps_value; + $aData['payout'] = number_format(round($aData['valid'] * $pps_value, 8)); // Defaults $aData['fee' ] = 0; From 3f8a1ea0f7a86ee247f2b5b9a1ab581f0c64ca8c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:34:02 +0200 Subject: [PATCH 096/168] forgot 8 decimals --- 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 1dafa288..bef78f2a 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -44,7 +44,7 @@ verbose("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\t\tPayout\t\tDonation\tFee\ foreach ($aAccountShares as $aData) { // Take our valid shares and multiply by per share value - $aData['payout'] = number_format(round($aData['valid'] * $pps_value, 8)); + $aData['payout'] = number_format(round($aData['valid'] * $pps_value, 8), 8); // Defaults $aData['fee' ] = 0; From cb3e02e896f37cbef2ac55d239edb4cd5862ac90 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:45:53 +0200 Subject: [PATCH 097/168] properly format pps value to 12 digits --- 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 bef78f2a..e2804595 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -31,7 +31,7 @@ if ( $bitcoin->can_connect() === true ){ } // Value per share calculation -$pps_value = 50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']); +$pps_value = number_format(round(50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12); // Find our last share accounted and last inserted share for PPS calculations $iPreviousShareId = $setting->getValue('pps_last_share_id'); From 41b35a2d962240408669b6d9a611e02841e4785a Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 6 Jun 2013 23:48:09 +0200 Subject: [PATCH 098/168] moved table header to the left --- 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 e2804595..427035d5 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -40,7 +40,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\t\tPayout\t\tDonation\tFee\t\tStatus\n"); +verbose("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\tPayout\t\tDonation\tFee\t\tStatus\n"); foreach ($aAccountShares as $aData) { // Take our valid shares and multiply by per share value From 666fde91b6d0a9863cc57e39aeff9b62947fae4f Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 9 Jun 2013 17:29:38 +0200 Subject: [PATCH 099/168] adding PPS value to sidebard --- public/include/smarty_globals.inc.php | 6 ++++++ public/templates/mmcFE/global/sidebar.tpl | 13 +++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index fa5c7923..d37e45cc 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -12,12 +12,18 @@ $aRoundShares = $statistics->getRoundShares(); $iCurrentActiveWorkers = $worker->getCountAllActiveWorkers(); $iCurrentPoolHashrate = $statistics->getCurrentHashrate(); $iCurrentPoolShareRate = $statistics->getCurrentShareRate(); +if ($bitcoin->can_connect() === true){ + $dDifficulty = $bitcoin->query('getdifficulty'); +} else { + $dDifficulty = 1; +} $aGlobal = array( 'slogan' => $config['website']['slogan'], 'websitename' => $config['website']['name'], 'hashrate' => $iCurrentPoolHashrate, 'sharerate' => $iCurrentPoolShareRate, + 'ppsvalue' => number_format(round(50 / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12), 'workers' => $iCurrentActiveWorkers, 'roundshares' => $aRoundShares, 'fees' => $config['fees'], diff --git a/public/templates/mmcFE/global/sidebar.tpl b/public/templates/mmcFE/global/sidebar.tpl index 21a8e0aa..05f6e00e 100644 --- a/public/templates/mmcFE/global/sidebar.tpl +++ b/public/templates/mmcFE/global/sidebar.tpl @@ -6,8 +6,17 @@
    - - + + + + + + + + + + + From e8f8b2f5f444efacdd3a8482efb66eccfaae7e80 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 11:28:20 +0200 Subject: [PATCH 100/168] Changed getLockedBalance and added SQL * New SQL file for upgrade includes next changes * Properly calculate getLockedBalance based on shares Further addresses #70 --- public/include/classes/transaction.class.php | 2 +- sql/issue_70_transactions_upgrade.sql | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 sql/issue_70_transactions_upgrade.sql diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index f1c9907c..79d5ba66 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -133,7 +133,7 @@ class Transaction { LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id WHERE ( ( t.type IN ('Credit','Bonus') AND b.confirmations >= ? ) OR - t.type = 'Credit_PPS' + ( t.type = 'Credit_PPS' ) ) ) AS t1, ( diff --git a/sql/issue_70_transactions_upgrade.sql b/sql/issue_70_transactions_upgrade.sql new file mode 100644 index 00000000..90561397 --- /dev/null +++ b/sql/issue_70_transactions_upgrade.sql @@ -0,0 +1 @@ +ALTER TABLE `transactions` CHANGE `type` `type` ENUM( 'Credit', 'Debit_MP', 'Debit_AP', 'Donation', 'Fee', 'Orphan_Credit', 'Orphan_Fee', 'Orphan_Donation', 'Bonus', 'Orphan_Bonus', 'Credit_PPS', 'Debit_PPS', 'Donation_PPS' ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL; From b782857c41cc52147f5d14ea9a0a561e6ee3f746 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 11:34:20 +0200 Subject: [PATCH 101/168] Adding new config option `payout_system` * Change templates based on the payout system used * Modified sidebar for new PPS method Further addresses #70 --- public/include/config/global.inc.dist.php | 1 + public/include/smarty_globals.inc.php | 1 + public/templates/mmcFE/global/sidebar.tpl | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index f1cb0cbe..3d329645 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -35,6 +35,7 @@ $config = array( 'email' => 'test@example.com', // Mail address used for notifications ), 'block_bonus' => 0, + 'payout_system' => 'pps', // Set your payout here so template changes are activated 'archive_shares' => true, // Store accounted shares in archive table? 'blockexplorer' => 'http://explorer.litecoin.net/search?q=', // URL for block searches, prefixed to each block number 'chaininfo' => 'http://allchains.info', // Link to Allchains for Difficulty information diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index d37e45cc..209f6a71 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -33,6 +33,7 @@ $aGlobal = array( 'blockexplorer' => $config['blockexplorer'], 'chaininfo' => $config['chaininfo'], 'config' => array( + 'payout_system' => $config['payout_system'], 'ap_threshold' => array( 'min' => $config['ap_threshold']['min'], 'max' => $config['ap_threshold']['max'] diff --git a/public/templates/mmcFE/global/sidebar.tpl b/public/templates/mmcFE/global/sidebar.tpl index 05f6e00e..e6736e1a 100644 --- a/public/templates/mmcFE/global/sidebar.tpl +++ b/public/templates/mmcFE/global/sidebar.tpl @@ -13,10 +13,13 @@ +{if $GLOBAL.config.payout_system == 'pps'} +{/if} +{if $GLOBAL.config.payout_system != 'pps'} @@ -28,6 +31,7 @@ +{/if} @@ -43,6 +47,7 @@ +{if $GLOBAL.config.payout_system != 'pps'} @@ -62,6 +67,7 @@ +{/if} From b4a09f22eebda4b794c0a65c82df997b22eb2c34 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 11:53:44 +0200 Subject: [PATCH 102/168] archive/purge accounted PPS shares --- cronjobs/pps_payout.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php index 427035d5..efdbfc94 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -84,4 +84,22 @@ foreach ($aAccountShares as $aData) { $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"); +} + +// Go through blocks and archive/delete shares that have been accounted for +foreach ($aAllBlocks as $iIndex => $aBlock) { + $dummy = $iIndex - 1; + if ($config['archive_shares'] && $aBlock['share_id'] < $iLastShareId) { + $share->moveArchive($aBlock['share_id'], $aBlock['id'], @$aAllBlocks[$dummy]['share_id']); + } + if ($aBlock['share_id'] < $iLastShareId && !$share->deleteAccountedShares($aBlock['share_id'], @$aAllBlocks[$dummy]['share_id'])) { + verbose("\nERROR : Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . @$aAllBlocks[$dummy]['share_id'] . ", aborting!\n"); + exit(1); + } +} ?> From 601b5c45cc64852c0c071f60d72899c83ed63904 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 20:09:21 +0300 Subject: [PATCH 103/168] Update README.md Added new features to list: PPS and Notifications --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b8b1f0a8..33551e74 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ The following feature have been implemented so far: * Reward Systems * Propotional - * (Planned) PPS + * PPS **NEW** * (Planned) PPLNS * Use of memcache for statistics instead of a cronjob * Web User accounts @@ -76,6 +76,11 @@ The following feature have been implemented so far: * Wallet information * (Planned) News Posts * (Planned) Pool Donations +* Notification system + * IDLE Workers + * New blocks found in pool + * Auto Payout + * Manual Payou Installation ============ From d2daed331747544a3957dddbeacdba4d7803511d Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 20:21:42 +0200 Subject: [PATCH 104/168] Fixing montoring checkboxes This will fix #163 --- public/include/pages/account/notifications.inc.php | 1 + .../mmcFE/account/notifications/default.tpl | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/public/include/pages/account/notifications.inc.php b/public/include/pages/account/notifications.inc.php index 2ab9c0d0..03294bb7 100644 --- a/public/include/pages/account/notifications.inc.php +++ b/public/include/pages/account/notifications.inc.php @@ -5,6 +5,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); if (!$_SESSION['AUTHENTICATED']) header('Location: index.php?page=home'); if ($_REQUEST['do'] == 'save') { + var_dump($_REQUEST['data']); if ($notification->updateSettings($_SESSION['USERDATA']['id'], $_REQUEST['data'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'Updated notification settings'); } else { diff --git a/public/templates/mmcFE/account/notifications/default.tpl b/public/templates/mmcFE/account/notifications/default.tpl index 676807d3..1d54729b 100644 --- a/public/templates/mmcFE/account/notifications/default.tpl +++ b/public/templates/mmcFE/account/notifications/default.tpl @@ -12,28 +12,32 @@ From da6e217ac2bbac1919033e796946c0c82c618b9a Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 20:23:53 +0200 Subject: [PATCH 105/168] removing debug dump output --- public/include/pages/account/notifications.inc.php | 1 - 1 file changed, 1 deletion(-) diff --git a/public/include/pages/account/notifications.inc.php b/public/include/pages/account/notifications.inc.php index 03294bb7..2ab9c0d0 100644 --- a/public/include/pages/account/notifications.inc.php +++ b/public/include/pages/account/notifications.inc.php @@ -5,7 +5,6 @@ if (!defined('SECURITY')) die('Hacking attempt'); if (!$_SESSION['AUTHENTICATED']) header('Location: index.php?page=home'); if ($_REQUEST['do'] == 'save') { - var_dump($_REQUEST['data']); if ($notification->updateSettings($_SESSION['USERDATA']['id'], $_REQUEST['data'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'Updated notification settings'); } else { From 786926a328ede12abbe7b60d787acb2d2e3df003 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 10 Jun 2013 20:39:41 +0200 Subject: [PATCH 106/168] fixing newline on footer --- public/templates/mmcFE/global/footer.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/templates/mmcFE/global/footer.tpl b/public/templates/mmcFE/global/footer.tpl index cef5c014..2450da79 100644 --- a/public/templates/mmcFE/global/footer.tpl +++ b/public/templates/mmcFE/global/footer.tpl @@ -1,5 +1,5 @@
    - Litecoin Pool using litecoind, pushpoold
    , stratum-mining + Litecoin Pool using litecoind, pushpoold, stratum-mining
    mmcfe-ng Website based on mmcfe by AnnihilaT overhauled by TheSerapher, available on GitHub
    LTC: Lge95QR2frp9y1wJufjUPCycVsg5gLJPW8


    From 8c937cf0b117ce82bfd58ecb442ddfde23cdaeec Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 11 Jun 2013 15:44:16 +0200 Subject: [PATCH 107/168] made prop default payout system in config --- 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 3d329645..df743132 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -35,7 +35,7 @@ $config = array( 'email' => 'test@example.com', // Mail address used for notifications ), 'block_bonus' => 0, - 'payout_system' => 'pps', // Set your payout here so template changes are activated + 'payout_system' => 'prop', // Set your payout here so template changes are activated 'archive_shares' => true, // Store accounted shares in archive table? 'blockexplorer' => 'http://explorer.litecoin.net/search?q=', // URL for block searches, prefixed to each block number 'chaininfo' => 'http://allchains.info', // Link to Allchains for Difficulty information From eb42993c2b61c00922f923f482a64b3297b7f5d8 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 11 Jun 2013 16:32:53 +0200 Subject: [PATCH 108/168] formatting some numbers --- public/templates/mmcFE/global/header.tpl | 6 +++--- public/templates/mmcFE/statistics/pool/authenticated.tpl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/templates/mmcFE/global/header.tpl b/public/templates/mmcFE/global/header.tpl index 19f1a788..126fde52 100644 --- a/public/templates/mmcFE/global/header.tpl +++ b/public/templates/mmcFE/global/header.tpl @@ -4,9 +4,9 @@
    - - - + + +
  • LTC/usd: {$GLOBAL.price|default:"n/a"}    
  • Pool Hashrate: {$GLOBAL.hashrate / 1000} MH/s    
  • Pool Sharerate: {$GLOBAL.sharerate} Shares/s    
  • LTC/usd: {$GLOBAL.price|default:"n/a"|number_format:"4"}    
  • Pool Hashrate: {($GLOBAL.hashrate / 1000)|number_format:"3"} MH/s    
  • Pool Sharerate: {$GLOBAL.sharerate|number_format:"2"} Shares/s    
  • Pool Workers: {$GLOBAL.workers}    
  • diff --git a/public/templates/mmcFE/statistics/pool/authenticated.tpl b/public/templates/mmcFE/statistics/pool/authenticated.tpl index d0ce7803..f8530cd7 100644 --- a/public/templates/mmcFE/statistics/pool/authenticated.tpl +++ b/public/templates/mmcFE/statistics/pool/authenticated.tpl @@ -9,7 +9,7 @@ Pool Hash Rate - {$GLOBAL.hashrate / 1000} Mhash/s + {($GLOBAL.hashrate / 1000)|number_format:"3"} Mhash/s Current Workers Mining From a006ea782a50e8ef0699c13e7d5db39cf69bf6e4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 7 Jun 2013 20:58:50 +0200 Subject: [PATCH 109/168] Adding confirmed and unconfirmed balance * Added SQL code to support both balances * Added sidebar entries * Modified auto_payout to use new index * Mofified account page to use new index Addresses #140 but needs to be tested! --- cronjobs/auto_payout.php | 3 +- public/include/classes/transaction.class.php | 32 ++++++++++++++++--- public/include/pages/account/edit.inc.php | 3 +- public/include/pages/admin/user.inc.php | 3 +- .../templates/mmcFE/account/edit/default.tpl | 2 +- public/templates/mmcFE/global/sidebar.tpl | 3 +- 6 files changed, 37 insertions(+), 9 deletions(-) diff --git a/cronjobs/auto_payout.php b/cronjobs/auto_payout.php index 63b06d08..ca33dc3f 100755 --- a/cronjobs/auto_payout.php +++ b/cronjobs/auto_payout.php @@ -38,7 +38,8 @@ if (! empty($users)) { verbose("UserID\tUsername\tBalance\tThreshold\tAddress\t\t\t\t\tStatus\n\n"); foreach ($users as $aUserData) { - $dBalance = $transaction->getBalance($aUserData['id']); + $aBalance = $transaction->getBalance($aUserData['id']); + $dBalance = $aBalance['confirmed']; verbose($aUserData['id'] . "\t" . $aUserData['username'] . "\t" . $dBalance . "\t" . $aUserData['ap_threshold'] . "\t\t" . $aUserData['coin_address'] . "\t"); // Only run if balance meets threshold and can pay the potential transaction fee diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index 79d5ba66..d4f0b203 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -166,7 +166,9 @@ class Transaction { public function getBalance($account_id) { $this->debug->append("STA " . __METHOD__, 4); $stmt = $this->mysqli->prepare(" - SELECT ROUND(IFNULL(t1.credit, 0) - IFNULL(t2.debit, 0) - IFNULL(t3.other, 0), 8) AS balance + 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 FROM ( SELECT sum(t.amount) AS credit @@ -195,17 +197,39 @@ class Transaction { ( t.type IN ('Donation_PPS', 'Fee_PPS') ) ) AND t.account_id = ? - ) AS t3 + ) AS t3, + ( + 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 = 'Credit' AND b.confirmations < ? ) OR + ( t.type = 'Credit_PPS' ) + ) + AND t.account_id = ? + ) AS t4, + ( + 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 < ? ) OR + ( t.type IN ('Donation_PPS', 'Fee_PPS') ) + ) + AND t.account_id = ? + ) AS t5 "); if ($this->checkStmt($stmt)) { - $stmt->bind_param("iiiii", $this->config['confirmations'], $account_id, $account_id, $this->config['confirmations'], $account_id); + $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); if (!$stmt->execute()) { $this->debug->append("Unable to execute statement: " . $stmt->error); $this->setErrorMessage("Fetching balance failed"); } $result = $stmt->get_result(); $stmt->close(); - return $result->fetch_object()->balance; + return $result->fetch_assoc(); } return false; } diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php index 8d8b1a3d..345816a7 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -18,7 +18,8 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST } else { $setting->setValue('manual_payout_active', 1); $continue = true; - $dBalance = $transaction->getBalance($_SESSION['USERDATA']['id']); + $aBalance = $transaction->getBalance($_SESSION['USERDATA']['id']); + $dBalance = $aBalance['confirmed']; $sCoinAddress = $user->getCoinAddress($_SESSION['USERDATA']['id']); // Ensure we can cover the potential transaction fee of 0.1 LTC with the balance if ($dBalance > 0.1) { diff --git a/public/include/pages/admin/user.inc.php b/public/include/pages/admin/user.inc.php index b6dab89b..8b40ab21 100644 --- a/public/include/pages/admin/user.inc.php +++ b/public/include/pages/admin/user.inc.php @@ -20,7 +20,8 @@ if ($_POST['query']) { // This is not optimized yet, best is a proper SQL // Query against the stats table? Currently cached though. foreach ($aUsers as $iKey => $aUser) { - $aUser['balance'] = $transaction->getBalance($aUser['id']); + $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); diff --git a/public/templates/mmcFE/account/edit/default.tpl b/public/templates/mmcFE/account/edit/default.tpl index 31352e54..86f4f02d 100644 --- a/public/templates/mmcFE/account/edit/default.tpl +++ b/public/templates/mmcFE/account/edit/default.tpl @@ -23,7 +23,7 @@ - +
    Account Balance:    {$GLOBAL.userdata.balance|escape} LTC
    Account Balance:    {$GLOBAL.userdata.balance.confirmed|escape} LTC
    Payout to:
    {$GLOBAL.userdata.coin_address|escape}
    4 digit PIN:
    diff --git a/public/templates/mmcFE/global/sidebar.tpl b/public/templates/mmcFE/global/sidebar.tpl index e6736e1a..6049f9ac 100644 --- a/public/templates/mmcFE/global/sidebar.tpl +++ b/public/templates/mmcFE/global/sidebar.tpl @@ -70,7 +70,8 @@ {/if}   Account Balance - {$GLOBAL.userdata.balance|default:"0"} LTC + Confirmed{$GLOBAL.userdata.balance.confirmed|default:"0"} LTC + Unconfirmed{$GLOBAL.userdata.balance.unconfirmed|default:"0"} LTC
    From 27b4c4473f6578d633caa51d236c6066c0e82e49 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 12 Jun 2013 09:17:50 +0200 Subject: [PATCH 110/168] Adding bonus to unconfirmed calculations --- public/include/classes/transaction.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index d4f0b203..3609de73 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -204,7 +204,7 @@ class Transaction { LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id WHERE ( - ( t.type = 'Credit' AND b.confirmations < ? ) OR + ( t.type IN ('Credit','Bonus') AND b.confirmations < ? ) OR ( t.type = 'Credit_PPS' ) ) AND t.account_id = ? From 8ef44f79dbc5624d7afa60f0437df2d70474fa7e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 12 Jun 2013 11:19:07 +0200 Subject: [PATCH 111/168] Fixing column name in settings table * This will change the name in the settings table for old installations Fixes #167 --- sql/issue_167_settings_upgrade.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 sql/issue_167_settings_upgrade.sql diff --git a/sql/issue_167_settings_upgrade.sql b/sql/issue_167_settings_upgrade.sql new file mode 100644 index 00000000..dff0378a --- /dev/null +++ b/sql/issue_167_settings_upgrade.sql @@ -0,0 +1 @@ +ALTER TABLE `settings` CHANGE `setting` `name` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ; From 4d513318831c14b025a8e1566425426f6dd80103 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 12 Jun 2013 11:31:37 +0200 Subject: [PATCH 112/168] trying new layout for stats page --- public/include/pages/statistics/pool.inc.php | 2 +- .../mmcFE/statistics/blocks/blocks_found.tpl | 18 +++++++++--------- .../mmcFE/statistics/pool/authenticated.tpl | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/public/include/pages/statistics/pool.inc.php b/public/include/pages/statistics/pool.inc.php index ede0933a..76014fe4 100644 --- a/public/include/pages/statistics/pool.inc.php +++ b/public/include/pages/statistics/pool.inc.php @@ -21,7 +21,7 @@ $aContributorsShares = $statistics->getTopContributors('shares', 15); $aContributorsHashes = $statistics->getTopContributors('hashes', 15); // Grab the last 10 blocks found -$iLimit = 10; +$iLimit = 5; $aBlocksFoundData = $statistics->getBlocksFound($iLimit); $aBlockData = $aBlocksFoundData[0]; diff --git a/public/templates/mmcFE/statistics/blocks/blocks_found.tpl b/public/templates/mmcFE/statistics/blocks/blocks_found.tpl index 474fd87c..2278fb3c 100644 --- a/public/templates/mmcFE/statistics/blocks/blocks_found.tpl +++ b/public/templates/mmcFE/statistics/blocks/blocks_found.tpl @@ -3,27 +3,27 @@ - - - - - - + + + + + + {assign var=rank value=1} {section block $BLOCKSFOUND} - - + - + diff --git a/public/templates/mmcFE/statistics/pool/authenticated.tpl b/public/templates/mmcFE/statistics/pool/authenticated.tpl index f8530cd7..5d21e3eb 100644 --- a/public/templates/mmcFE/statistics/pool/authenticated.tpl +++ b/public/templates/mmcFE/statistics/pool/authenticated.tpl @@ -4,7 +4,7 @@ {include file="statistics/pool/contributors_hashrate.tpl"} -{include file="global/block_header.tpl" BLOCK_HEADER="Server Stats" BLOCK_STYLE="clear:all;" STYLE="padding-left:5px;padding-right:5px;"} +{include file="global/block_header.tpl" ALIGN="left" BLOCK_HEADER="Server Stats" BLOCK_STYLE="clear:all;" STYLE="padding-left:5px;padding-right:5px;"}
    BlockValidityFinderDate / TimeDifficultySharesBlockValidityFinderTimeDifficultyShares
    {$BLOCKSFOUND[block].height} + {$BLOCKSFOUND[block].height} {if $BLOCKSFOUND[block].confirmations >= $GLOBAL.confirmations} Confirmed {else if $BLOCKSFOUND[block].confirmations == -1} Orphan {else}{$GLOBAL.confirmations - $BLOCKSFOUND[block].confirmations} left{/if} {$BLOCKSFOUND[block].finder|default:"unknown"}{$BLOCKSFOUND[block].time|date_format:"%d/%m/%Y %H:%M:%S"}{$BLOCKSFOUND[block].time|date_format:"%d/%m/%Y %H:%M:%S"} {$BLOCKSFOUND[block].difficulty|number_format:"8"} {$BLOCKSFOUND[block].shares|number_format}
    @@ -44,6 +44,6 @@ {include file="global/block_footer.tpl"} -{include file="statistics/blocks/blocks_found.tpl"} +{include file="statistics/blocks/blocks_found.tpl" ALIGN="right"} {include file="global/block_footer.tpl"} From 1b850ae67c22ec54a60b8603d891a2304ee86312 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 12 Jun 2013 12:04:44 +0200 Subject: [PATCH 113/168] Changing default statistics URL to pool Fixes #170 --- public/templates/mmcFE/global/navigation.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/templates/mmcFE/global/navigation.tpl b/public/templates/mmcFE/global/navigation.tpl index 3f26387a..84da0388 100644 --- a/public/templates/mmcFE/global/navigation.tpl +++ b/public/templates/mmcFE/global/navigation.tpl @@ -19,7 +19,7 @@ {/if} -
  • Statistics +
  • Statistics
    • Pool Stats
    • {if $smarty.session.AUTHENTICATED|default}
    • Block Stats
    • {/if} From baed854bc9509f63138f2da98c3c65dc0a6e96fc Mon Sep 17 00:00:00 2001 From: lhpool Date: Wed, 12 Jun 2013 19:22:40 +0200 Subject: [PATCH 114/168] Added Usd/Day Added Est Usd/Day to stats --- .../templates/mmcFE/statistics/pool/contributors_hashrate.tpl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl index 232a5ab0..37e6e6a8 100644 --- a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl +++ b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl @@ -7,6 +7,7 @@
  • + @@ -19,6 +20,7 @@ + {/section} {if $listed != 1} @@ -27,6 +29,7 @@ + {/if} From 13fc01243e56bd9499616db7f6c89bec9f1b9b7c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 12 Jun 2013 13:36:44 +0200 Subject: [PATCH 115/168] Adding configurable currency name to global config Addresses #138 --- public/include/config/global.inc.dist.php | 1 + public/include/pages/account/edit.inc.php | 6 +++--- public/include/smarty_globals.inc.php | 2 ++ public/templates/mmcFE/account/edit/default.tpl | 6 +++--- public/templates/mmcFE/global/header.tpl | 2 +- public/templates/mmcFE/global/sidebar.tpl | 8 ++++---- .../mmcFE/statistics/pool/contributors_hashrate.tpl | 4 ++-- 7 files changed, 16 insertions(+), 13 deletions(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index df743132..934df25d 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -34,6 +34,7 @@ $config = array( 'slogan' => 'Resistance is futile', 'email' => 'test@example.com', // Mail address used for notifications ), + 'currency' => 'LTC', // Currency name to be used on website 'block_bonus' => 0, 'payout_system' => 'prop', // Set your payout here so template changes are activated 'archive_shares' => true, // Store accounted shares in archive table? diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php index 345816a7..968ecfbe 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -21,7 +21,7 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST $aBalance = $transaction->getBalance($_SESSION['USERDATA']['id']); $dBalance = $aBalance['confirmed']; $sCoinAddress = $user->getCoinAddress($_SESSION['USERDATA']['id']); - // Ensure we can cover the potential transaction fee of 0.1 LTC with the balance + // Ensure we can cover the potential transaction fee of 0.1 with the balance if ($dBalance > 0.1) { if ($bitcoin->can_connect() === true) { try { @@ -40,7 +40,7 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST $continue = false; } } catch (BitcoinClientException $e) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to send LTC, please contact site support immidiately', 'TYPE' => 'errormsg'); + $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to send ' . $config['currency'] . ', please contact site support immidiately', 'TYPE' => 'errormsg'); $continue = false; } } @@ -56,7 +56,7 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service', 'TYPE' => 'errormsg'); } } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than 0.1 LTC to cover transaction fees', 'TYPE' => 'errormsg'); + $_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than 0.1 ' . $conifg['currency'] . ' to cover transaction fees', 'TYPE' => 'errormsg'); } $setting->setValue('manual_payout_active', 0); } diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index 209f6a71..950f1db6 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -18,6 +18,7 @@ if ($bitcoin->can_connect() === true){ $dDifficulty = 1; } +// Global data for Smarty $aGlobal = array( 'slogan' => $config['website']['slogan'], 'websitename' => $config['website']['name'], @@ -33,6 +34,7 @@ $aGlobal = array( 'blockexplorer' => $config['blockexplorer'], 'chaininfo' => $config['chaininfo'], 'config' => array( + 'currency' => $config['currency'], 'payout_system' => $config['payout_system'], 'ap_threshold' => array( 'min' => $config['ap_threshold']['min'], diff --git a/public/templates/mmcFE/account/edit/default.tpl b/public/templates/mmcFE/account/edit/default.tpl index 86f4f02d..e5c31eab 100644 --- a/public/templates/mmcFE/account/edit/default.tpl +++ b/public/templates/mmcFE/account/edit/default.tpl @@ -10,20 +10,20 @@ - +
    User Name KH/s Ł/Day (est)Usd/Day (est)
    {$CONTRIBHASHES[contrib].account} {$CONTRIBHASHES[contrib].hashrate|number_format} {$estday|number_format:"3"}{$estday|number_format:"3"*$GLOBAL.price|default:"n/a"|number_format:"4"}
    {$GLOBAL.userdata.username} {$GLOBAL.userdata.hashrate} {$estday|number_format:"3"}{$estday|number_format:"3"*$GLOBAL.price|default:"n/a"|number_format:"4"}
    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} LTC. Set to '0' for no auto payout]
    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]
    {include file="global/block_footer.tpl"} {include file="global/block_header.tpl" BLOCK_HEADER="Cash Out"} -
    • Please note: a 0.1 ltc transaction will apply when processing "On-Demand" manual payments
    +
    • Please note: a 0.1 {$GLOBAL.config.currency} transaction will apply when processing "On-Demand" manual payments
    - +
    Account Balance:    {$GLOBAL.userdata.balance.confirmed|escape} LTC
    Account Balance:    {$GLOBAL.userdata.balance.confirmed|escape} {$GLOABL.config.currency}
    Payout to:
    {$GLOBAL.userdata.coin_address|escape}
    4 digit PIN:
    diff --git a/public/templates/mmcFE/global/header.tpl b/public/templates/mmcFE/global/header.tpl index 126fde52..a4b23f4b 100644 --- a/public/templates/mmcFE/global/header.tpl +++ b/public/templates/mmcFE/global/header.tpl @@ -4,7 +4,7 @@
    - + diff --git a/public/templates/mmcFE/global/sidebar.tpl b/public/templates/mmcFE/global/sidebar.tpl index 6049f9ac..34018785 100644 --- a/public/templates/mmcFE/global/sidebar.tpl +++ b/public/templates/mmcFE/global/sidebar.tpl @@ -49,7 +49,7 @@ {if $GLOBAL.config.payout_system != 'pps'} - + @@ -69,9 +69,9 @@ {/if} - - - + + +
  • LTC/usd: {$GLOBAL.price|default:"n/a"|number_format:"4"}    
  • {$GLOBAL.config.currency}/usd: {$GLOBAL.price|default:"n/a"|number_format:"4"}    
  • Pool Hashrate: {($GLOBAL.hashrate / 1000)|number_format:"3"} MH/s    
  • Pool Sharerate: {$GLOBAL.sharerate|number_format:"2"} Shares/s    
  • Pool Workers: {$GLOBAL.workers}    
  • LTC Round Estimate{$GLOBAL.config.currency} Round Estimate
    Block
     
    Account Balance
    Confirmed{$GLOBAL.userdata.balance.confirmed|default:"0"} LTC
    Unconfirmed{$GLOBAL.userdata.balance.unconfirmed|default:"0"} LTC
    {$GLOBAL.config.currency} Account Balance
    Confirmed{$GLOBAL.userdata.balance.confirmed|default:"0"}
    Unconfirmed{$GLOBAL.userdata.balance.unconfirmed|default:"0"}
    diff --git a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl index 37e6e6a8..67c8f5d7 100644 --- a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl +++ b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl @@ -6,8 +6,8 @@ Rank User Name KH/s - Ł/Day (est) - Usd/Day (est) + {$GLOBAL.config.currency}/Day + Usd/Day From a2ad57e92aaa3c122017d8f657ed8b625b33d33e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 12 Jun 2013 13:46:29 +0200 Subject: [PATCH 116/168] Adding configurable TX Fee for web interface * This does not actually check the TX fee set on the RPC side * Ensures TX fee is covered before doing transactions * Display TX fee when required Fixes #138 --- cronjobs/auto_payout.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cronjobs/auto_payout.php b/cronjobs/auto_payout.php index ca33dc3f..366298e1 100755 --- a/cronjobs/auto_payout.php +++ b/cronjobs/auto_payout.php @@ -43,7 +43,7 @@ if (! empty($users)) { verbose($aUserData['id'] . "\t" . $aUserData['username'] . "\t" . $dBalance . "\t" . $aUserData['ap_threshold'] . "\t\t" . $aUserData['coin_address'] . "\t"); // Only run if balance meets threshold and can pay the potential transaction fee - if ($dBalance > $aUserData['ap_threshold'] && $dBalance > 0.1) { + if ($dBalance > $aUserData['ap_threshold'] && $dBalance > $config['txfee']) { // Validate address against RPC try { $bitcoin->validateaddress($aUserData['coin_address']); @@ -52,7 +52,7 @@ if (! empty($users)) { continue; } - // Send balance, fees are reduced later + // Send balance, fees are reduced later by RPC Server try { $bitcoin->sendtoaddress($aUserData['coin_address'], $dBalance); } catch (BitcoinClientException $e) { @@ -61,7 +61,7 @@ if (! empty($users)) { } // Create transaction record - if ($transaction->addTransaction($aUserData['id'], $dBalance, 'Debit_AP', NULL, $aUserData['coin_address'], 0.1)) { + if ($transaction->addTransaction($aUserData['id'], $dBalance, 'Debit_AP', NULL, $aUserData['coin_address'])) { // Notify user via mail $aMailData['email'] = $user->getUserEmail($user->getUserName($aUserData['id'])); $aMailData['subject'] = 'Auto Payout Completed'; @@ -76,7 +76,7 @@ if (! empty($users)) { } } else { - verbose("SKIPPED\n"); + verbose("INSUFF_TXFEE\n"); } } } else { From 8859f0c0915950ceadcc81ecff2d3b275d9009d4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 12 Jun 2013 13:51:10 +0200 Subject: [PATCH 117/168] forgot to add the actual files :/ --- public/include/config/global.inc.dist.php | 1 + public/include/pages/account/edit.inc.php | 8 ++++---- public/include/smarty_globals.inc.php | 1 + public/templates/mmcFE/account/edit/default.tpl | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 934df25d..e8d12774 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -35,6 +35,7 @@ $config = array( 'email' => 'test@example.com', // Mail address used for notifications ), 'currency' => 'LTC', // Currency name to be used on website + 'txfee' => 0.1, // Default tx fee added by RPC server 'block_bonus' => 0, 'payout_system' => 'prop', // Set your payout here so template changes are activated 'archive_shares' => true, // Store accounted shares in archive table? diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php index 968ecfbe..6726b7d9 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -21,8 +21,8 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST $aBalance = $transaction->getBalance($_SESSION['USERDATA']['id']); $dBalance = $aBalance['confirmed']; $sCoinAddress = $user->getCoinAddress($_SESSION['USERDATA']['id']); - // Ensure we can cover the potential transaction fee of 0.1 with the balance - if ($dBalance > 0.1) { + // Ensure we can cover the potential transaction fee + if ($dBalance > $config['txfee']) { if ($bitcoin->can_connect() === true) { try { $bitcoin->validateaddress($sCoinAddress); @@ -31,7 +31,7 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST $continue = false; } if ($continue == true) { - // Send balance to address, mind 0.1 fee for transaction! + // Send balance to address, mind fee for transaction! try { if ($setting->getValue('auto_payout_active') == 0) { $bitcoin->sendtoaddress($sCoinAddress, $dBalance); @@ -56,7 +56,7 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service', 'TYPE' => 'errormsg'); } } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than 0.1 ' . $conifg['currency'] . ' to cover transaction fees', 'TYPE' => 'errormsg'); + $_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than ' . $config['txfee'] . ' ' . $conifg['currency'] . ' to cover transaction fees', 'TYPE' => 'errormsg'); } $setting->setValue('manual_payout_active', 0); } diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index 950f1db6..8ec1e882 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -35,6 +35,7 @@ $aGlobal = array( 'chaininfo' => $config['chaininfo'], 'config' => array( 'currency' => $config['currency'], + 'txfee' => $config['txfee'], 'payout_system' => $config['payout_system'], 'ap_threshold' => array( 'min' => $config['ap_threshold']['min'], diff --git a/public/templates/mmcFE/account/edit/default.tpl b/public/templates/mmcFE/account/edit/default.tpl index e5c31eab..f48ef67f 100644 --- a/public/templates/mmcFE/account/edit/default.tpl +++ b/public/templates/mmcFE/account/edit/default.tpl @@ -17,7 +17,7 @@ {include file="global/block_footer.tpl"} {include file="global/block_header.tpl" BLOCK_HEADER="Cash Out"} -
    • Please note: a 0.1 {$GLOBAL.config.currency} transaction will apply when processing "On-Demand" manual payments
    +
    • Please note: a {$GLOBAL.config.txfee} {$GLOBAL.config.currency} transaction will apply when processing "On-Demand" manual payments
    From 42b6d4b3b29751d91f56e4debf3519c1d26c7122 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 12 Jun 2013 12:57:18 +0200 Subject: [PATCH 118/168] Adding support for user locking * Currently no GUI, use DB access to change the row entry * Upgraded Database table `accounts` with upgrade SQL * Updated `admin` field to `is_admin` as boolean * Modified pages, classes, templates to support is_admin and is_locked Addresses #147 --- public/include/classes/statistics.class.php | 3 +- public/include/classes/user.class.php | 45 +++++-- public/include/pages/account.inc.php | 11 +- public/include/pages/account/edit.inc.php | 122 +++++++++--------- .../pages/account/notifications.inc.php | 36 +++--- .../pages/account/transactions.inc.php | 13 +- public/include/pages/account/workers.inc.php | 57 ++++---- public/include/pages/admin.inc.php | 7 +- public/include/pages/admin/user.inc.php | 7 +- public/include/pages/admin/wallet.inc.php | 7 +- public/include/pages/login.inc.php | 2 +- .../include/pages/statistics/blocks.inc.php | 11 +- public/include/pages/statistics/pool.inc.php | 2 +- public/include/pages/statistics/user.inc.php | 10 +- public/templates/mmcFE/admin/user/default.tpl | 6 +- public/templates/mmcFE/global/navigation.tpl | 2 +- sql/issue_147_accounts_upgrade.sql | 2 + 17 files changed, 181 insertions(+), 162 deletions(-) create mode 100644 sql/issue_147_accounts_upgrade.sql diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index a63416dd..3bcebcfb 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -185,7 +185,8 @@ class Statistics { $stmt = $this->mysqli->prepare(" SELECT a.id AS id, - a.admin as admin, + a.is_admin as is_admin, + a.is_locked as is_locked, a.username AS username, a.donate_percent AS donate_percent, a.email AS email, diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index feb0466f..76cfd1a4 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -36,7 +36,10 @@ class User { return $this->getSingle($username, 'email', 'username', 's'); } public function getUserAdmin($id) { - return $this->getSingle($id, 'admin', 'id'); + return $this->getSingle($id, 'is_admin', 'id'); + } + public function getUserLocked($id) { + return $this->getSingle($id, 'is_locked', 'id'); } public function getUserToken($id) { return $this->getSingle($id, 'token', 'id'); @@ -44,9 +47,11 @@ class User { public function getIdFromToken($token) { return $this->getSingle($token, 'id', 'token', 's'); } + public function isLocked($id) { + return $this->getUserLocked($id); + } public function isAdmin($id) { - if ($this->getUserAdmin($id) == 1) return true; - return false; + return $this->getUserAdmin($id); } public function setUserToken($id) { @@ -79,10 +84,15 @@ class User { public function checkLogin($username, $password) { $this->debug->append("STA " . __METHOD__, 4); $this->debug->append("Checking login for $username with password $password", 2); - if ( $this->checkUserPassword($username, $password) ) { + if ($this->isLocked($this->getUserId($username))) { + $this->setErrorMessage("Account is locked. Please contact site support."); + return false; + } + if ( $this->checkUserPassword($username, $password)) { $this->createSession($username); return true; } + $this->setErrorMessage("Invalid username or password"); return false; } @@ -300,7 +310,7 @@ class User { private function checkUserPassword($username, $password) { $this->debug->append("STA " . __METHOD__, 4); $user = array(); - $stmt = $this->mysqli->prepare("SELECT username, id, admin FROM $this->table WHERE username=? AND pass=? LIMIT 1"); + $stmt = $this->mysqli->prepare("SELECT username, id, is_admin FROM $this->table WHERE username=? AND pass=? LIMIT 1"); if ($this->checkStmt($stmt)) { $stmt->bind_param('ss', $username, hash('sha256', $password.$this->salt)); $stmt->execute(); @@ -308,7 +318,7 @@ class User { $stmt->fetch(); $stmt->close(); // Store the basic login information - $this->user = array('username' => $row_username, 'id' => $row_id, 'admin' => $row_admin); + $this->user = array('username' => $row_username, 'id' => $row_id, 'is_admin' => $row_admin); return $username === $row_username; } return false; @@ -337,7 +347,8 @@ class User { $this->debug->append("STA " . __METHOD__, 4); session_destroy(); session_regenerate_id(true); - return true; + // Enforce a page reload + header("Location: index.php"); } /** @@ -359,7 +370,7 @@ class User { $this->debug->append("Fetching user information for user id: $userID"); $stmt = $this->mysqli->prepare(" SELECT - id, username, pin, api_key, admin, email, + id, username, pin, api_key, is_admin, email, IFNULL(donate_percent, '0') as donate_percent, coin_address, ap_threshold FROM $this->table WHERE id = ? LIMIT 0,1"); @@ -417,7 +428,7 @@ class User { "); } else { $stmt = $this->mysqli->prepare(" - INSERT INTO $this->table (username, pass, email, pin, api_key, admin) + INSERT INTO $this->table (username, pass, email, pin, api_key, is_admin) VALUES (?, ?, ?, ?, ?, 1) "); } @@ -505,6 +516,22 @@ class User { } return false; } + + /** + * Check if a user is authenticated and allowed to login + * Checks the $_SESSION for existing data + * Destroys the session if account is now locked + * @param none + * @return bool + **/ + public function isAuthenticated() { + $this->debug->append("STA " . __METHOD__, 4); + if ($_SESSION['AUTHENTICATED'] == true && ! $this->isLocked($_SESSION['USERDATA']['id'])) + return true; + // Catchall + $this->logoutUser(); + return false; + } } // Make our class available automatically diff --git a/public/include/pages/account.inc.php b/public/include/pages/account.inc.php index 859575d9..9e43518e 100644 --- a/public/include/pages/account.inc.php +++ b/public/include/pages/account.inc.php @@ -1,13 +1,10 @@ isAuthenticated()) { + // Tempalte specifics + $smarty->assign("CONTENT", "default.tpl"); } - -// Tempalte specifics -$smarty->assign("CONTENT", "default.tpl"); ?> diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php index 6726b7d9..a4859c8f 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -4,79 +4,77 @@ if (!defined('SECURITY')) die('Hacking attempt'); -if (!$_SESSION['AUTHENTICATED']) { - header('Location: index.php?page=home'); -} - -if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST['do']) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Invalid PIN','TYPE' => 'errormsg'); -} else { - switch ($_POST['do']) { - case 'cashOut': - if ($setting->getValue('manual_payout_active') == 1) { - $_SESSION['POPUP'][] = array('CONTENT' => 'A manual payout is in progress. Please try again later.', 'TYPE' => 'errormsg'); - } else { - $setting->setValue('manual_payout_active', 1); - $continue = true; - $aBalance = $transaction->getBalance($_SESSION['USERDATA']['id']); - $dBalance = $aBalance['confirmed']; - $sCoinAddress = $user->getCoinAddress($_SESSION['USERDATA']['id']); - // Ensure we can cover the potential transaction fee - if ($dBalance > $config['txfee']) { - if ($bitcoin->can_connect() === true) { - try { - $bitcoin->validateaddress($sCoinAddress); - } catch (BitcoinClientException $e) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Invalid payment address: ' . $sUserSendAddress, 'TYPE' => 'errormsg'); - $continue = false; - } - if ($continue == true) { - // Send balance to address, mind fee for transaction! +if ($user->isAuthenticated()) { + if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST['do']) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Invalid PIN','TYPE' => 'errormsg'); + } else { + switch ($_POST['do']) { + case 'cashOut': + if ($setting->getValue('manual_payout_active') == 1) { + $_SESSION['POPUP'][] = array('CONTENT' => 'A manual payout is in progress. Please try again later.', 'TYPE' => 'errormsg'); + } else { + $setting->setValue('manual_payout_active', 1); + $continue = true; + $aBalance = $transaction->getBalance($_SESSION['USERDATA']['id']); + $dBalance = $aBalance['confirmed']; + $sCoinAddress = $user->getCoinAddress($_SESSION['USERDATA']['id']); + // Ensure we can cover the potential transaction fee + if ($dBalance > $config['txfee']) { + if ($bitcoin->can_connect() === true) { try { - if ($setting->getValue('auto_payout_active') == 0) { - $bitcoin->sendtoaddress($sCoinAddress, $dBalance); - } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Auto-payout active, please contact site support immidiately to revoke invalid transactions.', 'TYPE' => 'errormsg'); - $continue = false; - } + $bitcoin->validateaddress($sCoinAddress); } catch (BitcoinClientException $e) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to send ' . $config['currency'] . ', please contact site support immidiately', 'TYPE' => 'errormsg'); + $_SESSION['POPUP'][] = array('CONTENT' => 'Invalid payment address: ' . $sUserSendAddress, 'TYPE' => 'errormsg'); $continue = false; } - } - // Set balance to 0, add to paid out, insert to ledger - if ($continue == true && $transaction->addTransaction($_SESSION['USERDATA']['id'], $dBalance, 'Debit_MP', NULL, $sCoinAddress)) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Transaction completed', 'TYPE' => 'success'); - $aMailData['email'] = $user->getUserEmail($user->getUserName($_SESSION['USERDATA']['id'])); - $aMailData['amount'] = $dBalance; - $aMailData['subject'] = 'Manual Payout Completed'; - $notification->sendNotification($_SESSION['USERDATA']['id'], 'manual_payout', $aMailData); + if ($continue == true) { + // Send balance to address, mind fee for transaction! + try { + if ($setting->getValue('auto_payout_active') == 0) { + $bitcoin->sendtoaddress($sCoinAddress, $dBalance); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'Auto-payout active, please contact site support immidiately to revoke invalid transactions.', 'TYPE' => 'errormsg'); + $continue = false; + } + } catch (BitcoinClientException $e) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to send ' . $config['currency'] . ', please contact site support immidiately', 'TYPE' => 'errormsg'); + $continue = false; + } + } + // Set balance to 0, add to paid out, insert to ledger + if ($continue == true && $transaction->addTransaction($_SESSION['USERDATA']['id'], $dBalance, 'Debit_MP', NULL, $sCoinAddress)) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Transaction completed', 'TYPE' => 'success'); + $aMailData['email'] = $user->getUserEmail($user->getUserName($_SESSION['USERDATA']['id'])); + $aMailData['amount'] = $dBalance; + $aMailData['subject'] = 'Manual Payout Completed'; + $notification->sendNotification($_SESSION['USERDATA']['id'], 'manual_payout', $aMailData); + } + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service', 'TYPE' => 'errormsg'); } } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service', 'TYPE' => 'errormsg'); + $_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than ' . $config['txfee'] . ' ' . $conifg['currency'] . ' to cover transaction fees', 'TYPE' => 'errormsg'); } - } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than ' . $config['txfee'] . ' ' . $conifg['currency'] . ' to cover transaction fees', 'TYPE' => 'errormsg'); + $setting->setValue('manual_payout_active', 0); } - $setting->setValue('manual_payout_active', 0); - } - break; + break; - case 'updateAccount': - if ($user->updateAccount($_SESSION['USERDATA']['id'], $_POST['paymentAddress'], $_POST['payoutThreshold'], $_POST['donatePercent'], $_POST['email'])) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Account details updated', 'TYPE' => 'success'); - } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update your account: ' . $user->getError(), 'TYPE' => 'errormsg'); - } - break; + case 'updateAccount': + if ($user->updateAccount($_SESSION['USERDATA']['id'], $_POST['paymentAddress'], $_POST['payoutThreshold'], $_POST['donatePercent'], $_POST['email'])) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Account details updated', 'TYPE' => 'success'); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update your account: ' . $user->getError(), 'TYPE' => 'errormsg'); + } + break; - case 'updatePassword': - if ($user->updatePassword($_SESSION['USERDATA']['id'], $_POST['currentPassword'], $_POST['newPassword'], $_POST['newPassword2'])) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Password updated', 'TYPE' => 'success'); - } else { - $_SESSION['POPUP'][] = array('CONTENT' => $user->getError(), 'TYPE' => 'errormsg'); + case 'updatePassword': + if ($user->updatePassword($_SESSION['USERDATA']['id'], $_POST['currentPassword'], $_POST['newPassword'], $_POST['newPassword2'])) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Password updated', 'TYPE' => 'success'); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => $user->getError(), 'TYPE' => 'errormsg'); + } + break; } - break; } } diff --git a/public/include/pages/account/notifications.inc.php b/public/include/pages/account/notifications.inc.php index 2ab9c0d0..87fd6217 100644 --- a/public/include/pages/account/notifications.inc.php +++ b/public/include/pages/account/notifications.inc.php @@ -2,24 +2,24 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -if (!$_SESSION['AUTHENTICATED']) header('Location: index.php?page=home'); - -if ($_REQUEST['do'] == 'save') { - if ($notification->updateSettings($_SESSION['USERDATA']['id'], $_REQUEST['data'])) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Updated notification settings'); - } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update settings', 'TYPE' => 'errormsg'); +if ($user->isAuthenticated()) { + if ($_REQUEST['do'] == 'save') { + if ($notification->updateSettings($_SESSION['USERDATA']['id'], $_REQUEST['data'])) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Updated notification settings'); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update settings', 'TYPE' => 'errormsg'); + } } + + // Fetch notifications + $aNotifications = $notification->getNofifications($_SESSION['USERDATA']['id']); + if (!$aNotifications) $_SESSION['POPUP'][] = array('CONTENT' => 'Could not find any notifications', 'TYPE' => 'errormsg'); + + // Fetch user notification settings + $aSettings = $notification->getNotificationSettings($_SESSION['USERDATA']['id']); + + $smarty->assign('NOTIFICATIONS', $aNotifications); + $smarty->assign('SETTINGS', $aSettings); + $smarty->assign('CONTENT', 'default.tpl'); } - -// Fetch notifications -$aNotifications = $notification->getNofifications($_SESSION['USERDATA']['id']); -if (!$aNotifications) $_SESSION['POPUP'][] = array('CONTENT' => 'Could not find any notifications', 'TYPE' => 'errormsg'); - -// Fetch user notification settings -$aSettings = $notification->getNotificationSettings($_SESSION['USERDATA']['id']); - -$smarty->assign('NOTIFICATIONS', $aNotifications); -$smarty->assign('SETTINGS', $aSettings); -$smarty->assign('CONTENT', 'default.tpl'); ?> diff --git a/public/include/pages/account/transactions.inc.php b/public/include/pages/account/transactions.inc.php index 6e83e292..f6bdbfb2 100644 --- a/public/include/pages/account/transactions.inc.php +++ b/public/include/pages/account/transactions.inc.php @@ -2,11 +2,10 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -if (!$_SESSION['AUTHENTICATED']) header('Location: index.php?page=home'); - -$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'); +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'); +} ?> diff --git a/public/include/pages/account/workers.inc.php b/public/include/pages/account/workers.inc.php index 67bd0e19..78556424 100644 --- a/public/include/pages/account/workers.inc.php +++ b/public/include/pages/account/workers.inc.php @@ -2,35 +2,36 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -if (!$_SESSION['AUTHENTICATED']) header('Location: index.php?page=home'); -switch ($_REQUEST['do']) { -case 'delete': - if ($worker->deleteWorker($_SESSION['USERDATA']['id'], $_GET['id'])) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Worker removed'); - } else { - $_SESSION['POPUP'][] = array('CONTENT' => $worker->getError(), 'TYPE' => 'errormsg'); +if ($user->isAuthenticated()) { + switch ($_REQUEST['do']) { + case 'delete': + if ($worker->deleteWorker($_SESSION['USERDATA']['id'], $_GET['id'])) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Worker removed'); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => $worker->getError(), 'TYPE' => 'errormsg'); + } + break; + case 'add': + if ($worker->addWorker($_SESSION['USERDATA']['id'], $_POST['username'], $_POST['password'])) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Worker added'); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => $worker->getError(), 'TYPE' => 'errormsg'); + } + break; + case 'update': + if ($worker->updateWorkers($_SESSION['USERDATA']['id'], $_POST['data'])) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Worker updated'); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => $worker->getError(), 'TYPE' => 'errormsg'); + } + break; } - break; -case 'add': - if ($worker->addWorker($_SESSION['USERDATA']['id'], $_POST['username'], $_POST['password'])) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Worker added'); - } else { - $_SESSION['POPUP'][] = array('CONTENT' => $worker->getError(), 'TYPE' => 'errormsg'); - } - break; -case 'update': - if ($worker->updateWorkers($_SESSION['USERDATA']['id'], $_POST['data'])) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Worker updated'); - } else { - $_SESSION['POPUP'][] = array('CONTENT' => $worker->getError(), 'TYPE' => 'errormsg'); - } - break; + + $aWorkers = $worker->getWorkers($_SESSION['USERDATA']['id']); + if (!$aWorkers) $_SESSION['POPUP'][] = array('CONTENT' => 'You have no workers configured', 'TYPE' => 'errormsg'); + + $smarty->assign('CONTENT', 'default.tpl'); + $smarty->assign('WORKERS', $aWorkers); } - -$aWorkers = $worker->getWorkers($_SESSION['USERDATA']['id']); -if (!$aWorkers) $_SESSION['POPUP'][] = array('CONTENT' => 'You have no workers configured', 'TYPE' => 'errormsg'); - -$smarty->assign('CONTENT', 'default.tpl'); -$smarty->assign('WORKERS', $aWorkers); ?> diff --git a/public/include/pages/admin.inc.php b/public/include/pages/admin.inc.php index 4409a769..5305b030 100644 --- a/public/include/pages/admin.inc.php +++ b/public/include/pages/admin.inc.php @@ -1,13 +1,12 @@ isAdmin($_SESSION['USERDATA']['id'])) { +if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) { header("HTTP/1.1 404 Page not found"); - die(); + die("404 Page not found"); } // Tempalte specifics diff --git a/public/include/pages/admin/user.inc.php b/public/include/pages/admin/user.inc.php index 8b40ab21..548cdf3a 100644 --- a/public/include/pages/admin/user.inc.php +++ b/public/include/pages/admin/user.inc.php @@ -1,13 +1,12 @@ isAdmin($_SESSION['USERDATA']['id'])) { +if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) { header("HTTP/1.1 404 Page not found"); - die(); + die("404 Page not found"); } $aRoundShares = $statistics->getRoundShares(); diff --git a/public/include/pages/admin/wallet.inc.php b/public/include/pages/admin/wallet.inc.php index cb094fe9..45ff5af4 100644 --- a/public/include/pages/admin/wallet.inc.php +++ b/public/include/pages/admin/wallet.inc.php @@ -1,13 +1,12 @@ isAdmin($_SESSION['USERDATA']['id'])) { +if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) { header("HTTP/1.1 404 Page not found"); - die(); + die("404 Page not found"); } if ($bitcoin->can_connect() === true){ diff --git a/public/include/pages/login.inc.php b/public/include/pages/login.inc.php index 6600a872..c157d720 100644 --- a/public/include/pages/login.inc.php +++ b/public/include/pages/login.inc.php @@ -7,7 +7,7 @@ if (!defined('SECURITY')) if ( $user->checkLogin($_POST['username'],$_POST['password']) ) { header('Location: index.php?page=home'); } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Invalid username or password', 'TYPE' => 'errormsg'); + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to login: '. $user->getError(), 'TYPE' => 'errormsg'); } $smarty->assign('CONTENT', 'default.tpl'); ?> diff --git a/public/include/pages/statistics/blocks.inc.php b/public/include/pages/statistics/blocks.inc.php index 7ba0bfdd..69bbade7 100644 --- a/public/include/pages/statistics/blocks.inc.php +++ b/public/include/pages/statistics/blocks.inc.php @@ -1,9 +1,8 @@ isAuthenticated()) header("Location: index.php?page=home"); // Grab the last blocks found $iLimit = 30; @@ -14,9 +13,5 @@ $aBlockData = $aBlocksFoundData[0]; $smarty->assign("BLOCKSFOUND", $aBlocksFoundData); $smarty->assign("BLOCKLIMIT", $iLimit); -if ($_SESSION['AUTHENTICATED']) { - $smarty->assign("CONTENT", "blocks_found.tpl"); -} else { - $smarty->assign("CONTENT", "default.tpl"); -} +$smarty->assign("CONTENT", "blocks_found.tpl"); ?> diff --git a/public/include/pages/statistics/pool.inc.php b/public/include/pages/statistics/pool.inc.php index 76014fe4..fc546e72 100644 --- a/public/include/pages/statistics/pool.inc.php +++ b/public/include/pages/statistics/pool.inc.php @@ -50,7 +50,7 @@ $smarty->assign("LASTBLOCK", $aBlockData['height']); $smarty->assign("DIFFICULTY", $dDifficulty); $smarty->assign("REWARD", $config['reward']); -if ($_SESSION['AUTHENTICATED']) { +if ($user->isAuthenticated()) { $smarty->assign("CONTENT", "authenticated.tpl"); } else { $smarty->assign("CONTENT", "../default.tpl"); diff --git a/public/include/pages/statistics/user.inc.php b/public/include/pages/statistics/user.inc.php index 2b0b0ed9..13b3ba5b 100644 --- a/public/include/pages/statistics/user.inc.php +++ b/public/include/pages/statistics/user.inc.php @@ -4,12 +4,10 @@ if (!defined('SECURITY')) die('Hacking attempt'); -$aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA']['id']); - -// Propagate content our template -$smarty->assign("YOURHASHRATES", $aHourlyHashRates); - -if ($_SESSION['AUTHENTICATED']) { +if ($user->isAuthenticated()) { + $aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA']['id']); + // Propagate content our template + $smarty->assign("YOURHASHRATES", $aHourlyHashRates); $smarty->assign("CONTENT", "default.tpl"); } ?> diff --git a/public/templates/mmcFE/admin/user/default.tpl b/public/templates/mmcFE/admin/user/default.tpl index 86c52e48..1da8c3a4 100644 --- a/public/templates/mmcFE/admin/user/default.tpl +++ b/public/templates/mmcFE/admin/user/default.tpl @@ -23,6 +23,7 @@ Est. Payout    Balance    Admin + Locked @@ -37,7 +38,10 @@ {$USERS[user].payout.est_payout|number_format:"8"} {$USERS[user].balance|number_format:"8"} - + + + + {sectionelse} diff --git a/public/templates/mmcFE/global/navigation.tpl b/public/templates/mmcFE/global/navigation.tpl index 84da0388..9beb87cb 100644 --- a/public/templates/mmcFE/global/navigation.tpl +++ b/public/templates/mmcFE/global/navigation.tpl @@ -11,7 +11,7 @@ {/if} - {if $smarty.session.AUTHENTICATED|default:"0" == 1 && $GLOBAL.userdata.admin == 1} + {if $smarty.session.AUTHENTICATED|default:"0" == 1 && $GLOBAL.userdata.is_admin == 1}
  • Admin Panel
    • User Info
    • diff --git a/sql/issue_147_accounts_upgrade.sql b/sql/issue_147_accounts_upgrade.sql new file mode 100644 index 00000000..ecff0f6f --- /dev/null +++ b/sql/issue_147_accounts_upgrade.sql @@ -0,0 +1,2 @@ +ALTER TABLE `accounts` ADD `is_locked` BOOLEAN NOT NULL DEFAULT FALSE AFTER `email` ; +ALTER TABLE `accounts` CHANGE `admin` `is_admin` BOOLEAN NOT NULL DEFAULT FALSE ; From 44851e35df9007abacb1ad3eddec0fafb168b47c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 11:23:02 +0200 Subject: [PATCH 119/168] Add admin/lock changes to admin panel * Add clickable boxes for Admin and Locked status for all users * Change status via Ajax call, script embedded to the admin page Fixes #147 --- public/include/classes/user.class.php | 16 +++++++++++ public/include/pages/admin/user.inc.php | 12 +++++++++ public/templates/mmcFE/admin/user/default.tpl | 27 ++++++++++++++++--- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 76cfd1a4..cc8d7c70 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -53,6 +53,22 @@ class User { public function isAdmin($id) { return $this->getUserAdmin($id); } + public function changeLocked($id) { + $field = array( + 'name' => 'is_locked', + 'type' => 'i', + 'value' => !$this->isLocked($id) + ); + return $this->updateSingle($id, $field); + } + public function changeAdmin($id) { + $field = array( + 'name' => 'is_admin', + 'type' => 'i', + 'value' => !$this->isAdmin($id) + ); + return $this->updateSingle($id, $field); + } public function setUserToken($id) { $field = array( diff --git a/public/include/pages/admin/user.inc.php b/public/include/pages/admin/user.inc.php index 548cdf3a..11808edb 100644 --- a/public/include/pages/admin/user.inc.php +++ b/public/include/pages/admin/user.inc.php @@ -11,6 +11,18 @@ if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) { $aRoundShares = $statistics->getRoundShares(); +// Change account lock +if ($_POST['do'] == 'lock') { + $supress_master = 1; + $user->changeLocked($_POST['account_id']); +} + +// Change account admin +if ($_POST['do'] == 'admin') { + $supress_master = 1; + $user->changeAdmin($_POST['account_id']); +} + if ($_POST['query']) { // Fetch requested users $aUsers = $statistics->getAllUserStats($_POST['query']); diff --git a/public/templates/mmcFE/admin/user/default.tpl b/public/templates/mmcFE/admin/user/default.tpl index 1da8c3a4..8cfc263a 100644 --- a/public/templates/mmcFE/admin/user/default.tpl +++ b/public/templates/mmcFE/admin/user/default.tpl @@ -1,5 +1,22 @@ + + {include file="global/block_header.tpl" BLOCK_HEADER="Query User Database"} - + @@ -38,10 +55,14 @@ {$USERS[user].payout.est_payout|number_format:"8"} {$USERS[user].balance|number_format:"8"} - + + + - + + + {sectionelse} From 38f2ae5af755bc518e26a7540a624a5d18c04355 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 13:24:46 +0200 Subject: [PATCH 120/168] adding proper avg shares calculation based on target diff and network diff --- public/include/smarty_globals.inc.php | 1 + public/templates/mmcFE/statistics/pool/authenticated.tpl | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index 8ec1e882..d7712942 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -34,6 +34,7 @@ $aGlobal = array( 'blockexplorer' => $config['blockexplorer'], 'chaininfo' => $config['chaininfo'], 'config' => array( + 'targetdiff' => $config['difficulty'], 'currency' => $config['currency'], 'txfee' => $config['txfee'], 'payout_system' => $config['payout_system'], diff --git a/public/templates/mmcFE/statistics/pool/authenticated.tpl b/public/templates/mmcFE/statistics/pool/authenticated.tpl index 5d21e3eb..bedd8d4b 100644 --- a/public/templates/mmcFE/statistics/pool/authenticated.tpl +++ b/public/templates/mmcFE/statistics/pool/authenticated.tpl @@ -33,7 +33,7 @@ Est. Avg. Shares per Round - {($ESTTIME * $GLOBAL.sharerate)|number_format:"0"} + {(pow(2, 32 - $GLOBAL.config.targetdiff) * $DIFFICULTY)|number_format:"0"} Time Since Last Block From ba4a272442c5d5eaf3f18fc2cf116d0e73d920f1 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 13:25:08 +0200 Subject: [PATCH 121/168] reduced row size for time and difficulty --- public/templates/mmcFE/statistics/blocks/blocks_found.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/templates/mmcFE/statistics/blocks/blocks_found.tpl b/public/templates/mmcFE/statistics/blocks/blocks_found.tpl index 2278fb3c..d66e1349 100644 --- a/public/templates/mmcFE/statistics/blocks/blocks_found.tpl +++ b/public/templates/mmcFE/statistics/blocks/blocks_found.tpl @@ -23,8 +23,8 @@ Orphan {else}{$GLOBAL.confirmations - $BLOCKSFOUND[block].confirmations} left{/if} {$BLOCKSFOUND[block].finder|default:"unknown"} - {$BLOCKSFOUND[block].time|date_format:"%d/%m/%Y %H:%M:%S"} - {$BLOCKSFOUND[block].difficulty|number_format:"8"} + {$BLOCKSFOUND[block].time|date_format:"%d/%m %H:%M:%S"} + {$BLOCKSFOUND[block].difficulty|number_format:"2"} {$BLOCKSFOUND[block].shares|number_format} {/section} From 01c859d5f51dc580f18a3af257f60b5f886328c7 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 13:25:33 +0200 Subject: [PATCH 122/168] proper number format for USD/Est --- .../mmcFE/statistics/pool/contributors_hashrate.tpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl index 67c8f5d7..9086427a 100644 --- a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl +++ b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl @@ -7,7 +7,7 @@ User Name KH/s {$GLOBAL.config.currency}/Day - Usd/Day + USD/Day @@ -20,7 +20,7 @@ {$CONTRIBHASHES[contrib].account} {$CONTRIBHASHES[contrib].hashrate|number_format} {$estday|number_format:"3"} - {$estday|number_format:"3"*$GLOBAL.price|default:"n/a"|number_format:"4"} + {($estday * $GLOBAL.price)|default:"n/a"|number_format:"2"} {/section} {if $listed != 1} @@ -29,7 +29,7 @@ {$GLOBAL.userdata.username} {$GLOBAL.userdata.hashrate} {$estday|number_format:"3"} - {$estday|number_format:"3"*$GLOBAL.price|default:"n/a"|number_format:"4"} + {($estday * $GLOBAL.price)|default:"n/a"|number_format:"2"} {/if} From a703877122e8c89e2888973be66afcc193b40629 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 13:38:32 +0200 Subject: [PATCH 123/168] Adding support do disable account registration * Adding new configuration variable, see `global.inc.dist.php` * If you are not able to register anymore check the config var is * set Requested in and fixes #150 --- public/include/config/global.inc.dist.php | 1 + public/include/pages/register.inc.php | 12 ++++++++---- public/include/pages/register/register.inc.php | 9 ++++----- public/templates/mmcFE/register/disabled.tpl | 3 +++ 4 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 public/templates/mmcFE/register/disabled.tpl diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index e8d12774..4347a513 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -30,6 +30,7 @@ $config = array( 'max' => 250 ), 'website' => array( + 'registration' => true, // Allow new users to register 'name' => 'The Pool', 'slogan' => 'Resistance is futile', 'email' => 'test@example.com', // Mail address used for notifications diff --git a/public/include/pages/register.inc.php b/public/include/pages/register.inc.php index aecab054..f1c4cb73 100644 --- a/public/include/pages/register.inc.php +++ b/public/include/pages/register.inc.php @@ -1,9 +1,13 @@ assign("CONTENT", "default.tpl"); +if (!$config['website']['registration']) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); + $smarty->assign("CONTENT", "disabled.tpl"); +} else { + // 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 53e941bf..2e4e4885 100644 --- a/public/include/pages/register/register.inc.php +++ b/public/include/pages/register/register.inc.php @@ -1,11 +1,10 @@ register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'])) { +if (!$config['website']['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']) && $config['website']['registration']) { $_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/templates/mmcFE/register/disabled.tpl b/public/templates/mmcFE/register/disabled.tpl new file mode 100644 index 00000000..5e5ac8ee --- /dev/null +++ b/public/templates/mmcFE/register/disabled.tpl @@ -0,0 +1,3 @@ +{include file="global/block_header.tpl" BLOCK_HEADER="Registration disabled" BLOCK_STYLE="clear:none;"} +

      We are currently not accepting new user registrations.

      +{include file="global/block_footer.tpl"} From 6a5f9388954673d8130a8a3de317639e042017f4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 13:46:32 +0200 Subject: [PATCH 124/168] Store Users IP address in accounts after login Fixes #177 --- public/include/classes/user.class.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index cc8d7c70..9e69b01d 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -69,7 +69,6 @@ class User { ); return $this->updateSingle($id, $field); } - public function setUserToken($id) { $field = array( 'name' => 'token', @@ -78,6 +77,10 @@ class User { ); return $this->updateSingle($id, $field); } + private function setUserIp($id, $ip) { + $field = array( 'name' => 'loggedIp', 'type' => 's', 'value' => $ip ); + return $this->updateSingle($id, $field); + } /** * Fetch all users for administrative tasks @@ -106,6 +109,7 @@ class User { } if ( $this->checkUserPassword($username, $password)) { $this->createSession($username); + $this->setUserIp($this->getUserId($username), $_SERVER['REMOTE_ADDR']); return true; } $this->setErrorMessage("Invalid username or password"); From e681e8abf0c78e2014f8537808878e3e52423c97 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 13:49:53 +0200 Subject: [PATCH 125/168] new full database structure --- sql/mmcfe_ng_structure.sql | 91 ++++---------------------------------- 1 file changed, 9 insertions(+), 82 deletions(-) diff --git a/sql/mmcfe_ng_structure.sql b/sql/mmcfe_ng_structure.sql index 1e21bbf4..5d10574f 100644 --- a/sql/mmcfe_ng_structure.sql +++ b/sql/mmcfe_ng_structure.sql @@ -1,37 +1,18 @@ --- phpMyAdmin SQL Dump --- version 3.5.8.1deb1 --- http://www.phpmyadmin.net --- --- Host: localhost --- Generation Time: Jun 09, 2013 at 04:08 PM --- Server version: 5.5.31-0ubuntu0.13.04.1 --- PHP Version: 5.4.9-4ubuntu2 - -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 */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; --- --- Database: `mmcfe_ng` --- - --- -------------------------------------------------------- - --- --- Table structure for table `accounts` --- - CREATE TABLE IF NOT EXISTS `accounts` ( `id` int(255) NOT NULL AUTO_INCREMENT, - `admin` int(1) NOT NULL DEFAULT '0', + `is_admin` tinyint(1) NOT NULL DEFAULT '0', `username` varchar(40) NOT NULL, `pass` varchar(255) NOT NULL, `email` varchar(255) DEFAULT NULL COMMENT 'Assocaited email: used for validating users, and re-setting passwords', + `is_locked` tinyint(1) NOT NULL DEFAULT '0', `loggedIp` varchar(255) DEFAULT NULL, `sessionTimeoutStamp` int(255) DEFAULT NULL, `pin` varchar(255) NOT NULL COMMENT 'four digit pin to allow account changes', @@ -44,12 +25,6 @@ CREATE TABLE IF NOT EXISTS `accounts` ( UNIQUE KEY `username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; --- -------------------------------------------------------- - --- --- Table structure for table `blocks` --- - CREATE TABLE IF NOT EXISTS `blocks` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `height` int(10) unsigned NOT NULL, @@ -65,13 +40,7 @@ CREATE TABLE IF NOT EXISTS `blocks` ( PRIMARY KEY (`id`), UNIQUE KEY `height` (`height`,`blockhash`), KEY `time` (`time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Discovered blocks persisted from Litecoin Service'; - --- -------------------------------------------------------- - --- --- Table structure for table `notifications` --- +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Discovered blocks persisted from Litecoin Service'; CREATE TABLE IF NOT EXISTS `notifications` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, @@ -84,13 +53,7 @@ CREATE TABLE IF NOT EXISTS `notifications` ( KEY `active` (`active`), KEY `data` (`data`), KEY `account_id` (`account_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `notification_settings` --- +) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `notification_settings` ( `type` varchar(15) NOT NULL, @@ -98,12 +61,6 @@ CREATE TABLE IF NOT EXISTS `notification_settings` ( `active` tinyint(1) NOT NULL DEFAULT '0' ) ENGINE=InnoDB DEFAULT CHARSET=utf8; --- -------------------------------------------------------- - --- --- Table structure for table `pool_worker` --- - CREATE TABLE IF NOT EXISTS `pool_worker` ( `id` int(255) NOT NULL AUTO_INCREMENT, `account_id` int(255) NOT NULL, @@ -115,12 +72,6 @@ CREATE TABLE IF NOT EXISTS `pool_worker` ( KEY `account_id` (`account_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; --- -------------------------------------------------------- - --- --- Table structure for table `settings` --- - CREATE TABLE IF NOT EXISTS `settings` ( `name` varchar(255) NOT NULL, `value` varchar(255) DEFAULT NULL, @@ -128,12 +79,6 @@ CREATE TABLE IF NOT EXISTS `settings` ( UNIQUE KEY `setting` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; --- -------------------------------------------------------- - --- --- Table structure for table `shares` --- - CREATE TABLE IF NOT EXISTS `shares` ( `id` bigint(30) NOT NULL AUTO_INCREMENT, `rem_host` varchar(255) NOT NULL, @@ -148,13 +93,7 @@ CREATE TABLE IF NOT EXISTS `shares` ( KEY `upstream_result` (`upstream_result`), KEY `our_result` (`our_result`), KEY `username` (`username`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `shares_archive` --- +) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `shares_archive` ( `id` int(255) unsigned NOT NULL AUTO_INCREMENT, @@ -167,13 +106,7 @@ CREATE TABLE IF NOT EXISTS `shares_archive` ( PRIMARY KEY (`id`), UNIQUE KEY `share_id` (`share_id`), KEY `time` (`time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes'; - --- -------------------------------------------------------- - --- --- Table structure for table `statistics_shares` --- +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes'; CREATE TABLE IF NOT EXISTS `statistics_shares` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, @@ -184,13 +117,7 @@ CREATE TABLE IF NOT EXISTS `statistics_shares` ( PRIMARY KEY (`id`), KEY `account_id` (`account_id`), KEY `block_id` (`block_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `transactions` --- +) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `transactions` ( `id` int(255) NOT NULL AUTO_INCREMENT, @@ -204,7 +131,7 @@ CREATE TABLE IF NOT EXISTS `transactions` ( KEY `block_id` (`block_id`), KEY `account_id` (`account_id`), KEY `type` (`type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; From bfaa0a67ef3afdb723a1a2d95bf7534c84f80e11 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 13:59:45 +0200 Subject: [PATCH 126/168] Enforce session logout if IP address changed Fixes #179 --- 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 9e69b01d..9d747d34 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -44,6 +44,9 @@ class User { public function getUserToken($id) { return $this->getSingle($id, 'token', 'id'); } + public function getUserIp($id) { + return $this->getSingle($id, 'loggedIp', 'id'); + } public function getIdFromToken($token) { return $this->getSingle($token, 'id', 'token', 's'); } @@ -546,7 +549,7 @@ class User { **/ public function isAuthenticated() { $this->debug->append("STA " . __METHOD__, 4); - if ($_SESSION['AUTHENTICATED'] == true && ! $this->isLocked($_SESSION['USERDATA']['id'])) + if ($_SESSION['AUTHENTICATED'] == true && ! $this->isLocked($_SESSION['USERDATA']['id']) && $this->getUserIp($_SESSION['USERDATA']['id']) == $_SERVER['REMOTE_ADDR']) return true; // Catchall $this->logoutUser(); From fac1296812e57f05c50279a924249d3aede04b87 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 14:47:19 +0200 Subject: [PATCH 127/168] adding 3rd party library --- public/include/recaptchalib.php | 277 ++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 public/include/recaptchalib.php diff --git a/public/include/recaptchalib.php b/public/include/recaptchalib.php new file mode 100644 index 00000000..32c4f4d7 --- /dev/null +++ b/public/include/recaptchalib.php @@ -0,0 +1,277 @@ + $value ) + $req .= $key . '=' . urlencode( stripslashes($value) ) . '&'; + + // Cut the last '&' + $req=substr($req,0,strlen($req)-1); + return $req; +} + + + +/** + * Submits an HTTP POST to a reCAPTCHA server + * @param string $host + * @param string $path + * @param array $data + * @param int port + * @return array response + */ +function _recaptcha_http_post($host, $path, $data, $port = 80) { + + $req = _recaptcha_qsencode ($data); + + $http_request = "POST $path HTTP/1.0\r\n"; + $http_request .= "Host: $host\r\n"; + $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n"; + $http_request .= "Content-Length: " . strlen($req) . "\r\n"; + $http_request .= "User-Agent: reCAPTCHA/PHP\r\n"; + $http_request .= "\r\n"; + $http_request .= $req; + + $response = ''; + if( false == ( $fs = @fsockopen($host, $port, $errno, $errstr, 10) ) ) { + die ('Could not open socket'); + } + + fwrite($fs, $http_request); + + while ( !feof($fs) ) + $response .= fgets($fs, 1160); // One TCP-IP packet + fclose($fs); + $response = explode("\r\n\r\n", $response, 2); + + return $response; +} + + + +/** + * Gets the challenge HTML (javascript and non-javascript version). + * This is called from the browser, and the resulting reCAPTCHA HTML widget + * is embedded within the HTML form it was called from. + * @param string $pubkey A public key for reCAPTCHA + * @param string $error The error given by reCAPTCHA (optional, default is null) + * @param boolean $use_ssl Should the request be made over ssl? (optional, default is false) + + * @return string - The HTML to be embedded in the user's form. + */ +function recaptcha_get_html ($pubkey, $error = null, $use_ssl = false) +{ + if ($pubkey == null || $pubkey == '') { + die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"); + } + + if ($use_ssl) { + $server = RECAPTCHA_API_SECURE_SERVER; + } else { + $server = RECAPTCHA_API_SERVER; + } + + $errorpart = ""; + if ($error) { + $errorpart = "&error=" . $error; + } + return ' + + '; +} + + + + +/** + * A ReCaptchaResponse is returned from recaptcha_check_answer() + */ +class ReCaptchaResponse { + var $is_valid; + var $error; +} + + +/** + * Calls an HTTP POST function to verify if the user's guess was correct + * @param string $privkey + * @param string $remoteip + * @param string $challenge + * @param string $response + * @param array $extra_params an array of extra variables to post to the server + * @return ReCaptchaResponse + */ +function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $extra_params = array()) +{ + if ($privkey == null || $privkey == '') { + die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"); + } + + if ($remoteip == null || $remoteip == '') { + die ("For security reasons, you must pass the remote ip to reCAPTCHA"); + } + + + + //discard spam submissions + if ($challenge == null || strlen($challenge) == 0 || $response == null || strlen($response) == 0) { + $recaptcha_response = new ReCaptchaResponse(); + $recaptcha_response->is_valid = false; + $recaptcha_response->error = 'incorrect-captcha-sol'; + return $recaptcha_response; + } + + $response = _recaptcha_http_post (RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/verify", + array ( + 'privatekey' => $privkey, + 'remoteip' => $remoteip, + 'challenge' => $challenge, + 'response' => $response + ) + $extra_params + ); + + $answers = explode ("\n", $response [1]); + $recaptcha_response = new ReCaptchaResponse(); + + if (trim ($answers [0]) == 'true') { + $recaptcha_response->is_valid = true; + } + else { + $recaptcha_response->is_valid = false; + $recaptcha_response->error = $answers [1]; + } + return $recaptcha_response; + +} + +/** + * gets a URL where the user can sign up for reCAPTCHA. If your application + * has a configuration page where you enter a key, you should provide a link + * using this function. + * @param string $domain The domain where the page is hosted + * @param string $appname The name of your application + */ +function recaptcha_get_signup_url ($domain = null, $appname = null) { + return "https://www.google.com/recaptcha/admin/create?" . _recaptcha_qsencode (array ('domains' => $domain, 'app' => $appname)); +} + +function _recaptcha_aes_pad($val) { + $block_size = 16; + $numpad = $block_size - (strlen ($val) % $block_size); + return str_pad($val, strlen ($val) + $numpad, chr($numpad)); +} + +/* Mailhide related code */ + +function _recaptcha_aes_encrypt($val,$ky) { + if (! function_exists ("mcrypt_encrypt")) { + die ("To use reCAPTCHA Mailhide, you need to have the mcrypt php module installed."); + } + $mode=MCRYPT_MODE_CBC; + $enc=MCRYPT_RIJNDAEL_128; + $val=_recaptcha_aes_pad($val); + return mcrypt_encrypt($enc, $ky, $val, $mode, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); +} + + +function _recaptcha_mailhide_urlbase64 ($x) { + return strtr(base64_encode ($x), '+/', '-_'); +} + +/* gets the reCAPTCHA Mailhide url for a given email, public key and private key */ +function recaptcha_mailhide_url($pubkey, $privkey, $email) { + if ($pubkey == '' || $pubkey == null || $privkey == "" || $privkey == null) { + die ("To use reCAPTCHA Mailhide, you have to sign up for a public and private key, " . + "you can do so at http://www.google.com/recaptcha/mailhide/apikey"); + } + + + $ky = pack('H*', $privkey); + $cryptmail = _recaptcha_aes_encrypt ($email, $ky); + + return "http://www.google.com/recaptcha/mailhide/d?k=" . $pubkey . "&c=" . _recaptcha_mailhide_urlbase64 ($cryptmail); +} + +/** + * gets the parts of the email to expose to the user. + * eg, given johndoe@example,com return ["john", "example.com"]. + * the email is then displayed as john...@example.com + */ +function _recaptcha_mailhide_email_parts ($email) { + $arr = preg_split("/@/", $email ); + + if (strlen ($arr[0]) <= 4) { + $arr[0] = substr ($arr[0], 0, 1); + } else if (strlen ($arr[0]) <= 6) { + $arr[0] = substr ($arr[0], 0, 3); + } else { + $arr[0] = substr ($arr[0], 0, 4); + } + return $arr; +} + +/** + * Gets html to display an email address given a public an private key. + * to get a key, go to: + * + * http://www.google.com/recaptcha/mailhide/apikey + */ +function recaptcha_mailhide_html($pubkey, $privkey, $email) { + $emailparts = _recaptcha_mailhide_email_parts ($email); + $url = recaptcha_mailhide_url ($pubkey, $privkey, $email); + + return htmlentities($emailparts[0]) . "...@" . htmlentities ($emailparts [1]); + +} + + +?> From dda1585353dd9c50319a85147a1761c946280fc6 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 14:47:33 +0200 Subject: [PATCH 128/168] Adding ReCaptcha support for account registration * You will need to create a re-captcha account for your site * Disabled by default to keep backwards compatability Fixes #179 --- public/include/config/global.inc.dist.php | 6 +++ public/include/pages/register.inc.php | 4 ++ .../include/pages/register/register.inc.php | 43 ++++++++++++++++--- public/templates/mmcFE/register/default.tpl | 19 ++++---- 4 files changed, 59 insertions(+), 13 deletions(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 4347a513..b41d9110 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -35,6 +35,12 @@ $config = array( 'slogan' => 'Resistance is futile', 'email' => 'test@example.com', // Mail address used for notifications ), + // See: http://www.google.com/recaptcha + 'recaptcha' => array( + 'enabled' => false, // Enable re-captcha during registraion + 'public_key' => 'YOUR_PUBLIC_RECAPTCHA_KEY', + 'private_key' => 'YOUR_PRIVATE_RECAPTCHA_KEY' + ), 'currency' => 'LTC', // Currency name to be used on website 'txfee' => 0.1, // Default tx fee added by RPC server 'block_bonus' => 0, diff --git a/public/include/pages/register.inc.php b/public/include/pages/register.inc.php index f1c4cb73..d0a1d713 100644 --- a/public/include/pages/register.inc.php +++ b/public/include/pages/register.inc.php @@ -7,6 +7,10 @@ if (!$config['website']['registration']) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); $smarty->assign("CONTENT", "disabled.tpl"); } else { + if ($config['recaptcha']['enabled']) { + require_once(INCLUDE_DIR . '/recaptchalib.php'); + $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'])); + } // 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 2e4e4885..ce41630e 100644 --- a/public/include/pages/register/register.inc.php +++ b/public/include/pages/register/register.inc.php @@ -2,12 +2,45 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -if (!$config['website']['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']) && $config['website']['registration']) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); +if ($config['recaptcha']['enabled']) { + // Load re-captcha specific data + require_once(INCLUDE_DIR . '/recaptchalib.php'); + $rsp = recaptcha_check_answer ( + $config['recaptcha']['private_key'], + $_SERVER["REMOTE_ADDR"], + $_POST["recaptcha_challenge_field"], + $_POST["recaptcha_response_field"] + ); +} + +// 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 (!$config['website']['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']) && $config['website']['registration']) { + $_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 { - $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); + if (!$config['website']['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']) && $config['website']['registration']) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); + } } // We load the default registration template instead of an action specific one diff --git a/public/templates/mmcFE/register/default.tpl b/public/templates/mmcFE/register/default.tpl index d4440f1c..f711c67f 100644 --- a/public/templates/mmcFE/register/default.tpl +++ b/public/templates/mmcFE/register/default.tpl @@ -3,13 +3,16 @@ - - - - - - -
      Username:
      Password:
      Repeat Password:
      Email:
      Email Repeat:
      PIN: (4 digit number. Remember this pin!)
      - + + Username: + Password: + Repeat Password: + Email: + Email Repeat: + PIN: (4 digit number. Remember this pin!) + {nocache}{$RECAPTCHA}{/nocache} + + + {include file="global/block_footer.tpl"} From 5d356b5c4ff383c26e213a494f034f2057dbbdad Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 15:26:23 +0200 Subject: [PATCH 129/168] Adding counter for failed login attempts * Track failed login attempts of a user * Reset failed attempts as soon as the login succeeded Fixes #182 --- public/include/classes/user.class.php | 35 +++++++++++++++------------ sql/issue_182_accounts_upgrade.sql | 1 + 2 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 sql/issue_182_accounts_upgrade.sql diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 9d747d34..20ff072d 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -47,6 +47,9 @@ class User { public function getUserIp($id) { return $this->getSingle($id, 'loggedIp', 'id'); } + public function getUserFailed($id) { + return $this->getSingle($id, 'failed_logins', 'id'); + } public function getIdFromToken($token) { return $this->getSingle($token, 'id', 'token', 's'); } @@ -57,27 +60,23 @@ class User { return $this->getUserAdmin($id); } public function changeLocked($id) { - $field = array( - 'name' => 'is_locked', - 'type' => 'i', - 'value' => !$this->isLocked($id) - ); + $field = array('name' => 'is_locked', 'type' => 'i', 'value' => !$this->isLocked($id)); return $this->updateSingle($id, $field); } public function changeAdmin($id) { - $field = array( - 'name' => 'is_admin', - 'type' => 'i', - 'value' => !$this->isAdmin($id) - ); + $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' => hash('sha256', $id.time().$this->salt) - ); + $field = array('name' => 'token', 'type' => 's', 'value' => hash('sha256', $id.time().$this->salt)); + return $this->updateSingle($id, $field); + } + private function setUserFailed($id, $value) { + $field = array( 'name' => 'failed_logins', 'type' => 'i', 'value' => $value); + return $this->updateSingle($id, $field); + } + private function incUserFailed($id) { + $field = array( 'name' => 'failed_logins', 'type' => 'i', 'value' => $this->getUserFailed($id) + 1); return $this->updateSingle($id, $field); } private function setUserIp($id, $ip) { @@ -112,10 +111,14 @@ class User { } if ( $this->checkUserPassword($username, $password)) { $this->createSession($username); + $this->setUserFailed($this->getUserId($username), 0); $this->setUserIp($this->getUserId($username), $_SERVER['REMOTE_ADDR']); return true; } $this->setErrorMessage("Invalid username or password"); + if ($id = $this->getUserId($username)) + $this->incUserFailed($id); + return false; } @@ -213,7 +216,7 @@ class User { **/ private function updateSingle($id, $field) { $this->debug->append("STA " . __METHOD__, 4); - $stmt = $this->mysqli->prepare("UPDATE $this->table SET " . $field['name'] . " = ? WHERE id = ? LIMIT 1"); + $stmt = $this->mysqli->prepare("UPDATE $this->table SET `" . $field['name'] . "` = ? WHERE id = ? LIMIT 1"); if ($this->checkStmt($stmt) && $stmt->bind_param($field['type'].'i', $field['value'], $id) && $stmt->execute()) return true; $this->debug->append("Unable to update " . $field['name'] . " with " . $field['value'] . " for ID $id"); diff --git a/sql/issue_182_accounts_upgrade.sql b/sql/issue_182_accounts_upgrade.sql new file mode 100644 index 00000000..62266515 --- /dev/null +++ b/sql/issue_182_accounts_upgrade.sql @@ -0,0 +1 @@ +ALTER TABLE `accounts` ADD `failed_logins` INT( 5 ) UNSIGNED NULL DEFAULT '0' AFTER `is_locked` ; From a912819192dcbaf252fa69a4822cdcf31c43f7c1 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 16:38:48 +0300 Subject: [PATCH 130/168] Update README.md * Added re-captcha reference * Added new donor --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 33551e74..c7e8998a 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Donors These people have supported this project with a donation: * [obigal](https://github.com/obigal) +* [vias](https://github.com/vias79) Requirements ============ @@ -57,10 +58,11 @@ The following feature have been implemented so far: * Reward Systems * Propotional - * PPS **NEW** + * PPS * (Planned) PPLNS * Use of memcache for statistics instead of a cronjob * Web User accounts + * Re-Captcha protected registration form * Worker accounts * Worker activity (live, past 10 minutes) * Worker hashrates (live, past 10 minutes) @@ -68,8 +70,8 @@ The following feature have been implemented so far: * Minimal Block statistics * Pool donations * Pool fees -* Manual payout with 0.1 LTC fee -* Auto payout with 0.1 LTC fee +* Manual payout +* Auto payout * Transaction list (confirmed and unconfirmed) * Admin Panel * User Listing including statistics @@ -80,7 +82,7 @@ The following feature have been implemented so far: * IDLE Workers * New blocks found in pool * Auto Payout - * Manual Payou + * Manual Payout Installation ============ From acc4880dd695c1b73701fecf86d846bac0639094 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 16:25:52 +0200 Subject: [PATCH 131/168] Adding block luck graph * Added expected vs actual share graph to block stats * Added new small table template for overall stats in pool stats Fixes #91 --- .../include/pages/statistics/blocks.inc.php | 2 +- .../mmcFE/statistics/blocks/default.tpl | 70 +++++++++++++++++++ .../{blocks_found.tpl => small_table.tpl} | 13 +--- .../mmcFE/statistics/pool/authenticated.tpl | 2 +- 4 files changed, 74 insertions(+), 13 deletions(-) create mode 100644 public/templates/mmcFE/statistics/blocks/default.tpl rename public/templates/mmcFE/statistics/blocks/{blocks_found.tpl => small_table.tpl} (62%) diff --git a/public/include/pages/statistics/blocks.inc.php b/public/include/pages/statistics/blocks.inc.php index 69bbade7..e83aa8a8 100644 --- a/public/include/pages/statistics/blocks.inc.php +++ b/public/include/pages/statistics/blocks.inc.php @@ -13,5 +13,5 @@ $aBlockData = $aBlocksFoundData[0]; $smarty->assign("BLOCKSFOUND", $aBlocksFoundData); $smarty->assign("BLOCKLIMIT", $iLimit); -$smarty->assign("CONTENT", "blocks_found.tpl"); +$smarty->assign("CONTENT", "default.tpl"); ?> diff --git a/public/templates/mmcFE/statistics/blocks/default.tpl b/public/templates/mmcFE/statistics/blocks/default.tpl new file mode 100644 index 00000000..1511a46b --- /dev/null +++ b/public/templates/mmcFE/statistics/blocks/default.tpl @@ -0,0 +1,70 @@ +{include file="global/block_header.tpl" BLOCK_HEADER="Block Luck" BLOCK_STYLE="clear:none;"} + + + +{section block $BLOCKSFOUND} + +{/section} + + + + + +{section block $BLOCKSFOUND} + +{/section} + + + +{section block $BLOCKSFOUND} + +{/section} + + +
      {$BLOCKSFOUND[block].height}
      Expected Shares{(pow(2,32 - $GLOBAL.config.targetdiff) * $BLOCKSFOUND[block].difficulty)}
      Actual Shares{$BLOCKSFOUND[block].shares}
      +

      +

      +The graph above illustrates N shares to find a block vs. E Shares expected to find a block based on +target and network difficulty and assuming a zero variance scenario. +

      +{include file="global/block_footer.tpl"} + +{include file="global/block_header.tpl" BLOCK_HEADER="Last $BLOCKLIMIT Blocks Found" BLOCK_STYLE="clear:none;"} +
      + + + + + + + + + + + + + +{assign var=rank value=1} +{section block $BLOCKSFOUND} + + + + + + + + + +{/section} + +
      BlockValidityFinderTimeDifficultyExpected SharesActual Shares
      {$BLOCKSFOUND[block].height} + {if $BLOCKSFOUND[block].confirmations >= $GLOBAL.confirmations} + Confirmed + {else if $BLOCKSFOUND[block].confirmations == -1} + Orphan + {else}{$GLOBAL.confirmations - $BLOCKSFOUND[block].confirmations} left{/if}{$BLOCKSFOUND[block].finder|default:"unknown"}{$BLOCKSFOUND[block].time|date_format:"%d/%m %H:%M:%S"}{$BLOCKSFOUND[block].difficulty|number_format:"2"}{(pow(2,32 - $GLOBAL.config.targetdiff) * $BLOCKSFOUND[block].difficulty)|number_format}{$BLOCKSFOUND[block].shares|number_format}
      +
      +
        +
      • Note: Round Earnings are not credited until {$GLOBAL.confirmations} confirms.
      • +
      +{include file="global/block_footer.tpl"} diff --git a/public/templates/mmcFE/statistics/blocks/blocks_found.tpl b/public/templates/mmcFE/statistics/blocks/small_table.tpl similarity index 62% rename from public/templates/mmcFE/statistics/blocks/blocks_found.tpl rename to public/templates/mmcFE/statistics/blocks/small_table.tpl index d66e1349..9152fcc7 100644 --- a/public/templates/mmcFE/statistics/blocks/blocks_found.tpl +++ b/public/templates/mmcFE/statistics/blocks/small_table.tpl @@ -1,14 +1,12 @@ {include file="global/block_header.tpl" BLOCK_HEADER="Last $BLOCKLIMIT Blocks Found" BLOCK_STYLE="clear:none;"}
      - +
      - - - + @@ -16,15 +14,8 @@ {section block $BLOCKSFOUND} - - {/section} diff --git a/public/templates/mmcFE/statistics/pool/authenticated.tpl b/public/templates/mmcFE/statistics/pool/authenticated.tpl index bedd8d4b..5c67bbcd 100644 --- a/public/templates/mmcFE/statistics/pool/authenticated.tpl +++ b/public/templates/mmcFE/statistics/pool/authenticated.tpl @@ -44,6 +44,6 @@ {include file="global/block_footer.tpl"} -{include file="statistics/blocks/blocks_found.tpl" ALIGN="right"} +{include file="statistics/blocks/small_table.tpl" ALIGN="right" SHORT=true} {include file="global/block_footer.tpl"} From cc046a286cb04f4ab6cd37a1326e3cb37c396d33 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 16:34:04 +0200 Subject: [PATCH 132/168] fixing missing transactions table --- 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 f6bdbfb2..db927ca7 100644 --- a/public/include/pages/account/transactions.inc.php +++ b/public/include/pages/account/transactions.inc.php @@ -2,7 +2,7 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -if (!$user->isAuthenticated()) { +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); From 88b9d95ff26aa13184c71492e188261487669b05 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 16:41:38 +0200 Subject: [PATCH 133/168] Only run some globals for smarty if loggedin * Do not check for round shares if user is not logged in * Will prevent a long page load for new users trying to register * Only needed on statistics page that is not available to guests --- public/include/smarty_globals.inc.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index d7712942..257d33d4 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -7,16 +7,19 @@ if (!defined('SECURITY')) // Globally available variables $debug->append('Global smarty variables', 3); +// Only run these if the user is logged in +if ($_SESSION['AUTHENTICATED']) { + $aRoundShares = $statistics->getRoundShares(); + if ($bitcoin->can_connect() === true){ + $dDifficulty = $bitcoin->query('getdifficulty'); + } else { + $dDifficulty = 1; + } +} // Fetch some data -$aRoundShares = $statistics->getRoundShares(); $iCurrentActiveWorkers = $worker->getCountAllActiveWorkers(); $iCurrentPoolHashrate = $statistics->getCurrentHashrate(); $iCurrentPoolShareRate = $statistics->getCurrentShareRate(); -if ($bitcoin->can_connect() === true){ - $dDifficulty = $bitcoin->query('getdifficulty'); -} else { - $dDifficulty = 1; -} // Global data for Smarty $aGlobal = array( From 538c5ead8808975904e08369ad15d7a4d93ef5a8 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 17:09:59 +0200 Subject: [PATCH 134/168] properly sort blocks in graph --- public/templates/mmcFE/statistics/blocks/default.tpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/templates/mmcFE/statistics/blocks/default.tpl b/public/templates/mmcFE/statistics/blocks/default.tpl index 1511a46b..0cfec471 100644 --- a/public/templates/mmcFE/statistics/blocks/default.tpl +++ b/public/templates/mmcFE/statistics/blocks/default.tpl @@ -2,7 +2,7 @@
      BlockValidity Finder TimeDifficultySharesActual Shares
      {$BLOCKSFOUND[block].height} - {if $BLOCKSFOUND[block].confirmations >= $GLOBAL.confirmations} - Confirmed - {else if $BLOCKSFOUND[block].confirmations == -1} - Orphan - {else}{$GLOBAL.confirmations - $BLOCKSFOUND[block].confirmations} left{/if} {$BLOCKSFOUND[block].finder|default:"unknown"} {$BLOCKSFOUND[block].time|date_format:"%d/%m %H:%M:%S"}{$BLOCKSFOUND[block].difficulty|number_format:"2"} {$BLOCKSFOUND[block].shares|number_format}
      -{section block $BLOCKSFOUND} +{section block $BLOCKSFOUND step=-1} {/section} @@ -10,13 +10,13 @@ -{section block $BLOCKSFOUND} +{section block $BLOCKSFOUND step=-1} {/section} -{section block $BLOCKSFOUND} +{section block $BLOCKSFOUND step=-1} {/section} From d9886d8dca3be3bbf0f57b0b32438eccf684269f Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 22:00:28 +0200 Subject: [PATCH 135/168] re-adding tooltips for graphs --- .../mmcFE/js/jquery.tooltip.visualize.js | 106 ++ .../site_assets/mmcFE/js/jquery.visualize.js | 1601 ++++++++--------- 2 files changed, 901 insertions(+), 806 deletions(-) create mode 100644 public/site_assets/mmcFE/js/jquery.tooltip.visualize.js diff --git a/public/site_assets/mmcFE/js/jquery.tooltip.visualize.js b/public/site_assets/mmcFE/js/jquery.tooltip.visualize.js new file mode 100644 index 00000000..f6f71fd9 --- /dev/null +++ b/public/site_assets/mmcFE/js/jquery.tooltip.visualize.js @@ -0,0 +1,106 @@ +/** + * -------------------------------------------------------------------- + * Tooltip plugin for the jQuery-Plugin "Visualize" + * Tolltip by Iraê Carvalho, irae@irae.pro.br, http://irae.pro.br/en/ + * Copyright (c) 2010 Iraê Carvalho + * Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses. + * + * Visualize plugin by Scott Jehl, scott@filamentgroup.com + * Copyright (c) 2009 Filament Group, http://www.filamentgroup.com + * + * -------------------------------------------------------------------- + */ + +(function($){ + $.visualizePlugins.push(function visualizeTooltip(options,tableData) { + //configuration + var o = $.extend({ + tooltip: false, + tooltipalign: 'auto', // also available 'left' and 'right' + tooltipvalign: 'top', + tooltipclass: 'visualize-tooltip', + tooltiphtml: function(data){ + if(options.multiHover) { + var html=''; + for(var i=0;i'; + } + return html; + } else { + return '

      '+data.point.value+' - '+data.point.yLabels[0]+'

      '; + } + }, + delay:false + },options); + + // don't go any further if we are not to show anything + if(!o.tooltip) {return;} + + var self = $(this), + canvasContain = self.next(), + scroller = canvasContain.find('.visualize-scroller'), + scrollerW = scroller.width(), + tracker = canvasContain.find('.visualize-interaction-tracker'); + + // IE needs background color and opacity white or the tracker stays behind the tooltip + tracker.css({ + backgroundColor:'white', + opacity:0, + zIndex:100 + }); + + var tooltip = $('
      ').css({ + position:'absolute', + display:'none', + zIndex:90 + }) + .insertAfter(scroller.find('canvas')); + + var usescroll = true; + + if( typeof(G_vmlCanvasManager) != 'undefined' ){ + scroller.css({'position':'absolute'}); + tracker.css({marginTop:'-'+(o.height)+'px'}); + } + + + self.bind('vizualizeOver',function visualizeTooltipOver(e,data){ + if(data.canvasContain.get(0) != canvasContain.get(0)) {return;} // for multiple graphs originated from same table + if(o.multiHover) { + var p = data.point[0].canvasCords; + } else { + var p = data.point.canvasCords; + } + var left,right,top,clasRem,clasAd,bottom,x=Math.round(p[0]+data.tableData.zeroLocX),y=Math.round(p[1]+data.tableData.zeroLocY); + if(o.tooltipalign == 'left' || ( o.tooltipalign=='auto' && x-scroller.scrollLeft()<=scrollerW/2 ) ) { + if($.browser.msie && ($.browser.version == 7 || $.browser.version == 6) ) {usescroll=false;} else {usescroll=true;} + left = (x-(usescroll?scroller.scrollLeft():0))+'px'; + right = ''; + clasAdd="tooltipleft"; + clasRem="tooltipright"; + } else { + if($.browser.msie && $.browser.version == 7) {usescroll=false;} else {usescroll=true;} + left = ''; + right = (Math.abs(x-o.width)- (o.width-(usescroll?scroller.scrollLeft():0)-scrollerW) )+'px'; + clasAdd="tooltipright"; + clasRem="tooltipleft"; + } + + tooltip + .addClass(clasAdd) + .removeClass(clasRem) + .html(o.tooltiphtml(data)) + .css({ + display:'block', + top: y+'px', + left: left, + right: right + }); + }); + + self.bind('vizualizeOut',function visualizeTooltipOut(e,data){ + tooltip.css({display:'none'}); + }); + + }); +})(jQuery); \ No newline at end of file diff --git a/public/site_assets/mmcFE/js/jquery.visualize.js b/public/site_assets/mmcFE/js/jquery.visualize.js index 3daf800a..1043aaca 100644 --- a/public/site_assets/mmcFE/js/jquery.visualize.js +++ b/public/site_assets/mmcFE/js/jquery.visualize.js @@ -1,806 +1,795 @@ -/** - * -------------------------------------------------------------------- - * jQuery-Plugin "visualize" - * by Scott Jehl, scott@filamentgroup.com - * http://www.filamentgroup.com - * Copyright (c) 2009 Filament Group - * Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses. - * - * -------------------------------------------------------------------- - */ -(function ($) { - $.fn.visualize = function (options, container) { - return $(this).each(function () { - //configuration - var o = $.extend({ - type: 'bar', - //also available: area, pie, line - width: $(this).width(), - //height of canvas - defaults to table height - height: $(this).height(), - //height of canvas - defaults to table height - appendTitle: true, - //table caption text is added to chart - title: null, - //grabs from table caption if null - appendKey: true, - //color key is added to chart - colors: ['#be1e2d', '#666699', '#92d5ea', '#ee8310', '#8d10ee', '#5a3b16', '#26a4ed', '#f45a90', '#e9e744'], - textColors: [], - //corresponds with colors array. null/undefined items will fall back to CSS - parseDirection: 'x', - //which direction to parse the table data - pieMargin: 10, - //pie charts only - spacing around pie - pieLabelsAsPercent: true, - pieLabelPos: 'inside', - lineWeight: 4, - //for line and area - stroke weight - lineDots: false, - //also available: 'single', 'double' - dotInnerColor: "#ffffff", - // only used for lineDots:'double' - lineMargin: (options.lineDots ? 15 : 0), - //for line and area - spacing around lines - barGroupMargin: 10, - chartId: '', - xLabelParser: null, - // function to parse labels as values - valueParser: null, - // function to parse values. must return a Number - chartId: '', - chartClass: '', - barMargin: 1, - //space around bars in bar chart (added to both sides of bar) - yLabelInterval: 30, - //distance between y labels - interaction: false // only used for lineDots != false -- triggers mouseover and mouseout on original table - }, options); - - //reset width, height to numbers - o.width = parseFloat(o.width); - o.height = parseFloat(o.height); - - // reset padding if graph is not lines - if (o.type != 'line' && o.type != 'area') { - o.lineMargin = 0; - } - - var self = $(this); - - // scrape data from html table - var tableData = {}; - var colors = o.colors; - var textColors = o.textColors; - - - var parseLabels = function (direction) { - var labels = []; - if (direction == 'x') { - self.find('thead tr').each(function (i) { - $(this).find('th').each(function (j) { - if (!labels[j]) { - labels[j] = []; - } - labels[j][i] = $(this).text() - }) - }); - } else { - self.find('tbody tr').each(function (i) { - $(this).find('th').each(function (j) { - if (!labels[i]) { - labels[i] = []; - } - labels[i][j] = $(this).text() - }); - }); - } - return labels; - }; - - var fnParse = o.valueParser || parseFloat; - var dataGroups = tableData.dataGroups = []; - if (o.parseDirection == 'x') { - self.find('tbody tr').each(function (i, tr) { - dataGroups[i] = {}; - dataGroups[i].points = []; - dataGroups[i].color = colors[i]; - if (textColors[i]) { - dataGroups[i].textColor = textColors[i]; - } - $(tr).find('td').each(function (j, td) { - dataGroups[i].points.push({ - value: fnParse($(td).text()), - elem: td, - tableCords: [i, j] - }); - }); - }); - } else { - var cols = self.find('tbody tr:eq(0) td').size(); - for (var i = 0; i < cols; i++) { - dataGroups[i] = {}; - dataGroups[i].points = []; - dataGroups[i].color = colors[i]; - if (textColors[i]) { - dataGroups[i].textColor = textColors[i]; - } - self.find('tbody tr').each(function (j) { - dataGroups[i].points.push({ - value: $(this).find('td').eq(i).text() * 1, - elem: this, - tableCords: [i, j] - }); - }); - }; - } - - - var allItems = tableData.allItems = []; - $(dataGroups).each(function (i, row) { - var count = 0; - $.each(row.points, function (j, point) { - allItems.push(point); - count += point.value; - }); - row.groupTotal = count; - }); - - tableData.dataSum = 0; - tableData.topValue = 0; - tableData.bottomValue = Infinity; - $.each(allItems, function (i, item) { - tableData.dataSum += fnParse(item.value); - if (fnParse(item.value, 10) > tableData.topValue) { - tableData.topValue = fnParse(item.value, 10); - } - if (item.value < tableData.bottomValue) { - tableData.bottomValue = fnParse(item.value); - } - }); - var dataSum = tableData.dataSum; - var topValue = tableData.topValue; - var bottomValue = tableData.bottomValue; - - var xAllLabels = tableData.xAllLabels = parseLabels(o.parseDirection); - var yAllLabels = tableData.yAllLabels = parseLabels(o.parseDirection === 'x' ? 'y' : 'x'); - - var xLabels = tableData.xLabels = []; - $.each(tableData.xAllLabels, function (i, labels) { - tableData.xLabels.push(labels[0]); - }); - - var totalYRange = tableData.totalYRange = tableData.topValue - tableData.bottomValue; - - var zeroLocX = tableData.zeroLocX = 0; - - if ($.isFunction(o.xLabelParser)) { - - var xTopValue = null; - var xBottomValue = null; - - $.each(xLabels, function (i, label) { - label = xLabels[i] = o.xLabelParser(label); - if (i === 0) { - xTopValue = label; - xBottomValue = label; - } - if (label > xTopValue) { - xTopValue = label; - } - if (label < xBottomValue) { - xBottomValue = label; - } - }); - - var totalXRange = tableData.totalXRange = xTopValue - xBottomValue; - - - var xScale = tableData.xScale = (o.width - 2 * o.lineMargin) / totalXRange; - var marginDiffX = 0; - if (o.lineMargin) { - var marginDiffX = -2 * xScale - o.lineMargin; - } - zeroLocX = tableData.zeroLocX = xBottomValue + o.lineMargin; - - tableData.xBottomValue = xBottomValue; - tableData.xTopValue = xTopValue; - tableData.totalXRange = totalXRange; - } - - var yScale = tableData.yScale = (o.height - 2 * o.lineMargin) / totalYRange; - var zeroLocY = tableData.zeroLocY = (o.height - 2 * o.lineMargin) * (tableData.topValue / tableData.totalYRange) + o.lineMargin; - - var yLabels = tableData.yLabels = []; - - var numLabels = Math.floor((o.height - 2 * o.lineMargin) / 30); - - var loopInterval = tableData.totalYRange / numLabels; //fix provided from lab - loopInterval = Math.round(parseFloat(loopInterval) / 5) * 5; - loopInterval = Math.max(loopInterval, 1); - - // var start = - for (var j = Math.round(parseInt(tableData.bottomValue) / 5) * 5; j <= tableData.topValue - loopInterval; j += loopInterval) { - yLabels.push(j); - } - if (yLabels[yLabels.length - 1] > tableData.topValue + loopInterval) { - yLabels.pop(); - } else if (yLabels[yLabels.length - 1] <= tableData.topValue - 10) { - yLabels.push(tableData.topValue); - } - - // populate some data - $.each(dataGroups, function (i, row) { - row.yLabels = tableData.yAllLabels[i]; - $.each(row.points, function (j, point) { - point.zeroLocY = tableData.zeroLocY; - point.zeroLocX = tableData.zeroLocX; - point.xLabels = tableData.xAllLabels[j]; - point.yLabels = tableData.yAllLabels[i]; - point.color = row.color; - }); - }); - - try { - console.log(tableData); - } catch (e) {} - - var charts = {}; - - charts.pie = { - interactionPoints: dataGroups, - - setup: function () { - charts.pie.draw(true); - }, - draw: function (drawHtml) { - - var centerx = Math.round(canvas.width() / 2); - var centery = Math.round(canvas.height() / 2); - var radius = centery - o.pieMargin; - var counter = 0.0; - - if (drawHtml) { - canvasContain.addClass('visualize-pie'); - - if (o.pieLabelPos == 'outside') { - canvasContain.addClass('visualize-pie-outside'); - } - - var toRad = function (integer) { - return (Math.PI / 180) * integer; - }; - var labels = $('
        ').insertAfter(canvas); - } - - - //draw the pie pieces - $.each(dataGroups, function (i, row) { - var fraction = row.groupTotal / dataSum; - if (fraction <= 0 || isNaN(fraction)) return; - ctx.beginPath(); - ctx.moveTo(centerx, centery); - ctx.arc(centerx, centery, radius, counter * Math.PI * 2 - Math.PI * 0.5, (counter + fraction) * Math.PI * 2 - Math.PI * 0.5, false); - ctx.lineTo(centerx, centery); - ctx.closePath(); - ctx.fillStyle = dataGroups[i].color; - ctx.fill(); - // draw labels - if (drawHtml) { - var sliceMiddle = (counter + fraction / 2); - var distance = o.pieLabelPos == 'inside' ? radius / 1.5 : radius + radius / 5; - var labelx = Math.round(centerx + Math.sin(sliceMiddle * Math.PI * 2) * (distance)); - var labely = Math.round(centery - Math.cos(sliceMiddle * Math.PI * 2) * (distance)); - var leftRight = (labelx > centerx) ? 'right' : 'left'; - var topBottom = (labely > centery) ? 'bottom' : 'top'; - var percentage = parseFloat((fraction * 100).toFixed(2)); - - // interaction variables - row.canvasCords = [labelx, labely]; - row.zeroLocY = tableData.zeroLocY = 0; // related to zeroLocY and plugin API - row.zeroLocX = tableData.zeroLocX = 0; // related to zeroLocX and plugin API - row.value = row.groupTotal; - - - if (percentage) { - var labelval = (o.pieLabelsAsPercent) ? percentage + '%' : row.groupTotal; - var labeltext = $('' + labelval + '').css(leftRight, 0).css(topBottom, 0); - if (labeltext) var label = $('
      • ').appendTo(labels).css({ - left: labelx, - top: labely - }).append(labeltext); - labeltext.css('font-size', radius / 8).css('margin-' + leftRight, -labeltext.width() / 2).css('margin-' + topBottom, -labeltext.outerHeight() / 2); - - if (dataGroups[i].textColor) { - labeltext.css('color', dataGroups[i].textColor); - } - - } - } - counter += fraction; - }); - } - }; - - (function () { - - var xInterval; - - var drawPoint = function (ctx, x, y, color, size) { - ctx.moveTo(x, y); - ctx.beginPath(); - ctx.arc(x, y, size / 2, 0, 2 * Math.PI, false); - ctx.closePath(); - ctx.fillStyle = color; - ctx.fill(); - }; - - charts.line = { - - interactionPoints: allItems, - - setup: function (area) { - - if (area) { - canvasContain.addClass('visualize-area'); - } else { - canvasContain.addClass('visualize-line'); - } - - //write X labels - var xlabelsUL = $('
          ').width(canvas.width()).height(canvas.height()).insertBefore(canvas); - - if (!o.customXLabels) { - xInterval = (canvas.width() - 2 * o.lineMargin) / (xLabels.length - 1); - $.each(xLabels, function (i) { - var thisLi = $('
        • ' + this + '
        • ').prepend('').css('left', o.lineMargin + xInterval * i).appendTo(xlabelsUL); - var label = thisLi.find('span:not(.line)'); - var leftOffset = label.width() / -2; - if (i == 0) { - leftOffset = -20; - } else if (i == xLabels.length - 1) { - leftOffset = -label.width() + 20; - } - label.css('margin-left', leftOffset).addClass('label'); - }); - } else { - o.customXLabels(tableData, xlabelsUL); - } - - //write Y labels - var liBottom = (canvas.height() - 2 * o.lineMargin) / (yLabels.length - 1); - var ylabelsUL = $('
            ').width(canvas.width()).height(canvas.height()) - // .css('margin-top',-o.lineMargin) - .insertBefore(scroller); - - $.each(yLabels, function (i) { - var value = Math.floor(this); - var posB = (value - bottomValue) * yScale + o.lineMargin; - if (posB >= o.height - 1 || posB < 0) { - return; - } - var thisLi = $('
          • ' + value + '
          • ').css('bottom', posB); - if (Math.abs(posB) < o.height - 1) { - thisLi.prepend(''); - } - thisLi.prependTo(ylabelsUL); - - var label = thisLi.find('span:not(.line)'); - var topOffset = label.height() / -2; - if (!o.lineMargin) { - if (i == 0) { - topOffset = -label.height(); - } else if (i == yLabels.length - 1) { - topOffset = 0; - } - } - label.css('margin-top', topOffset).addClass('label'); - }); - - //start from the bottom left - ctx.translate(zeroLocX, zeroLocY); - - charts.line.draw(area); - - }, - - draw: function (area) { - // prevent drawing on top of previous draw - ctx.clearRect(-zeroLocX, -zeroLocY, o.width, o.height); - // Calculate each point properties before hand - var integer; - $.each(dataGroups, function (i, row) { - integer = o.lineMargin; // the current offset - $.each(row.points, function (j, point) { - if (o.xLabelParser) { - point.canvasCords = [(xLabels[j] - zeroLocX) * xScale - xBottomValue, -(point.value * yScale)]; - } else { - point.canvasCords = [integer, -(point.value * yScale)]; - } - - if (o.lineDots) { - point.dotSize = o.dotSize || o.lineWeight * Math.PI; - point.dotInnerSize = o.dotInnerSize || o.lineWeight * Math.PI / 2; - if (o.lineDots == 'double') { - point.innerColor = o.dotInnerColor; - } - } - integer += xInterval; - }); - }); - // fire custom event so we can enable rich interaction - self.trigger('vizualizeBeforeDraw', { - options: o, - table: self, - canvasContain: canvasContain, - tableData: tableData - }); - // draw lines and areas - $.each(dataGroups, function (h) { - // Draw lines - ctx.beginPath(); - ctx.lineWidth = o.lineWeight; - ctx.lineJoin = 'round'; - $.each(this.points, function (g) { - var loc = this.canvasCords; - if (g == 0) { - ctx.moveTo(loc[0], loc[1]); - } - ctx.lineTo(loc[0], loc[1]); - }); - ctx.strokeStyle = this.color; - ctx.stroke(); - // Draw fills - if (area) { - var integer = this.points[this.points.length - 1].canvasCords[0]; - if (isFinite(integer)) ctx.lineTo(integer, 0); - ctx.lineTo(o.lineMargin, 0); - ctx.closePath(); - ctx.fillStyle = this.color; - ctx.globalAlpha = .3; - ctx.fill(); - ctx.globalAlpha = 1.0; - } else { - ctx.closePath(); - } - }); - // draw points - if (o.lineDots) { - $.each(dataGroups, function (h) { - $.each(this.points, function (g) { - drawPoint(ctx, this.canvasCords[0], this.canvasCords[1], this.color, this.dotSize); - if (o.lineDots === 'double') { - drawPoint(ctx, this.canvasCords[0], this.canvasCords[1], this.innerColor, this.dotInnerSize); - } - }); - }); - } - - } - }; - - })(); - - charts.area = { - setup: function () { - charts.line.setup(true); - }, - draw: charts.line.draw - }; - - (function () { - - var horizontal, bottomLabels; - - charts.bar = { - setup: function () { - /** - * We can draw horizontal or vertical bars depending on the - * value of the 'barDirection' option (which may be 'vertical' or - * 'horizontal'). - */ - - horizontal = (o.barDirection == 'horizontal'); - - canvasContain.addClass('visualize-bar'); - - /** - * Write labels along the bottom of the chart. If we're drawing - * horizontal bars, these will be the yLabels, otherwise they - * will be the xLabels. The positioning also varies slightly: - * yLabels are values, hence they will span the whole width of - * the canvas, whereas xLabels are supposed to line up with the - * bars. - */ - bottomLabels = horizontal ? yLabels : xLabels; - - var xInterval = canvas.width() / (bottomLabels.length - (horizontal ? 1 : 0)); - - var xlabelsUL = $('
              ').width(canvas.width()).height(canvas.height()).insertBefore(canvas); - - $.each(bottomLabels, function (i) { - var thisLi = $('
            • ' + this + '
            • ').prepend('').css('left', xInterval * i).width(xInterval).appendTo(xlabelsUL); - - if (horizontal) { - var label = thisLi.find('span.label'); - label.css("margin-left", -label.width() / 2); - } - }); - - /** - * Write labels along the left of the chart. Follows the same idea - * as the bottom labels. - */ - var leftLabels = horizontal ? xLabels : yLabels; - var liBottom = canvas.height() / (leftLabels.length - (horizontal ? 0 : 1)); - - var ylabelsUL = $('
                ').width(canvas.width()).height(canvas.height()).insertBefore(canvas); - - $.each(leftLabels, function (i) { - var thisLi = $('
              • ' + this + '
              • ').prependTo(ylabelsUL); - - var label = thisLi.find('span:not(.line)').addClass('label'); - - if (horizontal) { - /** - * For left labels, we want to vertically align the text - * to the middle of its container, but we don't know how - * many lines of text we will have, since the labels could - * be very long. - * - * So we set a min-height of liBottom, and a max-height - * of liBottom + 1, so we can then check the label's actual - * height to determine if it spans one line or more lines. - */ - label.css({ - 'min-height': liBottom, - 'max-height': liBottom + 1, - 'vertical-align': 'middle' - }); - thisLi.css({ - 'top': liBottom * i, - 'min-height': liBottom - }); - - var r = label[0].getClientRects()[0]; - if (r.bottom - r.top == liBottom) { -/* This means we have only one line of text; hence - * we can centre the text vertically by setting the line-height, - * as described at: - * http://www.ampsoft.net/webdesign-l/vertical-aligned-nav-list.html - * - * (Although firefox has .height on the rectangle, IE doesn't, - * so we use r.bottom - r.top rather than r.height.) - */ - label.css('line-height', parseInt(liBottom) + 'px'); - } else { -/* - * If there is more than one line of text, then we shouldn't - * touch the line height, but we should make sure the text - * doesn't overflow the container. - */ - label.css("overflow", "hidden"); - } - } else { - thisLi.css('bottom', liBottom * i).prepend(''); - label.css('margin-top', -label.height() / 2) - } - }); - - charts.bar.draw(); - - }, - - draw: function () { - // Draw bars - if (horizontal) { - // for horizontal, keep the same code, but rotate everything 90 degrees - // clockwise. - ctx.rotate(Math.PI / 2); - } else { - // for vertical, translate to the top left corner. - ctx.translate(0, zeroLocY); - } - - // Don't attempt to draw anything if all the values are zero, - // otherwise we will get weird exceptions from the canvas methods. - if (totalYRange <= 0) return; - - var yScale = (horizontal ? canvas.width() : canvas.height()) / totalYRange; - var barWidth = horizontal ? (canvas.height() / xLabels.length) : (canvas.width() / (bottomLabels.length)); - var linewidth = (barWidth - o.barGroupMargin * 2) / dataGroups.length; - - for (var h = 0; h < dataGroups.length; h++) { - ctx.beginPath(); - - var strokeWidth = linewidth - (o.barMargin * 2); - ctx.lineWidth = strokeWidth; - var points = dataGroups[h].points; - var integer = 0; - for (var i = 0; i < points.length; i++) { - // If the last value is zero, IE will go nuts and not draw anything, - // so don't try to draw zero values at all. - if (points[i].value != 0) { - var xVal = (integer - o.barGroupMargin) + (h * linewidth) + linewidth / 2; - xVal += o.barGroupMargin * 2; - ctx.moveTo(xVal, 0); - ctx.lineTo(xVal, Math.round(-points[i].value * yScale)); - } - integer += barWidth; - } - ctx.strokeStyle = dataGroups[h].color; - ctx.stroke(); - ctx.closePath(); - } - - } - }; - - })(); - - //create new canvas, set w&h attrs (not inline styles) - var canvasNode = document.createElement("canvas"); - var canvas = $(canvasNode).attr({ - 'height': o.height, - 'width': o.width - }); - - //get title for chart - var title = o.title || self.find('caption').text(); - - //create canvas wrapper div, set inline w&h, append - var canvasContain = (container || $('
                ')).height(o.height).width(o.width); - - var scroller = $('
                ').appendTo(canvasContain).append(canvas); - - //title/key container - if (o.appendTitle || o.appendKey) { - var infoContain = $('
                ').appendTo(canvasContain); - } - - //append title - if (o.appendTitle) { - $('
                ' + title + '
                ').appendTo(infoContain); - } - - - //append key - if (o.appendKey) { - var newKey = $('
                  '); - $.each(yAllLabels, function (i, label) { - $('
                • ' + label + '
                • ').appendTo(newKey); - }); - newKey.appendTo(infoContain); - }; - - // init interaction - if (o.interaction) { - // sets the canvas to track interaction - // IE needs one div on top of the canvas since the VML shapes prevent mousemove from triggering correctly. - // Pie charts needs tracker because labels goes on top of the canvas and also messes up with mousemove - var tracker = $('
                  ').css({ - 'height': o.height + 'px', - 'width': o.width + 'px', - 'position': 'relative', - 'z-index': 200 - }).insertAfter(canvas); - - var triggerInteraction = function (overOut, data) { - var data = $.extend({ - canvasContain: canvasContain, - tableData: tableData - }, data); - self.trigger('vizualize' + overOut, data); - }; - - var over = false, - last = false, - started = false; - tracker.mousemove(function (e) { - var x, y, x1, y1, data, dist, i, current, selector, zLabel, elem, color, minDist, found, ev = e.originalEvent; - - // get mouse position relative to the tracker/canvas - x = ev.layerX || ev.offsetX || 0; - y = ev.layerY || ev.offsetY || 0; - - found = false; - minDist = started ? 30000 : (o.type == 'pie' ? (Math.round(canvas.height() / 2) - o.pieMargin) / 3 : o.lineWeight * 4); - // iterate datagroups to find points with matching - $.each(charts[o.type].interactionPoints, function (i, current) { - x1 = current.canvasCords[0] + zeroLocX; - y1 = current.canvasCords[1] + (o.type == "pie" ? 0 : zeroLocY); - dist = Math.sqrt((x1 - x) * (x1 - x) + (y1 - y) * (y1 - y)); - if (dist < minDist) { - found = current; - minDist = dist; - } - }); - - if (o.multiHover && found) { - x = found.canvasCords[0] + zeroLocX; - y = found.canvasCords[1] + (o.type == "pie" ? 0 : zeroLocY); - found = [found]; - $.each(charts[o.type].interactionPoints, function (i, current) { - if (current == found[0]) { - return; - } - x1 = current.canvasCords[0] + zeroLocX; - y1 = current.canvasCords[1] + zeroLocY; - dist = Math.sqrt((x1 - x) * (x1 - x) + (y1 - y) * (y1 - y)); - if (dist <= o.multiHover) { - found.push(current); - } - }); - } - // trigger over and out only when state changes, instead of on every mousemove - over = found; - if (over != last) { - if (over) { - if (last) { - triggerInteraction('Out', { - point: last - }); - } - triggerInteraction('Over', { - point: over - }); - last = over; - } - if (last && !over) { - triggerInteraction('Out', { - point: last - }); - last = false; - } - started = true; - } - }); - tracker.mouseleave(function () { - triggerInteraction('Out', { - point: last, - mouseOutGraph: true - }); - over = (last = false); - }); - } - - //append new canvas to page - if (!container) { - canvasContain.insertAfter(this); - } - if (typeof (G_vmlCanvasManager) != 'undefined') { - G_vmlCanvasManager.init(); - G_vmlCanvasManager.initElement(canvas[0]); - } - - //set up the drawing board - var ctx = canvas[0].getContext('2d'); - - // Scroll graphs - scroller.scrollLeft(o.width - scroller.width()); - - // init plugins - $.each($.visualizePlugins, function (i, plugin) { - plugin.call(self, o, tableData); - }); - - //create chart - charts[o.type].setup(); - - if (!container) { - //add event for updating - self.bind('visualizeRefresh', function () { - self.visualize(o, $(this).empty()); - }); - //add event for redraw - self.bind('visualizeRedraw', function () { - charts[o.type].draw(); - }); - } - }).next(); //returns canvas(es) - }; - // create array for plugins. if you wish to make a plugin, - // just push your init funcion into this array - $.visualizePlugins = []; - -})(jQuery); +/** + * -------------------------------------------------------------------- + * jQuery-Plugin "visualize" + * by Scott Jehl, scott@filamentgroup.com + * http://www.filamentgroup.com + * Copyright (c) 2009 Filament Group + * Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses. + * + * -------------------------------------------------------------------- + */ +(function($) { +$.fn.visualize = function(options, container){ + return $(this).each(function(){ + //configuration + var o = $.extend({ + type: 'bar', //also available: area, pie, line + width: $(this).width(), //height of canvas - defaults to table height + height: $(this).height(), //height of canvas - defaults to table height + appendTitle: true, //table caption text is added to chart + title: null, //grabs from table caption if null + appendKey: true, //color key is added to chart + colors: ['#be1e2d','#666699','#92d5ea','#ee8310','#8d10ee','#5a3b16','#26a4ed','#f45a90','#e9e744'], + textColors: [], //corresponds with colors array. null/undefined items will fall back to CSS + parseDirection: 'x', //which direction to parse the table data + pieMargin: 10, //pie charts only - spacing around pie + pieLabelsAsPercent: true, + pieLabelPos: 'inside', + lineWeight: 4, //for line and area - stroke weight + lineDots: false, //also available: 'single', 'double' + dotInnerColor: "#ffffff", // only used for lineDots:'double' + lineMargin: (options.lineDots?15:0), //for line and area - spacing around lines + barGroupMargin: 10, + chartId: '', + xLabelParser: null, // function to parse labels as values + valueParser: null, // function to parse values. must return a Number + chartId: '', + chartClass: '', + barMargin: 1, //space around bars in bar chart (added to both sides of bar) + yLabelInterval: 30, //distance between y labels + interaction: false // only used for lineDots != false -- triggers mouseover and mouseout on original table + },options); + + //reset width, height to numbers + o.width = parseFloat(o.width); + o.height = parseFloat(o.height); + + // reset padding if graph is not lines + if(o.type != 'line' && o.type != 'area' ) { + o.lineMargin = 0; + } + + var self = $(this); + + // scrape data from html table + var tableData = {}; + var colors = o.colors; + var textColors = o.textColors; + + + var parseLabels = function(direction){ + var labels = []; + if(direction == 'x'){ + self.find('thead tr').each(function(i){ + $(this).find('th').each(function(j){ + if(!labels[j]) { + labels[j] = []; + } + labels[j][i] = $(this).text() + }) + }); + } + else { + self.find('tbody tr').each(function(i){ + $(this).find('th').each(function(j) { + if(!labels[i]) { + labels[i] = []; + } + labels[i][j] = $(this).text() + }); + }); + } + return labels; + }; + + var fnParse = o.valueParser || parseFloat; + var dataGroups = tableData.dataGroups = []; + if(o.parseDirection == 'x'){ + self.find('tbody tr').each(function(i,tr){ + dataGroups[i] = {}; + dataGroups[i].points = []; + dataGroups[i].color = colors[i]; + if(textColors[i]){ dataGroups[i].textColor = textColors[i]; } + $(tr).find('td').each(function(j,td){ + dataGroups[i].points.push( { + value: fnParse($(td).text()), + elem: td, + tableCords: [i,j] + } ); + }); + }); + } else { + var cols = self.find('tbody tr:eq(0) td').size(); + for(var i=0; itableData.topValue) { + tableData.topValue = fnParse(item.value,10); + } + if(item.valuexTopValue) { + xTopValue = label; + } + if(label tableData.topValue+loopInterval) { + yLabels.pop(); + } else if (yLabels[yLabels.length-1] <= tableData.topValue-10) { + yLabels.push(tableData.topValue); + } + + // populate some data + $.each(dataGroups,function(i,row){ + row.yLabels = tableData.yAllLabels[i]; + $.each(row.points, function(j,point){ + point.zeroLocY = tableData.zeroLocY; + point.zeroLocX = tableData.zeroLocX; + point.xLabels = tableData.xAllLabels[j]; + point.yLabels = tableData.yAllLabels[i]; + point.color = row.color; + }); + }); + + try{console.log(tableData);}catch(e){} + + var charts = {}; + + charts.pie = { + interactionPoints: dataGroups, + + setup: function() { + charts.pie.draw(true); + }, + draw: function(drawHtml){ + + var centerx = Math.round(canvas.width()/2); + var centery = Math.round(canvas.height()/2); + var radius = centery - o.pieMargin; + var counter = 0.0; + + if(drawHtml) { + canvasContain.addClass('visualize-pie'); + + if(o.pieLabelPos == 'outside'){ canvasContain.addClass('visualize-pie-outside'); } + + var toRad = function(integer){ return (Math.PI/180)*integer; }; + var labels = $('
                    ') + .insertAfter(canvas); + } + + + //draw the pie pieces + $.each(dataGroups, function(i,row){ + var fraction = row.groupTotal / dataSum; + if (fraction <= 0 || isNaN(fraction)) + return; + ctx.beginPath(); + ctx.moveTo(centerx, centery); + ctx.arc(centerx, centery, radius, + counter * Math.PI * 2 - Math.PI * 0.5, + (counter + fraction) * Math.PI * 2 - Math.PI * 0.5, + false); + ctx.lineTo(centerx, centery); + ctx.closePath(); + ctx.fillStyle = dataGroups[i].color; + ctx.fill(); + // draw labels + if(drawHtml) { + var sliceMiddle = (counter + fraction/2); + var distance = o.pieLabelPos == 'inside' ? radius/1.5 : radius + radius / 5; + var labelx = Math.round(centerx + Math.sin(sliceMiddle * Math.PI * 2) * (distance)); + var labely = Math.round(centery - Math.cos(sliceMiddle * Math.PI * 2) * (distance)); + var leftRight = (labelx > centerx) ? 'right' : 'left'; + var topBottom = (labely > centery) ? 'bottom' : 'top'; + var percentage = parseFloat((fraction*100).toFixed(2)); + + // interaction variables + row.canvasCords = [labelx,labely]; + row.zeroLocY = tableData.zeroLocY = 0; // related to zeroLocY and plugin API + row.zeroLocX = tableData.zeroLocX = 0; // related to zeroLocX and plugin API + row.value = row.groupTotal; + + + if(percentage){ + var labelval = (o.pieLabelsAsPercent) ? percentage + '%' : row.groupTotal; + var labeltext = $('' + labelval +'') + .css(leftRight, 0) + .css(topBottom, 0); + if(labeltext) + var label = $('
                  • ') + .appendTo(labels) + .css({left: labelx, top: labely}) + .append(labeltext); + labeltext + .css('font-size', radius / 8) + .css('margin-'+leftRight, -labeltext.width()/2) + .css('margin-'+topBottom, -labeltext.outerHeight()/2); + + if(dataGroups[i].textColor){ labeltext.css('color', dataGroups[i].textColor); } + + } + } + counter+=fraction; + }); + } + }; + + (function(){ + + var xInterval; + + var drawPoint = function (ctx,x,y,color,size) { + ctx.moveTo(x,y); + ctx.beginPath(); + ctx.arc(x,y,size/2,0,2*Math.PI,false); + ctx.closePath(); + ctx.fillStyle = color; + ctx.fill(); + }; + + charts.line = { + + interactionPoints: allItems, + + setup: function(area){ + + if(area){ canvasContain.addClass('visualize-area'); } + else{ canvasContain.addClass('visualize-line'); } + + //write X labels + var xlabelsUL = $('
                      ') + .width(canvas.width()) + .height(canvas.height()) + .insertBefore(canvas); + + if(!o.customXLabels) { + xInterval = (canvas.width() - 2*o.lineMargin) / (xLabels.length -1); + $.each(xLabels, function(i){ + var thisLi = $('
                    • '+this+'
                    • ') + .prepend('') + .css('left', o.lineMargin + xInterval * i) + .appendTo(xlabelsUL); + var label = thisLi.find('span:not(.line)'); + var leftOffset = label.width()/-2; + if(i == 0){ leftOffset = 0; } + else if(i== xLabels.length-1){ leftOffset = -label.width(); } + label + .css('margin-left', leftOffset) + .addClass('label'); + }); + } else { + o.customXLabels(tableData,xlabelsUL); + } + + //write Y labels + var liBottom = (canvas.height() - 2*o.lineMargin) / (yLabels.length-1); + var ylabelsUL = $('
                        ') + .width(canvas.width()) + .height(canvas.height()) + // .css('margin-top',-o.lineMargin) + .insertBefore(scroller); + + $.each(yLabels, function(i){ + var value = Math.floor(this); + var posB = (value-bottomValue)*yScale + o.lineMargin; + if(posB >= o.height-1 || posB < 0) { + return; + } + var thisLi = $('
                      • '+value+'
                      • ') + .css('bottom', posB); + if(Math.abs(posB) < o.height-1) { + thisLi.prepend(''); + } + thisLi.prependTo(ylabelsUL); + + var label = thisLi.find('span:not(.line)'); + var topOffset = label.height()/-2; + if(!o.lineMargin) { + if(i == 0){ topOffset = -label.height(); } + else if(i== yLabels.length-1){ topOffset = 0; } + } + label + .css('margin-top', topOffset) + .addClass('label'); + }); + + //start from the bottom left + ctx.translate(zeroLocX,zeroLocY); + + charts.line.draw(area); + + }, + + draw: function(area) { + // prevent drawing on top of previous draw + ctx.clearRect(-zeroLocX,-zeroLocY,o.width,o.height); + // Calculate each point properties before hand + var integer; + $.each(dataGroups,function(i,row){ + integer = o.lineMargin; // the current offset + $.each(row.points, function(j,point){ + if(o.xLabelParser) { + point.canvasCords = [(xLabels[j]-zeroLocX)*xScale - xBottomValue,-(point.value*yScale)]; + } else { + point.canvasCords = [integer,-(point.value*yScale)]; + } + + if(o.lineDots) { + point.dotSize = o.dotSize||o.lineWeight*Math.PI; + point.dotInnerSize = o.dotInnerSize||o.lineWeight*Math.PI/2; + if(o.lineDots == 'double') { + point.innerColor = o.dotInnerColor; + } + } + integer+=xInterval; + }); + }); + // fire custom event so we can enable rich interaction + self.trigger('vizualizeBeforeDraw',{options:o,table:self,canvasContain:canvasContain,tableData:tableData}); + // draw lines and areas + $.each(dataGroups,function(h){ + // Draw lines + ctx.beginPath(); + ctx.lineWidth = o.lineWeight; + ctx.lineJoin = 'round'; + $.each(this.points, function(g){ + var loc = this.canvasCords; + if(g == 0) { + ctx.moveTo(loc[0],loc[1]); + } + ctx.lineTo(loc[0],loc[1]); + }); + ctx.strokeStyle = this.color; + ctx.stroke(); + // Draw fills + if(area){ + var integer = this.points[this.points.length-1].canvasCords[0]; + if (isFinite(integer)) + ctx.lineTo(integer,0); + ctx.lineTo(o.lineMargin,0); + ctx.closePath(); + ctx.fillStyle = this.color; + ctx.globalAlpha = .3; + ctx.fill(); + ctx.globalAlpha = 1.0; + } + else {ctx.closePath();} + }); + // draw points + if(o.lineDots) { + $.each(dataGroups,function(h){ + $.each(this.points, function(g){ + drawPoint(ctx,this.canvasCords[0],this.canvasCords[1],this.color,this.dotSize); + if(o.lineDots === 'double') { + drawPoint(ctx,this.canvasCords[0],this.canvasCords[1],this.innerColor,this.dotInnerSize); + } + }); + }); + } + + } + }; + + })(); + + charts.area = { + setup: function() { + charts.line.setup(true); + }, + draw: charts.line.draw + }; + + (function(){ + + var horizontal,bottomLabels; + + charts.bar = { + setup:function(){ + /** + * We can draw horizontal or vertical bars depending on the + * value of the 'barDirection' option (which may be 'vertical' or + * 'horizontal'). + */ + + horizontal = (o.barDirection == 'horizontal'); + + canvasContain.addClass('visualize-bar'); + + /** + * Write labels along the bottom of the chart. If we're drawing + * horizontal bars, these will be the yLabels, otherwise they + * will be the xLabels. The positioning also varies slightly: + * yLabels are values, hence they will span the whole width of + * the canvas, whereas xLabels are supposed to line up with the + * bars. + */ + bottomLabels = horizontal ? yLabels : xLabels; + + var xInterval = canvas.width() / (bottomLabels.length - (horizontal ? 1 : 0)); + + var xlabelsUL = $('
                          ') + .width(canvas.width()) + .height(canvas.height()) + .insertBefore(canvas); + + $.each(bottomLabels, function(i){ + var thisLi = $('
                        • '+this+'
                        • ') + .prepend('') + .css('left', xInterval * i) + .width(xInterval) + .appendTo(xlabelsUL); + + if (horizontal) { + var label = thisLi.find('span.label'); + label.css("margin-left", -label.width() / 2); + } + }); + + /** + * Write labels along the left of the chart. Follows the same idea + * as the bottom labels. + */ + var leftLabels = horizontal ? xLabels : yLabels; + var liBottom = canvas.height() / (leftLabels.length - (horizontal ? 0 : 1)); + + var ylabelsUL = $('
                            ') + .width(canvas.width()) + .height(canvas.height()) + .insertBefore(canvas); + + $.each(leftLabels, function(i){ + var thisLi = $('
                          • '+this+'
                          • ').prependTo(ylabelsUL); + + var label = thisLi.find('span:not(.line)').addClass('label'); + + if (horizontal) { + /** + * For left labels, we want to vertically align the text + * to the middle of its container, but we don't know how + * many lines of text we will have, since the labels could + * be very long. + * + * So we set a min-height of liBottom, and a max-height + * of liBottom + 1, so we can then check the label's actual + * height to determine if it spans one line or more lines. + */ + label.css({ + 'min-height': liBottom, + 'max-height': liBottom + 1, + 'vertical-align': 'middle' + }); + thisLi.css({'top': liBottom * i, 'min-height': liBottom}); + + var r = label[0].getClientRects()[0]; + if (r.bottom - r.top == liBottom) { + /* This means we have only one line of text; hence + * we can centre the text vertically by setting the line-height, + * as described at: + * http://www.ampsoft.net/webdesign-l/vertical-aligned-nav-list.html + * + * (Although firefox has .height on the rectangle, IE doesn't, + * so we use r.bottom - r.top rather than r.height.) + */ + label.css('line-height', parseInt(liBottom) + 'px'); + } + else { + /* + * If there is more than one line of text, then we shouldn't + * touch the line height, but we should make sure the text + * doesn't overflow the container. + */ + label.css("overflow", "hidden"); + } + } + else { + thisLi.css('bottom', liBottom * i).prepend(''); + label.css('margin-top', -label.height() / 2) + } + }); + + charts.bar.draw(); + + }, + + draw: function() { + // Draw bars + + if (horizontal) { + // for horizontal, keep the same code, but rotate everything 90 degrees + // clockwise. + ctx.rotate(Math.PI / 2); + } + else { + // for vertical, translate to the top left corner. + ctx.translate(0, zeroLocY); + } + + // Don't attempt to draw anything if all the values are zero, + // otherwise we will get weird exceptions from the canvas methods. + if (totalYRange <= 0) + return; + + var yScale = (horizontal ? canvas.width() : canvas.height()) / totalYRange; + var barWidth = horizontal ? (canvas.height() / xLabels.length) : (canvas.width() / (bottomLabels.length)); + var linewidth = (barWidth - o.barGroupMargin*2) / dataGroups.length; + + for(var h=0; h')) + .height(o.height) + .width(o.width); + + var scroller = $('
                            ') + .appendTo(canvasContain) + .append(canvas); + + //title/key container + if(o.appendTitle || o.appendKey){ + var infoContain = $('
                            ') + .appendTo(canvasContain); + } + + //append title + if(o.appendTitle){ + $('
                            '+ title +'
                            ').appendTo(infoContain); + } + + + //append key + if(o.appendKey){ + var newKey = $('
                              '); + $.each(yAllLabels, function(i,label){ + $('
                            • '+ label +'
                            • ') + .appendTo(newKey); + }); + newKey.appendTo(infoContain); + }; + + // init interaction + if(o.interaction) { + // sets the canvas to track interaction + // IE needs one div on top of the canvas since the VML shapes prevent mousemove from triggering correctly. + // Pie charts needs tracker because labels goes on top of the canvas and also messes up with mousemove + var tracker = $('
                              ') + .css({ + 'height': o.height + 'px', + 'width': o.width + 'px', + 'position':'relative', + 'z-index': 200 + }) + .insertAfter(canvas); + + var triggerInteraction = function(overOut,data) { + var data = $.extend({ + canvasContain:canvasContain, + tableData:tableData + },data); + self.trigger('vizualize'+overOut,data); + }; + + var over=false, last=false, started=false; + tracker.mousemove(function(e){ + var x,y,x1,y1,data,dist,i,current,selector,zLabel,elem,color,minDist,found,ev=e.originalEvent; + + // get mouse position relative to the tracker/canvas + x = ev.layerX || ev.offsetX || 0; + y = ev.layerY || ev.offsetY || 0; + + found = false; + minDist = started?30000:(o.type=='pie'?(Math.round(canvas.height()/2)-o.pieMargin)/3:o.lineWeight*4); + // iterate datagroups to find points with matching + $.each(charts[o.type].interactionPoints,function(i,current){ + x1 = current.canvasCords[0] + zeroLocX; + y1 = current.canvasCords[1] + (o.type=="pie"?0:zeroLocY); + dist = Math.sqrt( (x1 - x)*(x1 - x) + (y1 - y)*(y1 - y) ); + if(dist < minDist) { + found = current; + minDist = dist; + } + }); + + if(o.multiHover && found) { + x = found.canvasCords[0] + zeroLocX; + y = found.canvasCords[1] + (o.type=="pie"?0:zeroLocY); + found = [found]; + $.each(charts[o.type].interactionPoints,function(i,current){ + if(current == found[0]) {return;} + x1 = current.canvasCords[0] + zeroLocX; + y1 = current.canvasCords[1] + zeroLocY; + dist = Math.sqrt( (x1 - x)*(x1 - x) + (y1 - y)*(y1 - y) ); + if(dist <= o.multiHover) { + found.push(current); + } + }); + } + // trigger over and out only when state changes, instead of on every mousemove + over = found; + if(over != last) { + if(over) { + if(last) { + triggerInteraction('Out',{point:last}); + } + triggerInteraction('Over',{point:over}); + last = over; + } + if(last && !over) { + triggerInteraction('Out',{point:last}); + last=false; + } + started=true; + } + }); + tracker.mouseleave(function(){ + triggerInteraction('Out',{ + point:last, + mouseOutGraph:true + }); + over = (last = false); + }); + } + + //append new canvas to page + if(!container){canvasContain.insertAfter(this); } + if( typeof(G_vmlCanvasManager) != 'undefined' ){ G_vmlCanvasManager.init(); G_vmlCanvasManager.initElement(canvas[0]); } + + //set up the drawing board + var ctx = canvas[0].getContext('2d'); + + // Scroll graphs + scroller.scrollLeft(o.width-scroller.width()); + + // init plugins + $.each($.visualizePlugins,function(i,plugin){ + plugin.call(self,o,tableData); + }); + + //create chart + charts[o.type].setup(); + + if(!container){ + //add event for updating + self.bind('visualizeRefresh', function(){ + self.visualize(o, $(this).empty()); + }); + //add event for redraw + self.bind('visualizeRedraw', function(){ + charts[o.type].draw(); + }); + } + }).next(); //returns canvas(es) +}; +// create array for plugins. if you wish to make a plugin, +// just push your init funcion into this array +$.visualizePlugins = []; + +})(jQuery); + + From 32d94bb6fb3d089cbbc61dfe275a440d9a69da59 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 22:00:58 +0200 Subject: [PATCH 136/168] less wide graphs --- public/site_assets/mmcFE/js/custom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/site_assets/mmcFE/js/custom.js b/public/site_assets/mmcFE/js/custom.js index fcecd8e7..ab1b5167 100644 --- a/public/site_assets/mmcFE/js/custom.js +++ b/public/site_assets/mmcFE/js/custom.js @@ -19,7 +19,7 @@ $(function () { // calculate width of graph so it doesnt overflow its parent div //var chart_width = ($(this).parent('div').width()) - 60; // hack to statically set width as something is broken with div width calculation - anni - var chart_width = $(document).width() - 340; + var chart_width = $(document).width() - 400; if (statsType == 'line' || statsType == 'pie') { $(this).hide().visualize({ From 12843fab14d51d476d70b1259cea26e0d894b44a Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 22:01:18 +0200 Subject: [PATCH 137/168] adding new master template for tooltips --- public/templates/mmcFE/master.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/public/templates/mmcFE/master.tpl b/public/templates/mmcFE/master.tpl index aff628f4..4c3204dc 100644 --- a/public/templates/mmcFE/master.tpl +++ b/public/templates/mmcFE/master.tpl @@ -11,6 +11,7 @@ + From 2b5642327224d1893cfb9ec60ab8c37205fd2a52 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 22:01:34 +0200 Subject: [PATCH 138/168] adding line graphs in block stats --- 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 0cfec471..6f9f7687 100644 --- a/public/templates/mmcFE/statistics/blocks/default.tpl +++ b/public/templates/mmcFE/statistics/blocks/default.tpl @@ -1,5 +1,5 @@ {include file="global/block_header.tpl" BLOCK_HEADER="Block Luck" BLOCK_STYLE="clear:none;"} -
                              {$BLOCKSFOUND[block].height}
                              Expected Shares{(pow(2,32 - $GLOBAL.config.targetdiff) * $BLOCKSFOUND[block].difficulty)}
                              Actual Shares{$BLOCKSFOUND[block].shares}
                              +
                              {section block $BLOCKSFOUND step=-1} From e7bb725da444fbd982c6253dbd34173453fe4b85 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 22:03:21 +0200 Subject: [PATCH 139/168] remove decimals for expected shares in graph --- 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 6f9f7687..515f5dbb 100644 --- a/public/templates/mmcFE/statistics/blocks/default.tpl +++ b/public/templates/mmcFE/statistics/blocks/default.tpl @@ -11,7 +11,7 @@ {section block $BLOCKSFOUND step=-1} - + {/section} From 90640d345e085fa09cdb42994896430f475069f2 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 22:26:12 +0200 Subject: [PATCH 140/168] removing static-ish stats width, use table width instead --- public/site_assets/mmcFE/js/custom.js | 10 +--------- public/templates/mmcFE/statistics/blocks/default.tpl | 9 +++++---- public/templates/mmcFE/statistics/user/default.tpl | 4 ++-- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/public/site_assets/mmcFE/js/custom.js b/public/site_assets/mmcFE/js/custom.js index ab1b5167..76e967c6 100644 --- a/public/site_assets/mmcFE/js/custom.js +++ b/public/site_assets/mmcFE/js/custom.js @@ -16,19 +16,12 @@ $(function () { var statsType = 'area'; } - // calculate width of graph so it doesnt overflow its parent div - //var chart_width = ($(this).parent('div').width()) - 60; - // hack to statically set width as something is broken with div width calculation - anni - var chart_width = $(document).width() - 400; - if (statsType == 'line' || statsType == 'pie') { $(this).hide().visualize({ type: statsType, // 'bar', 'area', 'pie', 'line' - width: chart_width, height: '240px', colors: ['#6fb9e8', '#ec8526', '#9dc453', '#ddd74c'], - lineDots: 'double', interaction: true, multiHover: 5, @@ -43,9 +36,8 @@ $(function () { }); } else { $(this).hide().visualize({ - type: statsType, // 'bar', 'area', 'pie', 'line' - width: chart_width, + type: statsType, height: '240px', colors: ['#6fb9e8', '#ec8526', '#9dc453', '#ddd74c'] }); diff --git a/public/templates/mmcFE/statistics/blocks/default.tpl b/public/templates/mmcFE/statistics/blocks/default.tpl index 515f5dbb..8bcad85b 100644 --- a/public/templates/mmcFE/statistics/blocks/default.tpl +++ b/public/templates/mmcFE/statistics/blocks/default.tpl @@ -1,5 +1,6 @@ -{include file="global/block_header.tpl" BLOCK_HEADER="Block Luck" BLOCK_STYLE="clear:none;"} -
                              Expected Shares{(pow(2,32 - $GLOBAL.config.targetdiff) * $BLOCKSFOUND[block].difficulty)}{round(pow(2,32 - $GLOBAL.config.targetdiff) * $BLOCKSFOUND[block].difficulty)}
                              +{include file="global/block_header.tpl" BLOCK_HEADER="Block Shares" BLOCK_STYLE="clear:none;"} +
                              + {section block $BLOCKSFOUND step=-1} @@ -9,13 +10,13 @@ - + {section block $BLOCKSFOUND step=-1} {/section} - + {section block $BLOCKSFOUND step=-1} {/section} diff --git a/public/templates/mmcFE/statistics/user/default.tpl b/public/templates/mmcFE/statistics/user/default.tpl index dc285321..debbd23a 100644 --- a/public/templates/mmcFE/statistics/user/default.tpl +++ b/public/templates/mmcFE/statistics/user/default.tpl @@ -1,8 +1,8 @@ {include file="global/block_header.tpl" BLOCK_HEADER="Your Average Hourly Hash Rate" BUTTONS=array(mine,pool,both)} {if is_array($YOURHASHRATES)}
                              -
                              Block Shares
                              Expected SharesExpected{round(pow(2,32 - $GLOBAL.config.targetdiff) * $BLOCKSFOUND[block].difficulty)}
                              Actual SharesActual{$BLOCKSFOUND[block].shares}
                              - +
                              Your Hashrate 
                              + From 6f858188ad53a743307ece3369644e89b618cbf9 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Jun 2013 22:59:19 +0200 Subject: [PATCH 141/168] Adding pool and combined hashrate graphs * Moving from My Graph to its own section: Hashrate Graphs * Adding pool hashrate graph * Adding combined hashrate graph and piechart Fixes #187 --- public/include/classes/statistics.class.php | 29 ++++++++++++- .../{user.inc.php => graphs.inc.php} | 2 + public/templates/mmcFE/global/navigation.tpl | 2 +- .../mmcFE/statistics/graphs/both.tpl | 43 +++++++++++++++++++ .../mmcFE/statistics/graphs/default.tpl | 5 +++ .../{user/default.tpl => graphs/mine.tpl} | 2 - .../mmcFE/statistics/graphs/pool.tpl | 31 +++++++++++++ 7 files changed, 110 insertions(+), 4 deletions(-) rename public/include/pages/statistics/{user.inc.php => graphs.inc.php} (74%) create mode 100644 public/templates/mmcFE/statistics/graphs/both.tpl create mode 100644 public/templates/mmcFE/statistics/graphs/default.tpl rename public/templates/mmcFE/statistics/{user/default.tpl => graphs/mine.tpl} (83%) create mode 100644 public/templates/mmcFE/statistics/graphs/pool.tpl diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index 3bcebcfb..46ab5cb5 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -293,7 +293,6 @@ class Statistics { /** * get Hourly hashrate for a user - * Not working yet since I was not able to solve this via SQL queries * @param account_id int User ID * @return data array NOT FINISHED YET **/ @@ -321,6 +320,34 @@ class Statistics { $this->debug->append("Failed to fetch hourly hashrate: " . $this->mysqli->error); return false; } + + /** + * get Hourly hashrate for the pool + * @param none + * @return data array NOT FINISHED YET + **/ + public function getHourlyHashrateByPool() { + $this->debug->append("STA " . __METHOD__, 4); + if ($data = $this->memcache->get(__FUNCTION__)) return $data; + $stmt = $this->mysqli->prepare(" + SELECT + ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000) 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) + "); + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) { + while ($row = $result->fetch_assoc()) { + $aData[$row['hour']] = $row['hashrate']; + } + return $this->memcache->setCache(__FUNCTION__, $aData); + } + // Catchall + $this->debug->append("Failed to fetch hourly hashrate: " . $this->mysqli->error); + return false; + } } $statistics = new Statistics($debug, $mysqli, $config, $share, $user, $block, $memcache); diff --git a/public/include/pages/statistics/user.inc.php b/public/include/pages/statistics/graphs.inc.php similarity index 74% rename from public/include/pages/statistics/user.inc.php rename to public/include/pages/statistics/graphs.inc.php index 13b3ba5b..cf62bddf 100644 --- a/public/include/pages/statistics/user.inc.php +++ b/public/include/pages/statistics/graphs.inc.php @@ -6,8 +6,10 @@ if (!defined('SECURITY')) if ($user->isAuthenticated()) { $aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA']['id']); + $aPoolHourlyHashRates = $statistics->getHourlyHashrateByPool(); // Propagate content our template $smarty->assign("YOURHASHRATES", $aHourlyHashRates); + $smarty->assign("POOLHASHRATES", $aPoolHourlyHashRates); $smarty->assign("CONTENT", "default.tpl"); } ?> diff --git a/public/templates/mmcFE/global/navigation.tpl b/public/templates/mmcFE/global/navigation.tpl index 9beb87cb..c53f61c7 100644 --- a/public/templates/mmcFE/global/navigation.tpl +++ b/public/templates/mmcFE/global/navigation.tpl @@ -5,7 +5,6 @@ @@ -23,6 +22,7 @@
                            • Getting Started
                            • diff --git a/public/templates/mmcFE/statistics/graphs/both.tpl b/public/templates/mmcFE/statistics/graphs/both.tpl new file mode 100644 index 00000000..ff760be1 --- /dev/null +++ b/public/templates/mmcFE/statistics/graphs/both.tpl @@ -0,0 +1,43 @@ +{if is_array($YOURHASHRATES) && is_array($POOLHASHRATES)} +
                              +{foreach from=array('area','pie') item=chartType} +
                              Your Hashrate
                              + + + + +{for $i=date('G') to 23} + +{/for} +{for $i=0 to date('G', time () - 60 * 60)} + +{/for} + + + + + +{for $i=date('G') to 23} + +{/for} +{for $i=0 to date('G', time() - 60 * 60)} + +{/for} + + + +{for $i=date('G') to 23} + +{/for} +{for $i=0 to date('G', time() - 60 * 60)} + +{/for} + + +
                              Your vs Pool Hashrate
                              {$i}:00{$i}:00
                              {$smarty.session.USERDATA.username}{$YOURHASHRATES.$i|default:"0"}{$YOURHASHRATES.$i|default:"0"}
                              Pool{$POOLHASHRATES.$i|default:"0"}{$POOLHASHRATES.$i|default:"0"}
                              +
                              +{/foreach} + +{else} +

                            • No shares available to start calculations
                            • +{/if} diff --git a/public/templates/mmcFE/statistics/graphs/default.tpl b/public/templates/mmcFE/statistics/graphs/default.tpl new file mode 100644 index 00000000..c325a4c6 --- /dev/null +++ b/public/templates/mmcFE/statistics/graphs/default.tpl @@ -0,0 +1,5 @@ +{include file="global/block_header.tpl" BLOCK_HEADER="24h Hashrate Statistics" BUTTONS=array(mine,pool,both)} +{include file="{$smarty.request.page}/{$smarty.request.action}/mine.tpl"} +{include file="{$smarty.request.page}/{$smarty.request.action}/pool.tpl"} +{include file="{$smarty.request.page}/{$smarty.request.action}/both.tpl"} +{include file="global/block_footer.tpl"} diff --git a/public/templates/mmcFE/statistics/user/default.tpl b/public/templates/mmcFE/statistics/graphs/mine.tpl similarity index 83% rename from public/templates/mmcFE/statistics/user/default.tpl rename to public/templates/mmcFE/statistics/graphs/mine.tpl index debbd23a..fe217f2f 100644 --- a/public/templates/mmcFE/statistics/user/default.tpl +++ b/public/templates/mmcFE/statistics/graphs/mine.tpl @@ -1,4 +1,3 @@ -{include file="global/block_header.tpl" BLOCK_HEADER="Your Average Hourly Hash Rate" BUTTONS=array(mine,pool,both)} {if is_array($YOURHASHRATES)}
                              @@ -30,4 +29,3 @@ {else}

                            • No shares available to start calculations
                            • {/if} -{include file="global/block_footer.tpl"} diff --git a/public/templates/mmcFE/statistics/graphs/pool.tpl b/public/templates/mmcFE/statistics/graphs/pool.tpl new file mode 100644 index 00000000..ba2ed4e5 --- /dev/null +++ b/public/templates/mmcFE/statistics/graphs/pool.tpl @@ -0,0 +1,31 @@ +{if is_array($POOLHASHRATES)} +
                              +
                              + + + + +{for $i=date('G') to 23} + +{/for} +{for $i=0 to date('G', time () - 60 * 60)} + +{/for} + + + + + +{for $i=date('G') to 23} + +{/for} +{for $i=0 to date('G', time() - 60 * 60)} + +{/for} + + +
                              Pool Hashrate
                              {$i}:00{$i}:00
                              Pool{$POOLHASHRATES.$i|default:"0"}{$POOLHASHRATES.$i|default:"0"}
                              +
                              +{else} +

                            • No shares available to start calculations
                            • +{/if} From 6f3912b6a2a4b56352b6badcffb28384503396e4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 14 Jun 2013 09:58:54 +0200 Subject: [PATCH 142/168] fixed worker name in mail body --- cronjobs/notifications.php | 1 + public/templates/mail/notifications/idle_worker.tpl | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cronjobs/notifications.php b/cronjobs/notifications.php index a6b8b101..ec61002a 100755 --- a/cronjobs/notifications.php +++ b/cronjobs/notifications.php @@ -31,6 +31,7 @@ if (empty($aWorkers)) { $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']); if (!$notification->sendNotification($aWorker['account_id'], 'idle_worker', $aData)) verbose($notification->getError() . "\n"); diff --git a/public/templates/mail/notifications/idle_worker.tpl b/public/templates/mail/notifications/idle_worker.tpl index 6d1c282c..3ef12840 100644 --- a/public/templates/mail/notifications/idle_worker.tpl +++ b/public/templates/mail/notifications/idle_worker.tpl @@ -1,7 +1,9 @@ -

                              One of your workers is currently IDLE: {$DATA.username}

                              +

                              One of your workers is currently IDLE: {$DATA.worker}

                              +

                              We have not received any shares for this worker in the past 10 minutes.

                              Since monitoring is enabled for this worker, this notification was sent.

                              +

                              Please check your workers!



                              From bee35325dcabb94e6a46d384305f9cb82be6b74a Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 14 Jun 2013 11:38:00 +0200 Subject: [PATCH 143/168] Only show 20 blocks for block graph Graph dynamically expands if the blocks don't fit so it's now limited to 20 which should always fit well enough. --- public/templates/mmcFE/statistics/blocks/default.tpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/templates/mmcFE/statistics/blocks/default.tpl b/public/templates/mmcFE/statistics/blocks/default.tpl index 8bcad85b..cde7dcb3 100644 --- a/public/templates/mmcFE/statistics/blocks/default.tpl +++ b/public/templates/mmcFE/statistics/blocks/default.tpl @@ -3,7 +3,7 @@ Block Shares -{section block $BLOCKSFOUND step=-1} +{section block $BLOCKSFOUND step=-1 max=20} {$BLOCKSFOUND[block].height} {/section} @@ -11,13 +11,13 @@ Expected -{section block $BLOCKSFOUND step=-1} +{section block $BLOCKSFOUND step=-1 max=20} {round(pow(2,32 - $GLOBAL.config.targetdiff) * $BLOCKSFOUND[block].difficulty)} {/section} Actual -{section block $BLOCKSFOUND step=-1} +{section block $BLOCKSFOUND step=-1 max=20} {$BLOCKSFOUND[block].shares} {/section} From c0870b3a481df187ca7ccd3ec05110f921e0c2c2 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 14 Jun 2013 11:58:41 +0200 Subject: [PATCH 144/168] Added more generic pool stats * added pool efficiency based on valid and invalid shares * added estimated shares progress based on est and valid shares --- .../templates/mmcFE/statistics/pool/authenticated.tpl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/public/templates/mmcFE/statistics/pool/authenticated.tpl b/public/templates/mmcFE/statistics/pool/authenticated.tpl index 5c67bbcd..822609e8 100644 --- a/public/templates/mmcFE/statistics/pool/authenticated.tpl +++ b/public/templates/mmcFE/statistics/pool/authenticated.tpl @@ -12,7 +12,11 @@ {($GLOBAL.hashrate / 1000)|number_format:"3"} Mhash/s - Current Workers Mining + Pool Efficiency + {100 - (100 / $GLOBAL.roundshares.valid * $GLOBAL.roundshares.invalid)|number_format:"2"} % + + + Current Active Workers {$GLOBAL.workers} @@ -32,8 +36,8 @@ {$ESTTIME|seconds_to_words} - Est. Avg. Shares per Round - {(pow(2, 32 - $GLOBAL.config.targetdiff) * $DIFFICULTY)|number_format:"0"} + Est. Shares this Round + {(pow(2, 32 - $GLOBAL.config.targetdiff) * $DIFFICULTY)|number_format:"0"} (done: {(100 / (pow(2, 32 - $GLOBAL.config.targetdiff) * $DIFFICULTY) * $GLOBAL.roundshares.valid)|number_format:"2"} %) Time Since Last Block From d23d5773688c2a06cebb3c226f5ccc6735448fb5 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 14 Jun 2013 12:36:17 +0200 Subject: [PATCH 145/168] always show 2 digit pool efficiency --- public/templates/mmcFE/statistics/pool/authenticated.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/templates/mmcFE/statistics/pool/authenticated.tpl b/public/templates/mmcFE/statistics/pool/authenticated.tpl index 822609e8..edc0e0ba 100644 --- a/public/templates/mmcFE/statistics/pool/authenticated.tpl +++ b/public/templates/mmcFE/statistics/pool/authenticated.tpl @@ -13,7 +13,7 @@ Pool Efficiency - {100 - (100 / $GLOBAL.roundshares.valid * $GLOBAL.roundshares.invalid)|number_format:"2"} % + {(100 - (100 / $GLOBAL.roundshares.valid * $GLOBAL.roundshares.invalid))|number_format:"2"} % Current Active Workers From 4c4202bc1b56094b5969aa8ac7dcbb3fda3272d9 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 14 Jun 2013 13:22:50 +0200 Subject: [PATCH 146/168] fixing notice warning when not logged in --- public/include/smarty_globals.inc.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index 257d33d4..5c4bde10 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -7,15 +7,16 @@ if (!defined('SECURITY')) // Globally available variables $debug->append('Global smarty variables', 3); +// Defaults to get rid of PHP Notice warnings +$dDifficulty = 1; + // Only run these if the user is logged in if ($_SESSION['AUTHENTICATED']) { $aRoundShares = $statistics->getRoundShares(); - if ($bitcoin->can_connect() === true){ + if ($bitcoin->can_connect() === true) $dDifficulty = $bitcoin->query('getdifficulty'); - } else { - $dDifficulty = 1; - } } + // Fetch some data $iCurrentActiveWorkers = $worker->getCountAllActiveWorkers(); $iCurrentPoolHashrate = $statistics->getCurrentHashrate(); From 0817befaaa79a3386c8061f40fdc455233093951 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 14 Jun 2013 13:51:06 +0200 Subject: [PATCH 147/168] Further cleanup to reduce PHP notice warnings --- public/include/classes/statistics.class.php | 1 + public/include/classes/user.class.php | 2 +- public/include/pages/account/edit.inc.php | 4 ++-- public/include/pages/account/notifications.inc.php | 2 +- public/include/pages/account/workers.inc.php | 6 ++++-- public/include/pages/admin/user.inc.php | 10 +++++----- public/include/pages/statistics/graphs.inc.php | 9 +++++---- public/include/pages/statistics/pool.inc.php | 2 ++ public/include/smarty_globals.inc.php | 3 ++- public/templates/mmcFE/account/edit/default.tpl | 2 +- .../mmcFE/statistics/pool/contributors_hashrate.tpl | 2 +- 11 files changed, 25 insertions(+), 18 deletions(-) diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index 46ab5cb5..6f56453b 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -311,6 +311,7 @@ class Statistics { GROUP BY HOUR(time) "); if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result()) { + $aData = array(); while ($row = $result->fetch_assoc()) { $aData[$row['hour']] = $row['hashrate']; } diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 20ff072d..49de7b5f 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -552,7 +552,7 @@ class User { **/ public function isAuthenticated() { $this->debug->append("STA " . __METHOD__, 4); - if ($_SESSION['AUTHENTICATED'] == true && ! $this->isLocked($_SESSION['USERDATA']['id']) && $this->getUserIp($_SESSION['USERDATA']['id']) == $_SERVER['REMOTE_ADDR']) + if (@$_SESSION['AUTHENTICATED'] == true && ! $this->isLocked($_SESSION['USERDATA']['id']) && $this->getUserIp($_SESSION['USERDATA']['id']) == $_SERVER['REMOTE_ADDR']) return true; // Catchall $this->logoutUser(); diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php index a4859c8f..92b8f1e1 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -5,10 +5,10 @@ if (!defined('SECURITY')) die('Hacking attempt'); if ($user->isAuthenticated()) { - if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST['do']) { + if ( ! $user->checkPin($_SESSION['USERDATA']['id'], @$_POST['authPin']) && @$_POST['do']) { $_SESSION['POPUP'][] = array('CONTENT' => 'Invalid PIN','TYPE' => 'errormsg'); } else { - switch ($_POST['do']) { + switch (@$_POST['do']) { case 'cashOut': if ($setting->getValue('manual_payout_active') == 1) { $_SESSION['POPUP'][] = array('CONTENT' => 'A manual payout is in progress. Please try again later.', 'TYPE' => 'errormsg'); diff --git a/public/include/pages/account/notifications.inc.php b/public/include/pages/account/notifications.inc.php index 87fd6217..3f013ed0 100644 --- a/public/include/pages/account/notifications.inc.php +++ b/public/include/pages/account/notifications.inc.php @@ -3,7 +3,7 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); if ($user->isAuthenticated()) { - if ($_REQUEST['do'] == 'save') { + if (@$_REQUEST['do'] == 'save') { if ($notification->updateSettings($_SESSION['USERDATA']['id'], $_REQUEST['data'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'Updated notification settings'); } else { diff --git a/public/include/pages/account/workers.inc.php b/public/include/pages/account/workers.inc.php index 78556424..ccdae2b8 100644 --- a/public/include/pages/account/workers.inc.php +++ b/public/include/pages/account/workers.inc.php @@ -4,7 +4,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); if ($user->isAuthenticated()) { - switch ($_REQUEST['do']) { + switch (@$_REQUEST['do']) { case 'delete': if ($worker->deleteWorker($_SESSION['USERDATA']['id'], $_GET['id'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'Worker removed'); @@ -31,7 +31,9 @@ if ($user->isAuthenticated()) { $aWorkers = $worker->getWorkers($_SESSION['USERDATA']['id']); if (!$aWorkers) $_SESSION['POPUP'][] = array('CONTENT' => 'You have no workers configured', 'TYPE' => 'errormsg'); - $smarty->assign('CONTENT', 'default.tpl'); $smarty->assign('WORKERS', $aWorkers); } + +$smarty->assign('CONTENT', 'default.tpl'); + ?> diff --git a/public/include/pages/admin/user.inc.php b/public/include/pages/admin/user.inc.php index 11808edb..9bb9ecee 100644 --- a/public/include/pages/admin/user.inc.php +++ b/public/include/pages/admin/user.inc.php @@ -12,18 +12,18 @@ if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) { $aRoundShares = $statistics->getRoundShares(); // Change account lock -if ($_POST['do'] == 'lock') { +if (@$_POST['do'] == 'lock') { $supress_master = 1; $user->changeLocked($_POST['account_id']); } // Change account admin -if ($_POST['do'] == 'admin') { +if (@$_POST['do'] == 'admin') { $supress_master = 1; $user->changeAdmin($_POST['account_id']); } -if ($_POST['query']) { +if (@$_POST['query']) { // Fetch requested users $aUsers = $statistics->getAllUserStats($_POST['query']); @@ -40,10 +40,10 @@ if ($_POST['query']) { $aUser['payout']['est_payout'] = round($aUser['payout']['est_block'] - $aUser['payout']['est_donation'] - $aUser['payout']['est_fee'], 3); $aUsers[$iKey] = $aUser; } + // Assign our variables + $smarty->assign("USERS", $aUsers); } -// Assign our variables -$smarty->assign("USERS", $aUsers); // Tempalte specifics $smarty->assign("CONTENT", "default.tpl"); diff --git a/public/include/pages/statistics/graphs.inc.php b/public/include/pages/statistics/graphs.inc.php index cf62bddf..f7016835 100644 --- a/public/include/pages/statistics/graphs.inc.php +++ b/public/include/pages/statistics/graphs.inc.php @@ -7,9 +7,10 @@ if (!defined('SECURITY')) if ($user->isAuthenticated()) { $aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA']['id']); $aPoolHourlyHashRates = $statistics->getHourlyHashrateByPool(); - // Propagate content our template - $smarty->assign("YOURHASHRATES", $aHourlyHashRates); - $smarty->assign("POOLHASHRATES", $aPoolHourlyHashRates); - $smarty->assign("CONTENT", "default.tpl"); } + +// 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 fc546e72..9650681e 100644 --- a/public/include/pages/statistics/pool.inc.php +++ b/public/include/pages/statistics/pool.inc.php @@ -27,6 +27,8 @@ $aBlockData = $aBlocksFoundData[0]; // Estimated time to find the next block $iCurrentPoolHashrate = $statistics->getCurrentHashrate(); +$iCurrentPoolHashrate == 0 ? $iCurrentPoolHashrate = 1 : true; + // Time in seconds, not hours, using modifier in smarty to translate $iEstTime = $dDifficulty * pow(2,32) / ($iCurrentPoolHashrate * 1000); diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index 5c4bde10..c8231a49 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -9,9 +9,10 @@ $debug->append('Global smarty variables', 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']) { +if (@$_SESSION['AUTHENTICATED']) { $aRoundShares = $statistics->getRoundShares(); if ($bitcoin->can_connect() === true) $dDifficulty = $bitcoin->query('getdifficulty'); diff --git a/public/templates/mmcFE/account/edit/default.tpl b/public/templates/mmcFE/account/edit/default.tpl index f48ef67f..69861d61 100644 --- a/public/templates/mmcFE/account/edit/default.tpl +++ b/public/templates/mmcFE/account/edit/default.tpl @@ -23,7 +23,7 @@ - +
                              Account Balance:    {$GLOBAL.userdata.balance.confirmed|escape} {$GLOABL.config.currency}
                              Account Balance:    {$GLOBAL.userdata.balance.confirmed|escape} {$GLOBAL.config.currency}
                              Payout to:
                              {$GLOBAL.userdata.coin_address|escape}
                              4 digit PIN:
                              diff --git a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl index 9086427a..2392af09 100644 --- a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl +++ b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl @@ -28,7 +28,7 @@ n/a {$GLOBAL.userdata.username} {$GLOBAL.userdata.hashrate} - {$estday|number_format:"3"} + {$estday|number_format:"3"|default:"n/a"} {($estday * $GLOBAL.price)|default:"n/a"|number_format:"2"} {/if} From 006f4d7f8a00a6a944f90baea8b3312a0b016e47 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 14 Jun 2013 16:17:34 +0200 Subject: [PATCH 148/168] updated PPS payout to support block share stats for users --- cronjobs/pps_payout.php | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php index efdbfc94..06d4f3b4 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -93,12 +93,27 @@ if (empty($aAllBlocks)) { // Go through blocks and archive/delete shares that have been accounted for foreach ($aAllBlocks as $iIndex => $aBlock) { - $dummy = $iIndex - 1; - if ($config['archive_shares'] && $aBlock['share_id'] < $iLastShareId) { - $share->moveArchive($aBlock['share_id'], $aBlock['id'], @$aAllBlocks[$dummy]['share_id']); + // 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; + // 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"); } - if ($aBlock['share_id'] < $iLastShareId && !$share->deleteAccountedShares($aBlock['share_id'], @$aAllBlocks[$dummy]['share_id'])) { - verbose("\nERROR : Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . @$aAllBlocks[$dummy]['share_id'] . ", aborting!\n"); + // 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"); + } + // 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"); + exit(1); + } + // Mark this block as accounted for + if (!$block->setAccounted($aBlock['id'])) { + verbose("\nERROR : Failed to mark block as accounted! Aborting!\n"); exit(1); } } From ca1237c11cc4ea6982e75e90a5001d37edd3f24d Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 14 Jun 2013 17:52:56 +0200 Subject: [PATCH 149/168] fixing admin user information table format --- public/templates/mmcFE/admin/user/default.tpl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/public/templates/mmcFE/admin/user/default.tpl b/public/templates/mmcFE/admin/user/default.tpl index 8cfc263a..82955741 100644 --- a/public/templates/mmcFE/admin/user/default.tpl +++ b/public/templates/mmcFE/admin/user/default.tpl @@ -67,7 +67,7 @@ {sectionelse} - + {/section} @@ -77,11 +77,12 @@ Username E-Mail Shares   - Hashrate - Est. Donation - Est. Payout - Balance + Hashrate   + Est. Donation   + Est. Payout    + Balance    Admin + Locked From b7b0ab92984ca00efc0d03b147a8873c23bdcaf9 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 14 Jun 2013 18:55:17 +0300 Subject: [PATCH 150/168] Update README.md Adding new donor. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c7e8998a..ae08e1fc 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ These people have supported this project with a donation: * [obigal](https://github.com/obigal) * [vias](https://github.com/vias79) +* [WKNiGHT](https://github.com/WKNiGHT-) Requirements ============ From 91ef2caaab7663b744098832b2eeff04d4e37ec9 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 17 Jun 2013 10:07:40 +0200 Subject: [PATCH 151/168] Added cronjob to pre-cache statistics This will fix #199 and help on loading times for the website in case caches are empty. Caches are pre-filled by a cron so the website only does it as a fall back. Check Ticket for details. --- cronjobs/run-crons.sh | 2 +- cronjobs/statistics.php | 41 +++++++++++++++++++++ public/include/classes/statistics.class.php | 23 ++++++++---- 3 files changed, 58 insertions(+), 8 deletions(-) create mode 100755 cronjobs/statistics.php diff --git a/cronjobs/run-crons.sh b/cronjobs/run-crons.sh index 5f9179dc..f5cbaee7 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 blockupdate.php auto_payout.php tickerupdate.php notifications.php" +CRONS="findblock.php proportional_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php statistics.php" # Additional arguments to pass to cronjobs CRONARGS="-v" diff --git a/cronjobs/statistics.php b/cronjobs/statistics.php new file mode 100755 index 00000000..03aad394 --- /dev/null +++ b/cronjobs/statistics.php @@ -0,0 +1,41 @@ +#!/usr/bin/php +setGetCache(false); + +// Since fetching from cache is disabled, overwrite our stats +if (!$statistics->getRoundShares()) + verbose("Unable to fetch and store current round shares\n"); +if (!$statistics->getTopContributors('shares')) + verbose("Unable to fetch and store top share contributors\n"); +if (!$statistics->getTopContributors('hashes')) + verbose("Unable to fetch and store top hashrate contributors\n"); +if (!$statistics->getCurrentHashrate()) + verbose("Unable to fetch and store pool hashrate\n"); +// Admin specific statistics, we cache the global query due to slowness +if (!$statistics->getAllUserStats('%')) + verbose("Unable to fetch and store admin panel full user list\n"); + +?> diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index 6f56453b..f9f2ea9a 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -13,6 +13,7 @@ if (!defined('SECURITY')) class Statistics { private $sError = ''; private $table = 'statistics_shares'; + private $getcache = true; public function __construct($debug, $mysqli, $config, $share, $user, $block, $memcache) { $this->debug = $debug; @@ -34,6 +35,14 @@ class Statistics { return $this->sError; } + // Disable fetching values from cache + public function setGetCache($set=false) { + $this->getcache = $set; + } + public function getGetCache() { + return $this->getcache; + } + private function checkStmt($bState) { if ($bState ===! true) { $this->debug->append("Failed to prepare statement: " . $this->mysqli->error); @@ -88,7 +97,7 @@ class Statistics { **/ public function getCurrentHashrate() { $this->debug->append("STA " . __METHOD__, 4); - if ($data = $this->memcache->get(__FUNCTION__)) return $data; + if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__)) return $data; $stmt = $this->mysqli->prepare(" SELECT ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ")/600/1000) AS hashrate FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) "); @@ -122,7 +131,7 @@ class Statistics { **/ public function getRoundShares() { $this->debug->append("STA " . __METHOD__, 4); - if ($data = $this->memcache->get(__FUNCTION__)) return $data; + if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__)) return $data; $stmt = $this->mysqli->prepare(" SELECT ( SELECT IFNULL(count(id), 0) @@ -147,7 +156,7 @@ class Statistics { **/ public function getUserShares($account_id) { $this->debug->append("STA " . __METHOD__, 4); - if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data; + if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data; $stmt = $this->mysqli->prepare(" SELECT ( @@ -181,7 +190,7 @@ class Statistics { **/ public function getAllUserStats($filter='%') { $this->debug->append("STA " . __METHOD__, 4); - if ($data = $this->memcache->get(__FUNCTION__ . $filter)) return $data; + if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $filter)) return $data; $stmt = $this->mysqli->prepare(" SELECT a.id AS id, @@ -255,7 +264,7 @@ class Statistics { **/ public function getTopContributors($type='shares', $limit=15) { $this->debug->append("STA " . __METHOD__, 4); - if ($data = $this->memcache->get(__FUNCTION__ . $type . $limit)) return $data; + if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $type . $limit)) return $data; switch ($type) { case 'shares': $stmt = $this->mysqli->prepare(" @@ -329,7 +338,7 @@ class Statistics { **/ public function getHourlyHashrateByPool() { $this->debug->append("STA " . __METHOD__, 4); - if ($data = $this->memcache->get(__FUNCTION__)) return $data; + if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__)) return $data; $stmt = $this->mysqli->prepare(" SELECT ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000) AS hashrate, @@ -343,7 +352,7 @@ class Statistics { while ($row = $result->fetch_assoc()) { $aData[$row['hour']] = $row['hashrate']; } - return $this->memcache->setCache(__FUNCTION__, $aData); + return $this->memcache->setCache(__FUNCTION__, @$aData); } // Catchall $this->debug->append("Failed to fetch hourly hashrate: " . $this->mysqli->error); From 4f3e1e9ccb8e1fb4f297462e8af2d6836e0b1529 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 17 Jun 2013 10:40:36 +0200 Subject: [PATCH 152/168] Adding support for API currencies Since some altcoins are not listed with USD yet @vias79 suggested adding different API target currencies. This commit will add the feature and closes #201. --- public/include/config/global.inc.dist.php | 3 ++- public/include/smarty_globals.inc.php | 1 + public/templates/mmcFE/global/header.tpl | 2 +- .../templates/mmcFE/statistics/pool/contributors_hashrate.tpl | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index b41d9110..29a46658 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -23,7 +23,8 @@ define('SALT', 'PLEASEMAKEMESOMETHINGRANDOM'); $config = array( 'price' => array( 'url' => 'https://btc-e.com/api/2', - 'target' => '/ltc_usd/ticker' + 'target' => '/ltc_usd/ticker', + 'currency' => 'USD' // Used in ministats template ), 'ap_threshold' => array( 'min' => 1, diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index c8231a49..d55318d8 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -39,6 +39,7 @@ $aGlobal = array( 'blockexplorer' => $config['blockexplorer'], 'chaininfo' => $config['chaininfo'], 'config' => array( + 'price' => array( 'currency' => $config['price']['currency'] ), 'targetdiff' => $config['difficulty'], 'currency' => $config['currency'], 'txfee' => $config['txfee'], diff --git a/public/templates/mmcFE/global/header.tpl b/public/templates/mmcFE/global/header.tpl index a4b23f4b..fd625758 100644 --- a/public/templates/mmcFE/global/header.tpl +++ b/public/templates/mmcFE/global/header.tpl @@ -4,7 +4,7 @@
                              - + diff --git a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl index 2392af09..222902c7 100644 --- a/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl +++ b/public/templates/mmcFE/statistics/pool/contributors_hashrate.tpl @@ -7,7 +7,7 @@ - + From c93b6068a3c150a1dbafca55696f42bc80606b20 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 17 Jun 2013 10:45:34 +0200 Subject: [PATCH 153/168] Update README.md Added feature: support for various scrypt based coins. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index ae08e1fc..817aab98 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,10 @@ The following feature have been implemented so far: * New blocks found in pool * Auto Payout * Manual Payout +* Support for various Scrypt based coins via config + * MNC + * LTC + * ... Installation ============ From fa7f61c436b420f68e41f5854ce540b4d9b61c77 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 17 Jun 2013 11:17:15 +0200 Subject: [PATCH 154/168] Adding transaction fees to transaction class This will add back the transaction fees. Prior to this commit the pool had to cover the transaction fees. Now for each transaction the full balance is transferred (RPC Daemon will remove the TX Fee) but two transactions are added. One for the Debig and one TXFee. Fixes #203. **Requires database upgrade with supplied SQL file** --- cronjobs/auto_payout.php | 2 +- public/include/classes/transaction.class.php | 6 +++--- public/include/pages/account/edit.inc.php | 4 ++-- public/templates/mmcFE/account/transactions/default.tpl | 1 + sql/issue_203_transactions_upgrade.sql | 1 + 5 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 sql/issue_203_transactions_upgrade.sql diff --git a/cronjobs/auto_payout.php b/cronjobs/auto_payout.php index 366298e1..6bf36dd4 100755 --- a/cronjobs/auto_payout.php +++ b/cronjobs/auto_payout.php @@ -61,7 +61,7 @@ if (! empty($users)) { } // Create transaction record - if ($transaction->addTransaction($aUserData['id'], $dBalance, 'Debit_AP', NULL, $aUserData['coin_address'])) { + if ($transaction->addTransaction($aUserData['id'], $dBalance - $config['txfee'], 'Debit_AP', NULL, $aUserData['coin_address']) && $transaction->addTransaction($aUserData['id'], $config['txfee'], 'TXFee', NULL, $aUserData['coin_address'])) { // Notify user via mail $aMailData['email'] = $user->getUserEmail($user->getUserName($aUserData['id'])); $aMailData['subject'] = 'Auto Payout Completed'; diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index 3609de73..a6acd15c 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -147,7 +147,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 >= ? ) OR - t.type IN ('Donation_PPS','Fee_PPS') + t.type IN ('Donation_PPS','Fee_PPS','TXFee') ) ) AS t3"); if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $this->config['confirmations'], $this->config['confirmations']) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch()) @@ -194,7 +194,7 @@ class Transaction { WHERE ( ( t.type IN ('Donation','Fee') AND b.confirmations >= ? ) OR - ( t.type IN ('Donation_PPS', 'Fee_PPS') ) + ( t.type IN ('Donation_PPS', 'Fee_PPS', 'TXFee') ) ) AND t.account_id = ? ) AS t3, @@ -216,7 +216,7 @@ class Transaction { WHERE ( ( t.type IN ('Donation','Fee') AND b.confirmations < ? ) OR - ( t.type IN ('Donation_PPS', 'Fee_PPS') ) + ( t.type IN ('Donation_PPS', 'Fee_PPS', 'TXFee') ) ) AND t.account_id = ? ) AS t5 diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php index 92b8f1e1..2610f8f6 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -29,7 +29,7 @@ if ($user->isAuthenticated()) { } if ($continue == true) { // Send balance to address, mind fee for transaction! - try { + try { if ($setting->getValue('auto_payout_active') == 0) { $bitcoin->sendtoaddress($sCoinAddress, $dBalance); } else { @@ -42,7 +42,7 @@ if ($user->isAuthenticated()) { } } // Set balance to 0, add to paid out, insert to ledger - if ($continue == true && $transaction->addTransaction($_SESSION['USERDATA']['id'], $dBalance, 'Debit_MP', NULL, $sCoinAddress)) { + if ($continue == true && $transaction->addTransaction($_SESSION['USERDATA']['id'], $dBalance - $config['txfee'], 'Debit_MP', NULL, $sCoinAddress) && $transaction->addTransaction($_SESSION['USERDATA']['id'], $config['txfee'], 'TXFee', NULL, $sCoinAddress) ) { $_SESSION['POPUP'][] = array('CONTENT' => 'Transaction completed', 'TYPE' => 'success'); $aMailData['email'] = $user->getUserEmail($user->getUserName($_SESSION['USERDATA']['id'])); $aMailData['amount'] = $dBalance; diff --git a/public/templates/mmcFE/account/transactions/default.tpl b/public/templates/mmcFE/account/transactions/default.tpl index d2393450..c76e9fae 100644 --- a/public/templates/mmcFE/account/transactions/default.tpl +++ b/public/templates/mmcFE/account/transactions/default.tpl @@ -24,6 +24,7 @@ or $TRANSACTIONS[transaction].type == 'Donation_PPS' or $TRANSACTIONS[transaction].type == 'Debit_AP' or $TRANSACTIONS[transaction].type == 'Debit_MP' + or $TRANSACTIONS[transaction].type == 'TXFee' )} diff --git a/sql/issue_203_transactions_upgrade.sql b/sql/issue_203_transactions_upgrade.sql new file mode 100644 index 00000000..9c824b89 --- /dev/null +++ b/sql/issue_203_transactions_upgrade.sql @@ -0,0 +1 @@ +ALTER TABLE `transactions` CHANGE `type` `type` ENUM( 'Credit', 'Debit_MP', 'Debit_AP', 'Donation', 'Fee', 'Orphan_Credit', 'Orphan_Fee', 'Orphan_Donation', 'Credit_PPS', 'Fee_PPS', 'Donation_PPS', 'TXFee' ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ; From 9bde3d979cf15b638590b726d99b4f358539e337 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 17 Jun 2013 11:19:05 +0200 Subject: [PATCH 155/168] removed whitespace --- 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 2610f8f6..5b717cee 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -29,7 +29,7 @@ if ($user->isAuthenticated()) { } if ($continue == true) { // Send balance to address, mind fee for transaction! - try { + try { if ($setting->getValue('auto_payout_active') == 0) { $bitcoin->sendtoaddress($sCoinAddress, $dBalance); } else { From fe0beb16170daa44334cb730a68035dcd7b787ea Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 17 Jun 2013 11:25:54 +0200 Subject: [PATCH 156/168] changed output in auto_payout --- cronjobs/auto_payout.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cronjobs/auto_payout.php b/cronjobs/auto_payout.php index 6bf36dd4..7e5609fc 100755 --- a/cronjobs/auto_payout.php +++ b/cronjobs/auto_payout.php @@ -76,7 +76,7 @@ if (! empty($users)) { } } else { - verbose("INSUFF_TXFEE\n"); + verbose("SKIPPED\n"); } } } else { From 647b522b00b1f7ae17f23ab9c21a1aebe1f7c719 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 17 Jun 2013 15:00:56 +0200 Subject: [PATCH 157/168] Adding percentage colulmn to block template This shows the % of shares found for a block compared to the expected value. --- public/templates/mmcFE/statistics/blocks/default.tpl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/templates/mmcFE/statistics/blocks/default.tpl b/public/templates/mmcFE/statistics/blocks/default.tpl index cde7dcb3..3d7afdde 100644 --- a/public/templates/mmcFE/statistics/blocks/default.tpl +++ b/public/templates/mmcFE/statistics/blocks/default.tpl @@ -42,6 +42,7 @@ target and network difficulty and assuming a zero variance scenario. + @@ -60,6 +61,7 @@ target and network difficulty and assuming a zero variance scenario. + {/section} From 905898700ae3bb5447ec3b8c9c0862316b6b48b2 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 17 Jun 2013 16:35:27 +0200 Subject: [PATCH 158/168] adding per-user share statistics to cron --- cronjobs/statistics.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cronjobs/statistics.php b/cronjobs/statistics.php index 03aad394..d4fe65e0 100755 --- a/cronjobs/statistics.php +++ b/cronjobs/statistics.php @@ -38,4 +38,12 @@ if (!$statistics->getCurrentHashrate()) if (!$statistics->getAllUserStats('%')) verbose("Unable to fetch and store admin panel full user list\n"); +// Per user share statistics based on all shares submitted +$stmt = $mysqli->prepare("SELECT DISTINCT SUBSTRING_INDEX( `username` , '.', 1 ) AS username FROM " . $share->getTableName()); +if ($stmt && $stmt->execute() && $result = $stmt->get_result()) { + while ($row = $result->fetch_assoc()) { + if (!$statistics->getUserShares($user->getUserId($row['username']))) + verbose("Failed to fetch and store user stats for " . $row['username'] . "\n"); + } +} ?> From b532bbca51b5a20e6f6c7cd6283d8b3d83e77dc4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 17 Jun 2013 16:42:17 +0200 Subject: [PATCH 159/168] Do not mark PPS transactions as unconfirmed Fixes #206 --- public/include/classes/transaction.class.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index a6acd15c..38e1b429 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -203,10 +203,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 < ? ) OR - ( t.type = 'Credit_PPS' ) - ) + t.type IN ('Credit','Bonus') AND b.confirmations < ? AND t.account_id = ? ) AS t4, ( @@ -215,8 +212,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 < ? ) OR - ( t.type IN ('Donation_PPS', 'Fee_PPS', 'TXFee') ) + t.type IN ('Donation','Fee') AND b.confirmations < ? ) AND t.account_id = ? ) AS t5 From d3fa335e2c730254375f71fce6088a755dda0ecd Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 14 Jun 2013 10:14:13 +0200 Subject: [PATCH 160/168] Ensure we are able to fetch the upstream share * If a block was found but the upstream share is not inserted, bail out * Next run should find the upstream share if it was added properly Should address #189 but maybe not a fix yet. --- cronjobs/findblock.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cronjobs/findblock.php b/cronjobs/findblock.php index 0538d94c..dae9b432 100755 --- a/cronjobs/findblock.php +++ b/cronjobs/findblock.php @@ -82,9 +82,9 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $iCurrentUpstreamId = $share->getUpstreamId(); $iAccountId = $user->getUserId($share->getUpstreamFinder()); } else { - verbose("Unable to fetch blocks upstream share\n"); + verbose("Unable to fetch blocks upstream share. Aborting!\n"); verbose($share->getError() . "\n"); - continue; + exit; } // Fetch share information if (!$iPreviousShareId = $block->getLastShareId()) { From 34509051e29213e27398505342258bdbd5602790 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 18 Jun 2013 09:29:25 +0200 Subject: [PATCH 161/168] Properly detect if we did find an upstream share Avoid returning true even if no share was found. Addresses #189 --- cronjobs/findblock.php | 107 ++++++++++++------------- public/include/classes/share.class.php | 3 +- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/cronjobs/findblock.php b/cronjobs/findblock.php index dae9b432..cb1e7dba 100755 --- a/cronjobs/findblock.php +++ b/cronjobs/findblock.php @@ -39,9 +39,8 @@ if ( $bitcoin->can_connect() === true ){ // Nothing to do so bail out if (empty($aTransactions['transactions'])) { - verbose("No new transactions since last block\n"); + verbose("No new RPC transactions since last block\n"); } else { - // Table header verbose("Blockhash\t\tHeight\tAmount\tConfirmations\tDiff\t\tTime\t\t\tStatus\n"); @@ -66,65 +65,65 @@ if (empty($aTransactions['transactions'])) { } } -verbose("\n\n"); +verbose("\n"); // Now with our blocks added we can scan for their upstream shares $aAllBlocks = $block->getAllUnaccounted('ASC'); if (empty($aAllBlocks)) { verbose("No new unaccounted blocks found\n"); -} +} else { + // Loop through our unaccounted blocks + verbose("\nBlock ID\tBlock Height\tShare ID\tShares\tFinder\t\t\tStatus\n"); + foreach ($aAllBlocks as $iIndex => $aBlock) { + if (empty($aBlock['share_id'])) { + // Fetch this blocks upstream ID + if ($share->setUpstream($block->getLastUpstreamId())) { + $iCurrentUpstreamId = $share->getUpstreamId(); + $iAccountId = $user->getUserId($share->getUpstreamFinder()); + } else { + verbose("\nUnable to fetch blocks upstream share. Aborting!\n"); + verbose($share->getError() . "\n"); + 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"); + } + $iRoundShares = $share->getRoundShares($iPreviousShareId, $iCurrentUpstreamId); -// Loop through our unaccounted blocks -verbose("Block ID\tBlock Height\tShare ID\tShares\tFinder\t\t\tStatus\n"); -foreach ($aAllBlocks as $iIndex => $aBlock) { - if (empty($aBlock['share_id'])) { - // Fetch this blocks upstream ID - if ($share->setUpstream($block->getLastUpstreamId())) { - $iCurrentUpstreamId = $share->getUpstreamId(); - $iAccountId = $user->getUserId($share->getUpstreamFinder()); - } else { - verbose("Unable to fetch blocks upstream share. Aborting!\n"); - verbose($share->getError() . "\n"); - 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"); - } - $iRoundShares = $share->getRoundShares($iPreviousShareId, $iCurrentUpstreamId); + // Store new information + $strStatus = "OK"; + if (!$block->setShareId($aBlock['id'], $iCurrentUpstreamId)) + $strStatus = "Share ID Failed"; + if (!$block->setFinder($aBlock['id'], $iAccountId)) + $strStatus = "Finder Failed"; + if (!$block->setShares($aBlock['id'], $iRoundShares)) + $strStatus = "Shares Failed"; + if ($config['block_bonus'] > 0 && !$transaction->addTransaction($iAccountId, $config['block_bonus'], 'Bonus', $aBlock['id'])) { + $strStatus = "Bonus Failed"; + } - // Store new information - $strStatus = "OK"; - if (!$block->setShareId($aBlock['id'], $iCurrentUpstreamId)) - $strStatus = "Share ID Failed"; - if (!$block->setFinder($aBlock['id'], $iAccountId)) - $strStatus = "Finder Failed"; - if (!$block->setShares($aBlock['id'], $iRoundShares)) - $strStatus = "Shares Failed"; - if ($config['block_bonus'] > 0 && !$transaction->addTransaction($iAccountId, $config['block_bonus'], 'Bonus', $aBlock['id'])) { - $strStatus = "Bonus Failed"; - } + verbose( + $aBlock['id'] . "\t\t" + . $aBlock['height'] . "\t\t" + . $iCurrentUpstreamId . "\t\t" + . $iRoundShares . "\t" + . "[$iAccountId] " . $user->getUserName($iAccountId) . "\t\t" + . $strStatus + . "\n" + ); - verbose( - $aBlock['id'] . "\t\t" - . $aBlock['height'] . "\t\t" - . $iCurrentUpstreamId . "\t\t" - . $iRoundShares . "\t" - . "[$iAccountId] " . $user->getUserName($iAccountId) . "\t\t" - . $strStatus - . "\n" - ); - - // Notify users - $aAccounts = $notification->getNotificationAccountIdByType('new_block'); - if (is_array($aAccounts)) { - foreach ($aAccounts as $account_id) { - $aMailData['height'] = $aBlock['height']; - $aMailData['subject'] = 'New Block'; - $aMailData['email'] = $user->getUserEmail($user->getUserName($account_id)); - $aMailData['shares'] = $iRoundShares; - $notification->sendNotification($account_id, 'new_block', $aMailData); + // Notify users + $aAccounts = $notification->getNotificationAccountIdByType('new_block'); + if (is_array($aAccounts)) { + foreach ($aAccounts as $account_id) { + $aMailData['height'] = $aBlock['height']; + $aMailData['subject'] = 'New Block'; + $aMailData['email'] = $user->getUserEmail($user->getUserName($account_id)); + $aMailData['shares'] = $iRoundShares; + $notification->sendNotification($account_id, 'new_block', $aMailData); + } } } } diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index 1033f92c..85935a2d 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -187,7 +187,8 @@ class Share { ORDER BY id ASC LIMIT 1"); if ($this->checkStmt($stmt) && $stmt->bind_param('i', $last) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); - return true; + if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) + return true; } // Catchall return false; From 3f0c3884aa35563eaca220881b0ee6403905946a Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 14 Jun 2013 15:20:30 +0200 Subject: [PATCH 162/168] Adding some more information for PPS to sidebar * Added user share rate to sidebar for PPS * Added estimated 24h LTC payout based on PPS value and share rate Addresses #160 --- public/include/classes/statistics.class.php | 25 ++++++++++++++++++++- public/include/smarty_globals.inc.php | 1 + public/templates/mmcFE/global/sidebar.tpl | 10 ++++++++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index f9f2ea9a..a30aba1b 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -199,7 +199,7 @@ class Statistics { a.username AS username, a.donate_percent AS donate_percent, a.email AS email, - COUNT(s.id) AS shares + COUNT(s.id) AS shares FROM " . $this->user->getTableName() . " AS a LEFT JOIN " . $this->share->getTableName() . " AS s ON a.username = SUBSTRING_INDEX( s.username, '.', 1 ) @@ -219,6 +219,7 @@ class Statistics { * @return data integer Current Hashrate in khash/s **/ public function getUserHashrate($account_id) { + $this->debug->append("STA " . __METHOD__, 4); if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data; $stmt = $this->mysqli->prepare(" SELECT ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ")/600/1000) AS hashrate @@ -234,6 +235,28 @@ class Statistics { return false; } + /** + * Same as getUserHashrate for Sharerate + * @param account_id integer User ID + * @return data integer Current Sharerate in shares/s + **/ + public function getUserSharerate($account_id) { + $this->debug->append("STA " . __METHOD__, 4); + if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data; + $stmt = $this->mysqli->prepare(" + SELECT COUNT(s.id)/600 AS sharerate + FROM " . $this->share->getTableName() . " AS s, + " . $this->user->getTableName() . " AS u + WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 ) + AND s.time > DATE_SUB(now(), INTERVAL 10 MINUTE) + AND u.id = ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result() ) + return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->sharerate); + // Catchall + $this->debug->append("Failed to fetch sharerate: " . $this->mysqli->error); + return false; + } + /** * Get hashrate for a specific worker * @param worker_id int Worker ID to fetch hashrate for diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index d55318d8..2d33a173 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -59,6 +59,7 @@ if (@$_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']); // Some estimations $aGlobal['userdata']['est_block'] = round(( (int)$aGlobal['userdata']['shares']['valid'] / (int)$aRoundShares['valid'] ) * (int)$config['reward'], 3); diff --git a/public/templates/mmcFE/global/sidebar.tpl b/public/templates/mmcFE/global/sidebar.tpl index 34018785..450be70d 100644 --- a/public/templates/mmcFE/global/sidebar.tpl +++ b/public/templates/mmcFE/global/sidebar.tpl @@ -14,9 +14,17 @@ {if $GLOBAL.config.payout_system == 'pps'} + + + + - + + + + + {/if} {if $GLOBAL.config.payout_system != 'pps'} From 82f696164524c7d2813e2c3409ae7b04307e2164 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 18 Jun 2013 10:22:43 +0200 Subject: [PATCH 163/168] Adding PPS specific sidebar * Adding PPS sidebar to seperate the HTML code * Added PPS specific globals section if need be some day * Added payout detection to master template for sidebar * Added 7 days and 14 days estimates Fixes #160 --- public/include/smarty_globals.inc.php | 16 +++-- public/templates/mmcFE/global/sidebar.tpl | 18 ------ public/templates/mmcFE/global/sidebar_pps.tpl | 61 +++++++++++++++++++ public/templates/mmcFE/master.tpl | 6 +- 4 files changed, 77 insertions(+), 24 deletions(-) create mode 100644 public/templates/mmcFE/global/sidebar_pps.tpl diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index 2d33a173..923b7263 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -61,11 +61,17 @@ if (@$_SESSION['USERDATA']['id']) { $aGlobal['userdata']['hashrate'] = $statistics->getUserHashrate($_SESSION['USERDATA']['id']); $aGlobal['userdata']['sharerate'] = $statistics->getUserSharerate($_SESSION['USERDATA']['id']); - // Some estimations - $aGlobal['userdata']['est_block'] = round(( (int)$aGlobal['userdata']['shares']['valid'] / (int)$aRoundShares['valid'] ) * (int)$config['reward'], 3); - $aGlobal['userdata']['est_fee'] = round(($config['fees'] / 100) * $aGlobal['userdata']['est_block'], 3); - $aGlobal['userdata']['est_donation'] = round((( $aGlobal['userdata']['donate_percent'] / 100) * ($aGlobal['userdata']['est_block'] - $aGlobal['userdata']['est_fee'])), 3); - $aGlobal['userdata']['est_payout'] = round($aGlobal['userdata']['est_block'] - $aGlobal['userdata']['est_donation'] - $aGlobal['userdata']['est_fee'], 3); + switch ($config['payout_system']) { + case 'pps': + break; + default: + // Some estimations + $aGlobal['userdata']['est_block'] = round(( (int)$aGlobal['userdata']['shares']['valid'] / (int)$aRoundShares['valid'] ) * (int)$config['reward'], 3); + $aGlobal['userdata']['est_fee'] = round(($config['fees'] / 100) * $aGlobal['userdata']['est_block'], 3); + $aGlobal['userdata']['est_donation'] = round((( $aGlobal['userdata']['donate_percent'] / 100) * ($aGlobal['userdata']['est_block'] - $aGlobal['userdata']['est_fee'])), 3); + $aGlobal['userdata']['est_payout'] = round($aGlobal['userdata']['est_block'] - $aGlobal['userdata']['est_donation'] - $aGlobal['userdata']['est_fee'], 3); + break; + } } // Make it available in Smarty diff --git a/public/templates/mmcFE/global/sidebar.tpl b/public/templates/mmcFE/global/sidebar.tpl index 450be70d..58d49c67 100644 --- a/public/templates/mmcFE/global/sidebar.tpl +++ b/public/templates/mmcFE/global/sidebar.tpl @@ -13,21 +13,6 @@ -{if $GLOBAL.config.payout_system == 'pps'} - - - - - - - - - - - - -{/if} -{if $GLOBAL.config.payout_system != 'pps'} @@ -39,7 +24,6 @@ -{/if} @@ -55,7 +39,6 @@ -{if $GLOBAL.config.payout_system != 'pps'} @@ -75,7 +58,6 @@ -{/if} diff --git a/public/templates/mmcFE/global/sidebar_pps.tpl b/public/templates/mmcFE/global/sidebar_pps.tpl new file mode 100644 index 00000000..b7aa8617 --- /dev/null +++ b/public/templates/mmcFE/global/sidebar_pps.tpl @@ -0,0 +1,61 @@ +
                              +
                              +
                              +
                              +

                              Dashboard

                              +
                              +
                              +
                            • {$GLOBAL.config.currency}/usd: {$GLOBAL.price|default:"n/a"|number_format:"4"}    
                            • {$GLOBAL.config.currency}/{$GLOBAL.config.price.currency}: {$GLOBAL.price|default:"n/a"|number_format:"4"}    
                            • Pool Hashrate: {($GLOBAL.hashrate / 1000)|number_format:"3"} MH/s    
                            • Pool Sharerate: {$GLOBAL.sharerate|number_format:"2"} Shares/s    
                            • Pool Workers: {$GLOBAL.workers}    
                            • User Name KH/s {$GLOBAL.config.currency}/DayUSD/Day{$GLOBAL.config.price.currency}/Day
                              {$TRANSACTIONS[transaction].id}Difficulty Expected Shares Actual SharesPercentage
                              {$BLOCKSFOUND[block].difficulty|number_format:"2"} {(pow(2,32 - $GLOBAL.config.targetdiff) * $BLOCKSFOUND[block].difficulty)|number_format} {$BLOCKSFOUND[block].shares|number_format}{($BLOCKSFOUND[block].shares / (pow(2,32 - $GLOBAL.config.targetdiff) * $BLOCKSFOUND[block].difficulty) * 100)|number_format:"2"}
                              {$GLOBAL.userdata.hashrate|number_format} KH/s
                              Share Rate{$GLOBAL.userdata.sharerate|number_format:"2"} S/s
                              PPS Value{$GLOBAL.ppsvalue}{$GLOBAL.ppsvalue}
                              {$GLOBAL.config.currency} in 24h{($GLOBAL.userdata.sharerate * 24 * 60 * 60 * $GLOBAL.ppsvalue)|number_format:"8"}
                              Hashrate {$GLOBAL.userdata.hashrate|number_format} KH/s
                              Share Rate{$GLOBAL.userdata.sharerate|number_format:"2"} S/s
                              PPS Value{$GLOBAL.ppsvalue}
                              {$GLOBAL.config.currency} in 24h{($GLOBAL.userdata.sharerate * 24 * 60 * 60 * $GLOBAL.ppsvalue)|number_format:"8"}
                              Unpaid Shares
                              Pool Valid {$GLOBAL.roundshares.valid|number_format}
                              Round Shares
                              Your Invalid {$GLOBAL.userdata.shares.invalid|number_format}
                              {$GLOBAL.config.currency} Round Estimate
                              Payout {$GLOBAL.userdata.est_payout|number_format:"3"}
                               
                              {$GLOBAL.config.currency} Account Balance
                              Confirmed{$GLOBAL.userdata.balance.confirmed|default:"0"}
                              + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                              +
                              +
                              + diff --git a/public/templates/mmcFE/master.tpl b/public/templates/mmcFE/master.tpl index 4c3204dc..8709e974 100644 --- a/public/templates/mmcFE/master.tpl +++ b/public/templates/mmcFE/master.tpl @@ -47,7 +47,11 @@