Skip to content

Commit 105d772

Browse files
committed
[MERGE] Development
2 parents a7a731d + f58db4f commit 105d772

File tree

20 files changed

+258
-231
lines changed

20 files changed

+258
-231
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
/include/config/global.inc.scrypt.php
1919
/include/config/global.inc.sha.php
2020

21+
# Test files
22+
/scripts/test.php
23+
/cronjobs/test.php
24+
2125
# IDE Settings
2226
/.idea/*
2327
.buildpath

cronjobs/statistics.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
// Header
2929
$log->logInfo('Running statistical queries, errors may just mean no shares were available');
30-
$strLogMask = "| %-26.26s | %8.8s | %-6.6s |";
30+
$strLogMask = "| %-33.33s | %8.8s | %-6.6s |";
3131
$log->logInfo(sprintf($strLogMask, 'Method', 'Runtime', 'Status'));
3232

3333
// Per user share statistics based on all shares submitted
@@ -37,9 +37,15 @@
3737

3838
// Get all user hashrate statistics for caching
3939
$start = microtime(true);
40-
$statistics->getAllUserMiningStats() ? $status = 'OK' : $status = 'ERROR';
41-
$log->logInfo(sprintf($strLogMask, 'getAllUserMiningStats', number_format(microtime(true) - $start, 3), $status));
40+
$statistics->fetchAllUserMiningStats() ? $status = 'OK' : $status = 'ERROR';
41+
$log->logInfo(sprintf($strLogMask, 'fetchAllUserMiningStats', number_format(microtime(true) - $start, 3), $status));
4242

43+
// Store our statistical data into our `statistics_users` table
44+
$start = microtime(true);
45+
$statistics->storeAllUserMiningStatsSnapshot($statistics->getAllUserMiningStats()) ? $status = 'OK' : $status = 'ERROR';
46+
$log->logInfo(sprintf($strLogMask, 'storeAllUserMiningStatsSnapshot', number_format(microtime(true) - $start, 3), $status));
47+
48+
// Get stats for pool overview
4349
$start = microtime(true);
4450
$statistics->getTopContributors('hashes') ? $status = 'OK' : $status = 'ERROR';
4551
$log->logInfo(sprintf($strLogMask, 'getTopContributors(hashes)', number_format(microtime(true) - $start, 3), $status));

cronjobs/tables_cleanup.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,27 @@
6565
$message = '';
6666
$affected = $share->purgeArchive();
6767
if ($affected === false) {
68-
$message = 'Failed to delete notifications: ' . $oToken->getCronError();
68+
$message = 'Failed to delete shares: ' . $share->getCronError();
6969
$status = 'ERROR';
7070
$monitoring->endCronjob($cron_name, 'E0008', 0, false, false);
7171
} else {
7272
$affected == 0 ? $message = 'No shares deleted' : $message = 'Deleted old shares';
7373
}
7474
$log->logInfo(sprintf($strLogMask, 'purgeArchive', $affected, number_format(microtime(true) - $start, 3), $status, $message));
7575

76+
// Clenaup shares archive
77+
$start = microtime(true);
78+
$status = 'OK';
79+
$message = '';
80+
$affected = $statistics->purgeUserStats($setting->getValue('statistics_graphing_days', 1));
81+
if ($affected === false) {
82+
$message = 'Failed to delete entries: ' . $statistics->getCronError();
83+
$status = 'ERROR';
84+
$monitoring->endCronjob($cron_name, 'E0008', 0, false, false);
85+
} else {
86+
$affected == 0 ? $message = 'No entries deleted' : $message = 'Deleted old entries';
87+
}
88+
$log->logInfo(sprintf($strLogMask, 'purgeUserStats', $affected, number_format(microtime(true) - $start, 3), $status, $message));
7689

7790
// Cron cleanup and monitoring
7891
require_once('cron_end.inc.php');

include/classes/statistics.class.php

Lines changed: 75 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
**/
1010
class Statistics extends Base {
1111
protected $table = 'statistics_shares';
12+
protected $table_user_stats = 'statistics_users';
1213
private $getcache = true;
1314

1415
// Disable fetching values from cache
@@ -18,6 +19,12 @@ public function setGetCache($set=false) {
1819
public function getGetCache() {
1920
return $this->getcache;
2021
}
22+
public function getAllUserMiningStats() {
23+
return $this->allUserMiningStats;
24+
}
25+
public function getUserStatsTableName() {
26+
return $this->table_user_stats;
27+
}
2128

2229
/**
2330
* Get our first block found
@@ -104,7 +111,7 @@ public function getBlocksFound($limit=10) {
104111
b.*,
105112
a.username AS finder,
106113
a.is_anonymous AS is_anonymous,
107-
ROUND(difficulty * POW(2, 32 - " . $this->coin->getTargetBits() . "), 0), 4) AS estshares
114+
ROUND(difficulty * POW(2, 32 - " . $this->coin->getTargetBits() . "), 0) AS estshares
108115
FROM " . $this->block->getTableName() . " AS b
109116
LEFT JOIN " . $this->user->getTableName() . " AS a
110117
ON b.account_id = a.id
@@ -254,12 +261,12 @@ public function getCurrentShareRate($interval=180) {
254261
SELECT
255262
(
256263
(
257-
SELECT ROUND(COUNT(id) / ?, 2) AS sharerate
264+
SELECT ROUND(SUM(difficulty) / ?, 2) AS sharerate
258265
FROM " . $this->share->getTableName() . "
259266
WHERE time > DATE_SUB(now(), INTERVAL ? SECOND)
260267
AND our_result = 'Y'
261268
) + (
262-
SELECT ROUND(COUNT(id) / ?, 2) AS sharerate
269+
SELECT ROUND(SUM(difficulty) / ?, 2) AS sharerate
263270
FROM " . $this->share->getArchiveTableName() . "
264271
WHERE time > DATE_SUB(now(), INTERVAL ? SECOND)
265272
AND our_result = 'Y'
@@ -451,16 +458,19 @@ public function getAllUserStats($filter='%',$limit=1,$start=0) {
451458

452459
/**
453460
* Fetch all user hashrates based on shares and archived shares
461+
* Store it in cache, also keep a copy of the data internally to
462+
* return it for further processing
454463
* @return data array Set of all user stats
455464
**/
456-
public function getAllUserMiningStats($interval=180) {
465+
public function fetchAllUserMiningStats($interval=180) {
457466
$this->debug->append("STA " . __METHOD__, 4);
458467
$stmt = $this->mysqli->prepare("
459468
SELECT
460469
a.id AS id,
461470
a.username AS account,
471+
COUNT(DISTINCT t1.username) AS workers,
462472
IFNULL(SUM(t1.difficulty), 0) AS shares,
463-
ROUND(COUNT(t1.id) / ?, 2) AS sharerate,
473+
ROUND(SUM(t1.difficulty) / ?, 2) AS sharerate,
464474
IFNULL(AVG(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)), 0) AS avgsharediff
465475
FROM (
466476
SELECT
@@ -489,12 +499,45 @@ public function getAllUserMiningStats($interval=180) {
489499
$aData['data'][$row['id']] = $row;
490500
$aData['data'][$row['id']]['hashrate'] = $this->coin->calcHashrate($row['shares'], $interval);
491501
}
502+
$this->allUserMiningStats = $aData;
492503
return $this->memcache->setStaticCache(STATISTICS_ALL_USER_HASHRATES, $aData, 600);
493504
} else {
494505
return $this->sqlError();
495506
}
496507
}
497508

509+
/**
510+
* Store our gathered data into our statistic table for users
511+
* @param aData array Data created by fetchAllUserMiningStats
512+
* @return bool true or false
513+
**/
514+
public function storeAllUserMiningStatsSnapshot($aData) {
515+
$this->debug->append("STA " . __METHOD__, 4);
516+
if (!isset($aData['data'])) return false;
517+
// initilize
518+
$timestamp = time(); // Store all entries with the same timestamp to reduce cardinality
519+
$ok = 0;
520+
$failed = 0;
521+
foreach ($aData['data'] as $key => $aUserData) {
522+
$stmt = $this->mysqli->prepare("
523+
INSERT INTO " . $this->getUserStatsTableName() . "
524+
( account_id, hashrate, workers, sharerate, timestamp ) VALUES ( ?, ?, ?, ?, ?)");
525+
if ($this->checkStmt($stmt) && $stmt->bind_param("ididi", $aUserData['id'], $aUserData['hashrate'], $aUserData['workers'], $aUserData['sharerate'], $timestamp) && $stmt->execute() ) {
526+
$ok++;
527+
} else {
528+
$failed++;
529+
}
530+
}
531+
return array('ok' => $ok, 'failed' => $failed);
532+
}
533+
534+
/**
535+
* Fetch unpaid PPS shares for an account
536+
* @param username string Username
537+
* @param account_id int User ID
538+
* @param last_paid_pps_id int Last paid out share by pps_payout cron
539+
* @return data int Sum of unpaid diff1 shares
540+
**/
498541
public function getUserUnpaidPPSShares($username, $account_id=NULL, $last_paid_pps_id) {
499542
$this->debug->append("STA " . __METHOD__, 4);
500543
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
@@ -515,7 +558,7 @@ public function getUserUnpaidPPSShares($username, $account_id=NULL, $last_paid_p
515558
* Get Shares per x interval by user
516559
* @param username string username
517560
* @param $account_id int account id
518-
* @return data integer Current Sharerate in shares/s
561+
* @return data integer Current Sharerate in diff1 shares/s
519562
**/
520563
public function getUserMiningStats($username, $account_id=NULL, $interval=180) {
521564
$this->debug->append("STA " . __METHOD__, 4);
@@ -532,7 +575,7 @@ public function getUserMiningStats($username, $account_id=NULL, $interval=180) {
532575
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
533576
$stmt = $this->mysqli->prepare("
534577
SELECT
535-
IFNULL(COUNT(*) / ?, 0) AS sharerate,
578+
IFNULL(SUM(difficulty) / ?, 0) AS sharerate,
536579
IFNULL(SUM(difficulty), 0) AS shares,
537580
IFNULL(AVG(difficulty), 0) AS avgsharediff
538581
FROM (
@@ -655,78 +698,24 @@ public function getTopContributors($type='shares', $limit=15) {
655698
* @param $account_id int account id
656699
* @return data array NOT FINISHED YET
657700
**/
658-
public function getHourlyHashrateByAccount($username, $account_id=NULL) {
701+
public function getHourlyMiningStatsByAccount($account_id, $format='array', $days = 1) {
659702
$this->debug->append("STA " . __METHOD__, 4);
660703
if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
661704
$stmt = $this->mysqli->prepare("
662705
SELECT
663-
id,
664-
IFNULL(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)), 0) AS shares,
665-
HOUR(time) AS hour
666-
FROM " . $this->share->getTableName() . "
667-
WHERE time <= FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(NOW())/(60*60))*(60*60))
668-
AND time >= FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(NOW())/(60*60))*(60*60)) - INTERVAL 24 HOUR
669-
AND our_result = 'Y'
670-
AND username LIKE ?
671-
GROUP BY HOUR(time)
672-
UNION
673-
SELECT
674-
share_id,
675-
IFNULL(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)), 0) AS shares,
676-
HOUR(time) AS hour
677-
FROM " . $this->share->getArchiveTableName() . "
678-
WHERE time <= FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(NOW())/(60*60))*(60*60))
679-
AND time >= FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(NOW())/(60*60))*(60*60)) - INTERVAL 24 HOUR
680-
AND our_result = 'Y'
681-
AND username LIKE ?
682-
GROUP BY HOUR(time)");
683-
$username = $username . ".%";
684-
if ($this->checkStmt($stmt) && $stmt->bind_param('ss', $username, $username) && $stmt->execute() && $result = $stmt->get_result()) {
685-
$iStartHour = date('G');
686-
// Initilize array
687-
for ($i = 0; $i < 24; $i++) $aData[($iStartHour + $i) % 24] = 0;
688-
// Fill data
689-
while ($row = $result->fetch_assoc()) $aData[$row['hour']] += (int) $this->coin->calcHashrate($row['shares'], 3600);
690-
return $this->memcache->setCache(__FUNCTION__ . $account_id, $aData);
691-
}
692-
return $this->sqlError();
693-
}
694-
695-
/**
696-
* get Hourly hashrate for the pool
697-
* @param none
698-
* @return data array NOT FINISHED YET
699-
**/
700-
public function getHourlyHashrateByPool() {
701-
$this->debug->append("STA " . __METHOD__, 4);
702-
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__)) return $data;
703-
$stmt = $this->mysqli->prepare("
704-
SELECT
705-
id,
706-
IFNULL(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)), 0) AS shares,
707-
HOUR(s.time) AS hour
708-
FROM " . $this->share->getTableName() . " AS s
709-
WHERE time <= FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(NOW())/(60*60))*(60*60))
710-
AND time >= FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(NOW())/(60*60))*(60*60)) - INTERVAL 24 HOUR
711-
AND our_result = 'Y'
712-
GROUP BY HOUR(time)
713-
UNION
714-
SELECT
715-
share_id,
716-
IFNULL(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)), 0) AS shares,
717-
HOUR(s.time) AS hour
718-
FROM " . $this->share->getArchiveTableName() . " AS s
719-
WHERE time <= FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(NOW())/(60*60))*(60*60))
720-
AND time >= FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(NOW())/(60*60))*(60*60)) - INTERVAL 24 HOUR
721-
AND our_result = 'Y'
722-
GROUP BY HOUR(time)");
723-
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) {
724-
$iStartHour = date('G');
725-
// Initilize array
726-
for ($i = 0; $i < 24; $i++) $aData[($iStartHour + $i) % 24] = 0;
727-
// Fill data
728-
while ($row = $result->fetch_assoc()) $aData[$row['hour']] += (int) $this->coin->calcHashrate($row['shares'], 3600);
729-
return $this->memcache->setCache(__FUNCTION__, $aData);
706+
timestamp,
707+
FROM_UNIXTIME(timestamp, '%Y-%m-%d %H:%i') AS time,
708+
AVG(hashrate) AS hashrate,
709+
AVG(workers) AS workers,
710+
AVG(sharerate) AS sharerate
711+
FROM " . $this->getUserStatsTableName() . "
712+
WHERE FROM_UNIXTIME(timestamp) >= DATE_SUB(NOW(), INTERVAL $days DAY)
713+
AND account_id = ?
714+
GROUP BY DAY(FROM_UNIXTIME(timestamp)), HOUR(FROM_UNIXTIME(timestamp))");
715+
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result()) {
716+
$aData = $result->fetch_all(MYSQLI_ASSOC);
717+
if ($format == 'json') $aData = json_encode($aData);
718+
return $this->memcache->setCache(__FUNCTION__ . $account_id . $format, $aData);
730719
}
731720
return $this->sqlError();
732721
}
@@ -914,6 +903,17 @@ public function getCountAllActiveUsers($interval=120) {
914903
return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->total);
915904
return $this->sqlError();
916905
}
906+
907+
/**
908+
* Purge older entries from our statistics_users table
909+
**/
910+
public function purgeUserStats($days = 1) {
911+
// Fallbacks if unset
912+
$stmt = $this->mysqli->prepare("DELETE FROM " . $this->getUserStatsTableName() . " WHERE FROM_UNIXTIME(timestamp) <= DATE_SUB(NOW(), INTERVAL ? DAY)");
913+
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $days) && $stmt->execute())
914+
return $stmt->affected_rows;
915+
return $this->sqlError();
916+
}
917917
}
918918

919919
$statistics = new Statistics();

include/classes/user.class.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ public function updateAccount($userID, $address, $threshold, $donate, $email, $t
496496
$this->setErrorMessage('Donation above allowed 100% limit');
497497
return false;
498498
}
499-
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
499+
if ($email != 'hidden' && $email != NULL && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
500500
$this->setErrorMessage('Invalid email address');
501501
return false;
502502
}
@@ -540,6 +540,9 @@ public function updateAccount($userID, $address, $threshold, $donate, $email, $t
540540
}
541541
}
542542

543+
// If we hide our email or it's not set, fetch current one to update
544+
if ($email == 'hidden' || $email == NULL)
545+
$email = $this->getUserEmailById($userID);
543546
// We passed all validation checks so update the account
544547
$stmt = $this->mysqli->prepare("UPDATE $this->table SET ap_threshold = ?, donate_percent = ?, email = ?, timezone = ?, is_anonymous = ? WHERE id = ?");
545548
if ($this->checkStmt($stmt) && $stmt->bind_param('ddssii', $threshold, $donate, $email, $timezone, $is_anonymous, $userID) && $stmt->execute()) {

include/config/admin_settings.inc.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,13 @@
160160
'name' => 'statistics_ajax_data_interval', 'value' => $setting->getValue('statistics_ajax_data_interval'),
161161
'tooltip' => 'Time in minutes, interval for hashrate and sharerate calculations. Higher intervals allow for better accuracy at a higer server load.'
162162
);
163+
$aSettings['statistics'][] = array(
164+
'display' => 'Graphing Days', 'type' => 'text',
165+
'size' => 25,
166+
'default' => 1,
167+
'name' => 'statistics_graphing_days', 'value' => $setting->getValue('statistics_graphing_days'),
168+
'tooltip' => 'How many days to graph out on the statistics -> graphs page.'
169+
);
163170
$aSettings['statistics'][] = array(
164171
'display' => 'Block Statistics Count', 'type' => 'text',
165172
'size' => 25,

include/pages/api.inc.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
$api->isActive();
66

77
// Check for valid API key
8-
$id = $user->checkApiKey($_REQUEST['api_key']);
8+
$id = $user->checkApiKey(@$_REQUEST['api_key']);
99

1010
header('HTTP/1.1 400 Bad Request');
1111
die('400 Bad Request');

include/pages/dashboard.inc.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
$smarty->assign('BLOCKSFOUND', $aLastBlocks);
5454
$smarty->assign('DISABLED_DASHBOARD', $setting->getValue('disable_dashboard'));
5555
$smarty->assign('DISABLED_DASHBOARD_API', $setting->getValue('disable_dashboard_api'));
56+
$smarty->assign('DISABLED_API', $setting->getValue('disable_api'));
5657
$smarty->assign('ESTIMATES', array('shares' => $iEstShares, 'percent' => $dEstPercent));
5758
$smarty->assign('NETWORK', array('difficulty' => $dDifficulty, 'block' => $iBlock, 'EstNextDifficulty' => $dEstNextDifficulty, 'EstTimePerBlock' => $dExpectedTimePerBlock, 'BlocksUntilDiffChange' => $iBlocksUntilDiffChange));
5859
$smarty->assign('INTERVAL', $interval / 60);

include/pages/statistics/graphs.inc.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
55
$debug->append('No cached version available, fetching from backend', 3);
66
if ($user->isAuthenticated()) {
7-
$aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA']['username'], $_SESSION['USERDATA']['id']);
8-
$aPoolHourlyHashRates = $statistics->getHourlyHashrateByPool();
7+
$aHourlyMiningStats = $statistics->getHourlyMiningStatsByAccount($_SESSION['USERDATA']['id'], 'json', $setting->getValue('statistics_graphing_days', 1));
98
}
10-
$smarty->assign("YOURHASHRATES", @$aHourlyHashRates);
11-
$smarty->assign("POOLHASHRATES", @$aPoolHourlyHashRates);
9+
$smarty->assign('YOURMININGSTATS', @$aHourlyMiningStats);
1210
} else {
1311
$debug->append('Using cached page', 3);
1412
}

include/version.inc.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
$defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1;
33

44
define('MPOS_VERSION', '0.0.4');
5-
define('DB_VERSION', '0.0.13');
5+
define('DB_VERSION', '0.0.14');
66
define('CONFIG_VERSION', '0.0.8');
77
define('HASH_VERSION', 1);
88

0 commit comments

Comments
 (0)