Merge pull request #921 from TheSerapher/next

Next -> Master
This commit is contained in:
Sebastian Grewe 2013-12-08 00:54:26 -08:00
commit e3a310df34
563 changed files with 4413 additions and 6873 deletions

3
.gitignore vendored
View File

@ -3,3 +3,6 @@
/cronjobs/logs/*.txt /cronjobs/logs/*.txt
/cronjobs/logs/*.txt.*.gz /cronjobs/logs/*.txt.*.gz
/public/templates/cache/*.php /public/templates/cache/*.php
# Test configs
public/include/config/global.inc.scrypt.php
public/include/config/global.inc.sha.php

View File

@ -5,7 +5,7 @@ MPOS is a web based Mining Portal for various crypto currencies. A few pools usi
* http://ltc.pool.grewe.ca * http://ltc.pool.grewe.ca
* http://fst.pool.grewe.ca * http://fst.pool.grewe.ca
* http://nvc.pool.grewe.ca * http://ftc.pool.grewe.ca
**NOTE**: This project is still under development and commits are happening on a daily basis. **NOTE**: This project is still under development and commits are happening on a daily basis.
I do not recommend using this for a live setup as of yet. Wait for the later Release Candidate I do not recommend using this for a live setup as of yet. Wait for the later Release Candidate
@ -18,11 +18,7 @@ I was hoping to keep this out of the README but apparently people remove or chan
at the bottom of the page. For those of you finding my project and are willing to appreciate the work at the bottom of the page. For those of you finding my project and are willing to appreciate the work
with some hard earned coins feel free to donate: with some hard earned coins feel free to donate:
* BTC address: `1HuYK6WPU8o3yWCrAaADDZPRpL5QiXitfv`
* LTC address: `Lge95QR2frp9y1wJufjUPCycVsg5gLJPW8` * LTC address: `Lge95QR2frp9y1wJufjUPCycVsg5gLJPW8`
* FTC address: `6jDgGaUzMVyac5uqBhJCMiFMKCtH1LagTA`
* NVC address: `4Guct6z7NVPVALHRAVn517TTmvqQve4WYr`
* FST address: `g17CfFHqNqR5JnUjtG8RNBYh2WrhEirV67`
Website Footer Website Footer
============== ==============
@ -50,8 +46,6 @@ Requirements
This setup has been tested on Ubuntu 12.04, Ubuntu 13.04 and CentOS. This setup has been tested on Ubuntu 12.04, Ubuntu 13.04 and CentOS.
It should also work on any related distribution (RHEL, Debian). It should also work on any related distribution (RHEL, Debian).
For support on how to get `litecoind` or `pushpoold` to work, please ask
in the appropriate forums.
Be aware that `MPOS` is **only** for pooled mining. Solo Mining is not Be aware that `MPOS` is **only** for pooled mining. Solo Mining is not
supported. They will never match an upstream share, solo miners do not create supported. They will never match an upstream share, solo miners do not create
@ -77,16 +71,16 @@ The following feature have been implemented so far:
* Fully re-written GUI with [Smarty][2] templates * Fully re-written GUI with [Smarty][2] templates
* Mobile WebUI * Mobile WebUI
* **NEW** VARDIFF Support * Scrypt, **NEW** SHA256, VARDIFF Support
* Reward Systems * Reward Systems
* Propotional * Propotional
* PPS * PPS
* PPLNS * PPLNS
* Statistics are cached in Memcache by Cronjob for quick data access * Statistics are cached in Memcache by Cronjob for quick data access
* **NEW** New Theme * New Theme
* **NEW** Live Dashboard * Live Dashboard
* **NEW** AJAX Support * AJAX Support
* **NEW** Overhauled API * Overhauled API
* Web User accounts * Web User accounts
* Re-Captcha protected registration form * Re-Captcha protected registration form
* Worker accounts * Worker accounts
@ -143,6 +137,15 @@ Other customizations are also possible but will require merging changes together
on non-existing features in `MPOS`. For the vast majority, adjusting themes should be enough to highlight your pool from others. on non-existing features in `MPOS`. For the vast majority, adjusting themes should be enough to highlight your pool from others.
In all that, I humbly ask to keep the `MPOS` author reference and Github URL intact. In all that, I humbly ask to keep the `MPOS` author reference and Github URL intact.
Related Software
================
There are a few other projects out there that take advantage of MPOS and it's included API. Here a quick list that you can check out for yourself:
* [MPOS IRC Bot](https://github.com/WKNiGHT-/mpos-bot) written in Python, standlone bot, using the MPOS API
* [MPOS Eggdrop Module](https://github.com/iAmShorty/mpos-eggdrop-tcl) written in TCL, adding MPOS commands to this bot, using the MPOS API
Contributing Contributing
============ ============

View File

@ -28,10 +28,7 @@ require_once('shared.inc.php');
// If we don't keep archives, delete some now to release disk space // If we don't keep archives, delete some now to release disk space
if (!$share->purgeArchive()) { if (!$share->purgeArchive()) {
$log->logError("Failed to delete archived shares, not critical but should be checked!"); $log->logError("Failed to delete archived shares, not critical but should be checked!");
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0008', 1, true);
$monitoring->setStatus($cron_name . "_message", "message", "Failed to delete archived shares");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
} }
// Cron cleanup and monitoring // Cron cleanup and monitoring

View File

@ -27,19 +27,12 @@ require_once('shared.inc.php');
if ($setting->getValue('disable_ap') == 1) { if ($setting->getValue('disable_ap') == 1) {
$log->logInfo(" auto payout disabled via admin panel"); $log->logInfo(" auto payout disabled via admin panel");
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0009', 0, true);
$monitoring->setStatus($cron_name . "_message", "message", "Auto-Payout disabled");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
$monitoring->setStatus($cron_name . "_endtime", "date", time());
exit(0);
} }
if ($bitcoin->can_connect() !== true) { if ($bitcoin->can_connect() !== true) {
$log->logFatal(" unable to connect to RPC server, exiting"); $log->logFatal(" unable to connect to RPC server, exiting");
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0006', 1, true);
$monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
} }
// Fetch all users with setup AP // Fetch all users with setup AP
@ -73,14 +66,14 @@ if (! empty($users)) {
// Send balance, fees are reduced later by RPC Server // Send balance, fees are reduced later by RPC Server
try { try {
$bitcoin->sendtoaddress($aUserData['coin_address'], $dBalance - $config['txfee']); $txid = $bitcoin->sendtoaddress($aUserData['coin_address'], $dBalance - $config['txfee']);
} catch (BitcoinClientException $e) { } catch (BitcoinClientException $e) {
$log->logError('Failed to send requested balance to coin address, please check payout process'); $log->logError('Failed to send requested balance to coin address, please check payout process');
continue; continue;
} }
// Create transaction record // Create transaction record
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'])) { if ($transaction->addTransaction($aUserData['id'], $dBalance - $config['txfee'], 'Debit_AP', NULL, $aUserData['coin_address'], $txid) && $transaction->addTransaction($aUserData['id'], $config['txfee'], 'TXFee', NULL, $aUserData['coin_address'])) {
// Mark all older transactions as archived // Mark all older transactions as archived
if (!$transaction->setArchived($aUserData['id'], $transaction->insert_id)) if (!$transaction->setArchived($aUserData['id'], $transaction->insert_id))
$log->logError('Failed to mark transactions for user #' . $aUserData['id'] . ' prior to #' . $transaction->insert_id . ' as archived'); $log->logError('Failed to mark transactions for user #' . $aUserData['id'] . ' prior to #' . $transaction->insert_id . ' as archived');

View File

@ -27,10 +27,7 @@ require_once('shared.inc.php');
if ( $bitcoin->can_connect() !== true ) { if ( $bitcoin->can_connect() !== true ) {
$log->logFatal("Failed to connect to RPC server\n"); $log->logFatal("Failed to connect to RPC server\n");
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0006', 1, true);
$monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
} }
// Fetch all unconfirmed blocks // Fetch all unconfirmed blocks
@ -54,7 +51,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
if ($aBlock['confirmations'] == $aBlockInfo['confirmations']) { if ($aBlock['confirmations'] == $aBlockInfo['confirmations']) {
$log->logDebug(' No update needed'); $log->logDebug(' No update needed');
} else if (!$block->setConfirmations($aBlock['id'], $aBlockInfo['confirmations'])) { } else if (!$block->setConfirmations($aBlock['id'], $aBlockInfo['confirmations'])) {
$log->logError(' Failed to update block confirmations'); $log->logError(' Failed to update block confirmations: ' . $block->getCronMessage());
} }
} }

View File

@ -19,10 +19,7 @@ limitations under the License.
*/ */
// Monitoring cleanup and status update // Monitoring cleanup and status update
$monitoring->setStatus($cron_name . "_message", "message", "OK"); $monitoring->endCronjob($cron_name, 'OK', 0, false, false);
$monitoring->setStatus($cron_name . "_status", "okerror", 0);
$monitoring->setStatus($cron_name . "_runtime", "time", microtime(true) - $cron_start[$cron_name]); $monitoring->setStatus($cron_name . "_runtime", "time", microtime(true) - $cron_start[$cron_name]);
$monitoring->setStatus($cron_name . "_endtime", "date", time()); $monitoring->setStatus($cron_name . "_endtime", "date", time());
// Mark cron as running for monitoring
$monitoring->setStatus($cron_name . '_active', "yesno", 0);
?> ?>

View File

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

View File

@ -26,20 +26,13 @@ chdir(dirname(__FILE__));
require_once('shared.inc.php'); require_once('shared.inc.php');
if ($setting->getValue('disable_mp') == 1) { if ($setting->getValue('disable_mp') == 1) {
$log->logInfo(" auto payout disabled via admin panel"); $log->logInfo(" manual payout disabled via admin panel");
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0009', 0, true);
$monitoring->setStatus($cron_name . "_message", "message", "Auto-Payout disabled");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
$monitoring->setStatus($cron_name . "_endtime", "date", time());
exit(0);
} }
if ($bitcoin->can_connect() !== true) { if ($bitcoin->can_connect() !== true) {
$log->logFatal(" unable to connect to RPC server, exiting"); $log->logFatal(" unable to connect to RPC server, exiting");
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0006', 1, true);
$monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
} }
// Fetch outstanding payout requests // Fetch outstanding payout requests
@ -53,6 +46,12 @@ if (count($aPayouts) > 0) {
$aData['coin_address'] = $user->getCoinAddress($aData['account_id']); $aData['coin_address'] = $user->getCoinAddress($aData['account_id']);
$aData['username'] = $user->getUserName($aData['account_id']); $aData['username'] = $user->getUserName($aData['account_id']);
if ($dBalance > $config['txfee']) { if ($dBalance > $config['txfee']) {
// To ensure we don't run this transaction again, lets mark it completed
if (!$oPayout->setProcessed($aData['id'])) {
$log->logFatal('unable to mark transactions ' . $aData['id'] . ' as processed.');
$monitoring->endCronjob($cron_name, 'E0010', 1, true);
}
$log->logInfo("\t" . $aData['account_id'] . "\t\t" . $aData['username'] . "\t" . $dBalance . "\t\t" . $aData['coin_address']); $log->logInfo("\t" . $aData['account_id'] . "\t\t" . $aData['username'] . "\t" . $dBalance . "\t\t" . $aData['coin_address']);
try { try {
$aStatus = $bitcoin->validateaddress($aData['coin_address']); $aStatus = $bitcoin->validateaddress($aData['coin_address']);
@ -65,21 +64,13 @@ if (count($aPayouts) > 0) {
continue; continue;
} }
try { try {
$bitcoin->sendtoaddress($aData['coin_address'], $dBalance - $config['txfee']); $txid = $bitcoin->sendtoaddress($aData['coin_address'], $dBalance - $config['txfee']);
} catch (BitcoinClientException $e) { } catch (BitcoinClientException $e) {
$log->logError('Failed to send requested balance to coin address, please check payout process'); $log->logError('Failed to send requested balance to coin address, please check payout process');
continue; continue;
} }
// To ensure we don't run this transaction again, lets mark it completed
if (!$oPayout->setProcessed($aData['id'])) {
$log->logFatal('unable to mark transactions ' . $aData['id'] . ' as processed.');
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Unable set payout as processed");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
}
if ($transaction->addTransaction($aData['account_id'], $dBalance - $config['txfee'], 'Debit_MP', NULL, $aData['coin_address']) && $transaction->addTransaction($aData['account_id'], $config['txfee'], 'TXFee', NULL, $aData['coin_address'])) { if ($transaction->addTransaction($aData['account_id'], $dBalance - $config['txfee'], 'Debit_MP', NULL, $aData['coin_address'], $txid) && $transaction->addTransaction($aData['account_id'], $config['txfee'], 'TXFee', NULL, $aData['coin_address'])) {
// Mark all older transactions as archived // Mark all older transactions as archived
if (!$transaction->setArchived($aData['account_id'], $transaction->insert_id)) if (!$transaction->setArchived($aData['account_id'], $transaction->insert_id))
$log->logError('Failed to mark transactions for #' . $aData['account_id'] . ' prior to #' . $transaction->insert_id . ' as archived'); $log->logError('Failed to mark transactions for #' . $aData['account_id'] . ' prior to #' . $transaction->insert_id . ' as archived');
@ -91,7 +82,8 @@ if (count($aPayouts) > 0) {
if (!$notification->sendNotification($aData['account_id'], 'manual_payout', $aMailData)) if (!$notification->sendNotification($aData['account_id'], 'manual_payout', $aMailData))
$log->logError('Failed to send notification email to users address: ' . $aMailData['email']); $log->logError('Failed to send notification email to users address: ' . $aMailData['email']);
} else { } else {
$log->logError('Failed to add new Debit_MP transaction in database for user ' . $user->getUserName($aData['account_id'])); $log->logFatal('Failed to add new Debit_MP transaction in database for user ' . $user->getUserName($aData['account_id']));
$monitoring->endCronjob($cron_name, 'E0064', 1, true);
} }
} }

View File

@ -26,10 +26,7 @@ chdir(dirname(__FILE__));
require_once('shared.inc.php'); require_once('shared.inc.php');
if ($setting->getValue('disable_notifications') == 1) { if ($setting->getValue('disable_notifications') == 1) {
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0009', 0, true);
$monitoring->setStatus($cron_name . "_message", "message", "Cron disabled by admin");
$monitoring->setStatus($cron_name . "_status", "okerror", 0);
exit(0);
} }
$log->logDebug(" IDLE Worker Notifications ..."); $log->logDebug(" IDLE Worker Notifications ...");
@ -45,9 +42,9 @@ if (empty($aWorkers)) {
$aData['subject'] = 'IDLE Worker : ' . $aWorker['username']; $aData['subject'] = 'IDLE Worker : ' . $aWorker['username'];
$aData['worker'] = $aWorker['username']; $aData['worker'] = $aWorker['username'];
$aData['email'] = $user->getUserEmail($aData['username']); $aData['email'] = $user->getUserEmail($aData['username']);
$log->logInfo(" " . $aWorker['username'] . "..."); $log->logDebug(" " . $aWorker['username'] . "...");
if (!$notification->sendNotification($aWorker['account_id'], 'idle_worker', $aData)) if (!$notification->sendNotification($aWorker['account_id'], 'idle_worker', $aData))
$log->logError(" Failed sending notifications: " . $notification->getError() . "\n"); $log->logDebug(" Failed sending notifications: " . $notification->getCronError() . "\n");
} }
} }
@ -60,15 +57,15 @@ if (!empty($aNotifications)) {
foreach ($aNotifications as $aNotification) { foreach ($aNotifications as $aNotification) {
$aData = json_decode($aNotification['data'], true); $aData = json_decode($aNotification['data'], true);
$aWorker = $worker->getWorker($aData['id']); $aWorker = $worker->getWorker($aData['id']);
$log->logInfo(" " . $aWorker['username'] . " ..."); $log->logDebug(" " . $aWorker['username'] . " ...");
if ($aWorker['hashrate'] > 0) { if ($aWorker['hashrate'] > 0) {
if ($notification->setInactive($aNotification['id'])) { if ($notification->setInactive($aNotification['id'])) {
$log->logInfo(" updated #" . $aNotification['id'] . " for " . $aWorker['username'] . " as inactive\n"); $log->logDebug(" updated #" . $aNotification['id'] . " for " . $aWorker['username'] . " as inactive\n");
} else { } else {
$log->logInfo(" failed to update #" . $aNotification['id'] . " for " . $aWorker['username'] . "\n"); $log->logError(" failed to update #" . $aNotification['id'] . " for " . $aWorker['username'] . "\n");
} }
} else { } else {
$log->logInfo(" still inactive\n"); $log->logDebug(" still inactive\n");
} }
} }
} else { } else {

View File

@ -35,37 +35,50 @@ if ($config['payout_system'] != 'pplns') {
$aAllBlocks = $block->getAllUnaccounted('ASC'); $aAllBlocks = $block->getAllUnaccounted('ASC');
if (empty($aAllBlocks)) { if (empty($aAllBlocks)) {
$log->logDebug("No new unaccounted blocks found"); $log->logDebug("No new unaccounted blocks found");
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0011', 0, true, false);
$monitoring->setStatus($cron_name . "_message", "message", "No new unaccounted blocks");
$monitoring->setStatus($cron_name . "_status", "okerror", 0);
exit(0);
} }
$count = 0; $count = 0;
foreach ($aAllBlocks as $iIndex => $aBlock) { foreach ($aAllBlocks as $iIndex => $aBlock) {
// If we have unaccounted blocks without share_ids, they might not have been inserted yet
if (!$aBlock['share_id']) {
$log->logError('E0062: Block has no share_id, not running payouts');
$monitoring->endCronjob($cron_name, 'E0062', 0, true);
}
// We support some dynamic share targets but fall back to our fixed value // We support some dynamic share targets but fall back to our fixed value
// Re-calculate after each run due to re-targets in this loop // Re-calculate after each run due to re-targets in this loop
if ($config['pplns']['shares']['type'] == 'blockavg' && $block->getBlockCount() > 0) { if ($config['pplns']['shares']['type'] == 'blockavg' && $block->getBlockCount() > 0) {
$pplns_target = round($block->getAvgBlockShares($aBlock['height'], $config['pplns']['blockavg']['blockcount'])); $pplns_target = round($block->getAvgBlockShares($aBlock['height'], $config['pplns']['blockavg']['blockcount']));
} else if ($config['pplns']['shares']['type'] == 'dynamic' && $block->getBlockCount() > 0) {
$pplns_target = round($block->getAvgBlockShares($aBlock['height'], $config['pplns']['blockavg']['blockcount']) * (100 - $config['pplns']['dynamic']['percent'])/100 + $aBlock['shares'] * $config['pplns']['dynamic']['percent']/100);
} else { } else {
$pplns_target = $config['pplns']['shares']['default']; $pplns_target = $config['pplns']['shares']['default'];
} }
if (!$aBlock['accounted'] && $aBlock['height'] > $setting->getValue('last_accounted_block_height')) { // Fetch our last paid block information
if ($iLastBlockId = $setting->getValue('last_accounted_block_id')) {
$aLastAccountedBlock = $block->getBlockById($iLastBlockId);
} else {
// A fake block to ensure payouts get started on first round
$iLastBlockId = 0;
$aLastAccountedBlock = array('height' => 0, 'confirmations' => 1);
}
// Ensure we are not paying out twice, ignore if the previous paid block is orphaned (-1 confirmations) and payout anyway
if ((!$aBlock['accounted'] && $aBlock['height'] > $aLastAccountedBlock['height']) || (@$aLastAccountedBlock['confirmations'] == -1)) {
$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']; $iCurrentUpstreamId = $aBlock['share_id'];
if (!is_numeric($iCurrentUpstreamId)) { if (!is_numeric($iCurrentUpstreamId)) {
$log->logFatal("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue"); $log->logFatal("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue");
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0012', 1, true);
$monitoring->setStatus($cron_name . "_message", "message", "Block " . $aBlock['height'] . " has no share_id associated with it");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
} }
$iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']); $iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']);
$iNewRoundShares = 0; $iNewRoundShares = 0;
$config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward']; $config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward'];
$aRoundAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']); $aRoundAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']);
$log->logInfo('Target: ' . $pplns_target . '; Shares: ' . $iRoundShares . '; Height: ' . $aBlock['height'] . '; Amount: ' . $aBlock['amount'] . '; Found by ID: ' . $aBlock['account_id']);
if ($iRoundShares >= $pplns_target) { if ($iRoundShares >= $pplns_target) {
$log->logDebug("Matching or exceeding PPLNS target of $pplns_target with $iRoundShares"); $log->logDebug("Matching or exceeding PPLNS target of $pplns_target with $iRoundShares");
$iMinimumShareId = $share->getMinimumShareId($pplns_target, $aBlock['share_id']); $iMinimumShareId = $share->getMinimumShareId($pplns_target, $aBlock['share_id']);
@ -73,15 +86,12 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
$aAccountShares = $share->getSharesForAccounts($iMinimumShareId - 1, $aBlock['share_id']); $aAccountShares = $share->getSharesForAccounts($iMinimumShareId - 1, $aBlock['share_id']);
if (empty($aAccountShares)) { if (empty($aAccountShares)) {
$log->logFatal("No shares found for this block, aborted! Block Height : " . $aBlock['height']); $log->logFatal("No shares found for this block, aborted! Block Height : " . $aBlock['height']);
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0013', 1, true);
$monitoring->setStatus($cron_name . "_message", "message", "No shares found for this block: " . $aBlock['height']);
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
} }
foreach($aAccountShares as $key => $aData) { foreach($aAccountShares as $key => $aData) {
$iNewRoundShares += $aData['valid']; $iNewRoundShares += $aData['valid'];
} }
$log->logInfo('Adjusting round target to PPLNS target ' . $iNewRoundShares); $log->logInfo('Adjusting round to PPLNS target of ' . $pplns_target . ' shares used ' . $iNewRoundShares);
$iRoundShares = $iNewRoundShares; $iRoundShares = $iNewRoundShares;
} else { } else {
$log->logDebug("Not able to match PPLNS target of $pplns_target with $iRoundShares"); $log->logDebug("Not able to match PPLNS target of $pplns_target with $iRoundShares");
@ -90,10 +100,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
$aAccountShares = $aRoundAccountShares; $aAccountShares = $aRoundAccountShares;
if (empty($aAccountShares)) { if (empty($aAccountShares)) {
$log->logFatal("No shares found for this block, aborted! Block height: " . $aBlock['height']); $log->logFatal("No shares found for this block, aborted! Block height: " . $aBlock['height']);
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0013', 1, true);
$monitoring->setStatus($cron_name . "_message", "message", "No shares found for this block: " . $aBlock['height']);
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
} }
// Grab only the most recent shares from Archive that fill the missing shares // Grab only the most recent shares from Archive that fill the missing shares
@ -114,6 +121,24 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
$aAccountShares[$key]['invalid'] += $aArchiveShares[$aData['username']]['invalid']; $aAccountShares[$key]['invalid'] += $aArchiveShares[$aData['username']]['invalid'];
} }
} }
// reverse payout
if ($config['pplns']['reverse_payout']) {
$aSharesData = NULL;
foreach($aAccountShares as $key => $aData) {
$aSharesData[$aData['username']] = $aData;
}
// Add users from archive not in current round
foreach($aArchiveShares as $key => $aArchData) {
if (!array_key_exists($aArchData['account'], $aSharesData)) {
$log->logDebug('Adding user ' . $aArchData['account'] . ' to round shares');
$log->logDebug(' valid : ' . $aArchData['valid']);
$log->logDebug(' invalid : ' . $aArchData['invalid']);
$aArchData['username'] = $aArchData['account'];
$aSharesData[$aArchData['account']] = $aArchData;
}
}
$aAccountShares = $aSharesData;
}
} }
// We tried to fill up to PPLNS target, now we need to check the actual shares to properly payout users // We tried to fill up to PPLNS target, now we need to check the actual shares to properly payout users
foreach($aAccountShares as $key => $aData) { foreach($aAccountShares as $key => $aData) {
@ -159,44 +184,55 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
foreach ($aRoundAccountShares as $key => $aRoundData) { foreach ($aRoundAccountShares as $key => $aRoundData) {
if ($aRoundData['username'] == $aData['username']) if ($aRoundData['username'] == $aData['username'])
if (!$statistics->updateShareStatistics($aRoundData, $aBlock['id'])) if (!$statistics->updateShareStatistics($aRoundData, $aBlock['id']))
$log->logError('Failed to update share statistics for ' . $aData['username']); $log->logError('Failed to update share statistics for ' . $aData['username'] . ': ' . $statistics->getCronError());
}
// Add PPLNS share statistics
foreach ($aAccountShares as $key => $aRoundData) {
if ($aRoundData['username'] == $aData['username']){
if (@$statistics->getIdShareStatistics($aRoundData, $aBlock['id'])){
if (!$statistics->updatePPLNSShareStatistics($aRoundData, $aBlock['id']))
$log->logError('Failed to update pplns statistics for ' . $aData['username'] . ': ' . $statistics->getCronError());
} else {
if (!$statistics->insertPPLNSShareStatistics($aRoundData, $aBlock['id']))
$log->logError('Failed to insert pplns statistics for ' . $aData['username'] . ': ' . $statistics->getCronError());
}
}
} }
// Add new credit transaction // Add new credit transaction
if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit', $aBlock['id'])) if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit', $aBlock['id']))
$log->logFatal('Failed to insert new Credit transaction to database for ' . $aData['username']); $log->logFatal('Failed to insert new Credit transaction to database for ' . $aData['username'] . ': ' . $transaction->getCronError());
// Add new fee debit for this block // Add new fee debit for this block
if ($aData['fee'] > 0 && $config['fees'] > 0) if ($aData['fee'] > 0 && $config['fees'] > 0)
if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee', $aBlock['id'])) if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee', $aBlock['id']))
$log->logFatal('Failed to insert new Fee transaction to database for ' . $aData['username']); $log->logFatal('Failed to insert new Fee transaction to database for ' . $aData['username'] . ': ' . $transaction->getCronError());
// Add new donation debit // Add new donation debit
if ($aData['donation'] > 0) if ($aData['donation'] > 0)
if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation', $aBlock['id'])) if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation', $aBlock['id']))
$log->logFatal('Failed to insert new Donation transaction to database for ' . $aData['username']); $log->logFatal('Failed to insert new Donation transaction to database for ' . $aData['username'] . ': ' . $transaction->getCronError());
} }
// Store this blocks height as last accounted for // Store this blocks height as last accounted for
$setting->setValue('last_accounted_block_height', $aBlock['height']); $setting->setValue('last_accounted_block_id', $aBlock['id']);
// Move counted shares to archive before this blockhash upstream share // Move counted shares to archive before this blockhash upstream share
if (!$share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId)) if (!$share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId))
$log->logError('Failed to copy shares to archive table'); $log->logError('Failed to copy shares to archive table: ' . $share->getCronError() . ': ' . $share->getCronError());
// Delete all accounted shares // Delete all accounted shares
if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) { if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) {
$log->logFatal("Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting!"); $log->logFatal("Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting! Error: " . $share->getCronError());
exit(1); $monitoring->endCronjob($cron_name, 'E0016', 1, true);
} }
// Mark this block as accounted for // Mark this block as accounted for
if (!$block->setAccounted($aBlock['id'])) { if (!$block->setAccounted($aBlock['id'])) {
$log->logFatal("Failed to mark block as accounted! Aborting!"); $log->logFatal("Failed to mark block as accounted! Aborting! Error: " . $block->getCronError());
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0014', 1, true);
$monitoring->setStatus($cron_name . "_message", "message", "Failed to mark block " . $aBlock['height'] . " as accounted");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
} }
} else { } else {
$log->logFatal('Potential double payout detected. Aborted.');
$aMailData = array( $aMailData = array(
'email' => $setting->getValue('website_email'), 'email' => $setting->getValue('system_error_email'),
'subject' => 'Payout processing aborted', 'subject' => 'Payout processing aborted',
'Error' => 'Potential double payout detected. All payouts halted until fixed!', 'Error' => 'Potential double payout detected. All payouts halted until fixed!',
'BlockID' => $aBlock['id'], 'BlockID' => $aBlock['id'],
@ -204,12 +240,8 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
'Block Share ID' => $aBlock['share_id'] 'Block Share ID' => $aBlock['share_id']
); );
if (!$mail->sendMail('notifications/error', $aMailData)) if (!$mail->sendMail('notifications/error', $aMailData))
$log->logError(" Failed sending notifications: " . $notification->getError() . "\n"); $log->logError(" Failed sending notifications: " . $notification->getCronError() . "\n");
$log->logFatal('Potential double payout detected. Aborted.'); $monitoring->endCronjob($cron_name, 'E0015', 1, true);
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Block height for block too low! Potential double payout detected.");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
} }
} }

View File

@ -54,19 +54,17 @@ if ($config['pps']['reward']['type'] == 'blockavg' && $block->getBlockCount() >
if ($config['pps']['reward']['type'] == 'block') { if ($config['pps']['reward']['type'] == 'block') {
if ($aLastBlock = $block->getLast()) { if ($aLastBlock = $block->getLast()) {
$pps_reward = $aLastBlock['amount']; $pps_reward = $aLastBlock['amount'];
$log->logInfo("PPS reward using last block, amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty);
} else { } else {
$pps_reward = $config['pps']['reward']['default']; $pps_reward = $config['pps']['reward']['default'];
$log->logInfo("PPS reward using default, amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty);
} }
} else { } else {
$pps_reward = $config['pps']['reward']['default']; $pps_reward = $config['pps']['reward']['default'];
$log->logInfo("PPS reward fixed default, amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty);
} }
} }
// Per-share value to be paid out to users // Per-share value to be paid out to users
$pps_value = round($pps_reward / (pow(2,32) * $dDifficulty) * pow(2, $config['pps_target']), 12); $pps_value = round($pps_reward / (pow(2, $config['target_bits']) * $dDifficulty), 12);
// Find our last share accounted and last inserted share for PPS calculations // Find our last share accounted and last inserted share for PPS calculations
$iPreviousShareId = $setting->getValue('pps_last_share_id'); $iPreviousShareId = $setting->getValue('pps_last_share_id');
@ -75,11 +73,16 @@ $iLastShareId = $share->getLastInsertedShareId();
// Check for all new shares, we start one higher as our last accounted share to avoid duplicates // Check for all new shares, we start one higher as our last accounted share to avoid duplicates
$aAccountShares = $share->getSharesForAccounts($iPreviousShareId + 1, $iLastShareId); $aAccountShares = $share->getSharesForAccounts($iPreviousShareId + 1, $iLastShareId);
$log->logInfo("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\tPayout\t\tDonation\tFee"); if (!empty($aAccountShares)) {
// Info for this payout
$log->logInfo("PPS reward type: " . $config['pps']['reward']['type'] . ", amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty . "\tPPS value: " . $pps_value);
$log->logInfo("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\tPayout\t\tDonation\tFee");
}
foreach ($aAccountShares as $aData) { foreach ($aAccountShares as $aData) {
// Take our valid shares and multiply by per share value // MPOS uses a base difficulty setting to avoid showing weightened shares
$aData['payout'] = round($aData['valid'] * $pps_value, 8); // Since we need weightened shares here, we go back to the proper value for payouts
$aData['payout'] = round($aData['valid'] * pow(2, ($config['difficulty'] - 16)) * $pps_value, 8);
// Defaults // Defaults
$aData['fee' ] = 0; $aData['fee' ] = 0;
@ -94,7 +97,7 @@ foreach ($aAccountShares as $aData) {
$log->logInfo($aData['id'] . "\t" . $log->logInfo($aData['id'] . "\t" .
$aData['username'] . "\t" . $aData['username'] . "\t" .
$aData['invalid'] . "\t" . $aData['invalid'] . "\t" .
$aData['valid'] . "\t*\t" . $aData['valid'] * pow(2, ($config['difficulty'] - 16)) . "\t*\t" .
number_format($pps_value, 12) . "\t=\t" . number_format($pps_value, 12) . "\t=\t" .
number_format($aData['payout'], 8) . "\t" . number_format($aData['payout'], 8) . "\t" .
number_format($aData['donation'], 8) . "\t" . number_format($aData['donation'], 8) . "\t" .
@ -102,15 +105,15 @@ foreach ($aAccountShares as $aData) {
// Add new credit transaction // Add new credit transaction
if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit_PPS')) if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit_PPS'))
$log->logError('Failed to add Credit_PPS transaction in database'); $log->logError('Failed to add Credit_PPS transaction in database: ' . $transaction->getCronError());
// Add new fee debit for this block // Add new fee debit for this block
if ($aData['fee'] > 0 && $config['fees'] > 0) if ($aData['fee'] > 0 && $config['fees'] > 0)
if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee_PPS')) if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee_PPS'))
$log->logError('Failed to add Fee_PPS transaction in database'); $log->logError('Failed to add Fee_PPS transaction in database: ' . $transaction->getCronError());
// Add new donation debit // Add new donation debit
if ($aData['donation'] > 0) if ($aData['donation'] > 0)
if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation_PPS')) if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation_PPS'))
$log->logError('Failed to add Donation_PPS transaction in database'); $log->logError('Failed to add Donation_PPS transaction in database: ' . $transaction->getCronError());
} }
// Store our last inserted ID for the next run // Store our last inserted ID for the next run
@ -118,7 +121,10 @@ $setting->setValue('pps_last_share_id', $iLastShareId);
// Fetch all unaccounted blocks // Fetch all unaccounted blocks
$aAllBlocks = $block->getAllUnaccounted('ASC'); $aAllBlocks = $block->getAllUnaccounted('ASC');
if (empty($aAllBlocks)) $log->logDebug("No new unaccounted blocks found"); if (empty($aAllBlocks)) {
$log->logDebug("No new unaccounted blocks found");
// No monitoring event here, not fatal for PPS
}
// Go through blocks and archive/delete shares that have been accounted for // Go through blocks and archive/delete shares that have been accounted for
foreach ($aAllBlocks as $iIndex => $aBlock) { foreach ($aAllBlocks as $iIndex => $aBlock) {
@ -135,28 +141,22 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
$aAccountShares = $share->getSharesForAccounts(@$iLastBlockShare, $aBlock['share_id']); $aAccountShares = $share->getSharesForAccounts(@$iLastBlockShare, $aBlock['share_id']);
foreach ($aAccountShares as $key => $aData) { foreach ($aAccountShares as $key => $aData) {
if (!$statistics->updateShareStatistics($aData, $aBlock['id'])) if (!$statistics->updateShareStatistics($aData, $aBlock['id']))
$log->logError("Failed to update stats for this block on : " . $aData['username']); $log->logError("Failed to update stats for this block on : " . $aData['username'] . ': ' . $statistics->getCronError());
} }
// Move shares to archive // Move shares to archive
if ($aBlock['share_id'] < $iLastShareId) { if ($aBlock['share_id'] < $iLastShareId) {
if (!$share->moveArchive($aBlock['share_id'], $aBlock['id'], @$iLastBlockShare)) if (!$share->moveArchive($aBlock['share_id'], $aBlock['id'], @$iLastBlockShare))
$log->logError("Archving failed"); $log->logError("Failed to copy shares to archive: " . $share->getCronError() . ': ' . $share->getCronError());
} }
// Delete shares // Delete shares
if ($aBlock['share_id'] < $iLastShareId && !$share->deleteAccountedShares($aBlock['share_id'], $iLastBlockShare)) { if ($aBlock['share_id'] < $iLastShareId && !$share->deleteAccountedShares($aBlock['share_id'], $iLastBlockShare)) {
$log->logFatal("Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . $iLastBlockShare . ", aborting!"); $log->logFatal("Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . $iLastBlockShare . ", aborting! Error: " . $share->getCronError());
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0016', 1, true);
$monitoring->setStatus($cron_name . "_message", "message", "Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . $iLastBlockShare);
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
} }
// Mark this block as accounted for // Mark this block as accounted for
if (!$block->setAccounted($aBlock['id'])) { if (!$block->setAccounted($aBlock['id'])) {
$log->logFatal("Failed to mark block as accounted! Aborting!"); $log->logFatal("Failed to mark block as accounted! Aborting! Error: " . $block->getCronError());
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0014', 1, true);
$monitoring->setStatus($cron_name . "_message", "message", "Failed to mark block " . $aBlock['height'] . " as accounted");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
} }
} }

View File

@ -35,17 +35,30 @@ if ($config['payout_system'] != 'prop') {
$aAllBlocks = $block->getAllUnaccounted('ASC'); $aAllBlocks = $block->getAllUnaccounted('ASC');
if (empty($aAllBlocks)) { if (empty($aAllBlocks)) {
$log->logDebug('No new unaccounted blocks found in database'); $log->logDebug('No new unaccounted blocks found in database');
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0011', 0, true, false);
$monitoring->setStatus($cron_name . "_message", "message", "No new unaccounted blocks");
$monitoring->setStatus($cron_name . "_status", "okerror", 0);
exit(0);
} }
$count = 0; $count = 0;
// Table header for account shares // Table header for account shares
$log->logInfo("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee"); $log->logInfo("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee");
foreach ($aAllBlocks as $iIndex => $aBlock) { foreach ($aAllBlocks as $iIndex => $aBlock) {
if (!$aBlock['accounted'] && $aBlock['height'] > $setting->getValue('last_accounted_block_height')) { // If we have unaccounted blocks without share_ids, they might not have been inserted yet
if (!$aBlock['share_id']) {
$log->logError('E0062: Block has no share_id, not running payouts');
$monitoring->endCronjob($cron_name, 'E0062', 0, true);
}
// Fetch last paid block information
if ($iLastBlockId = $setting->getValue('last_accounted_block_id')) {
$aLastAccountedBlock = $block->getBlockById($iLastBlockId);
} else {
// A fake block to ensure payouts get started on first round
$iLastBlockId = 0;
$aLastAccountedBlock = array('height' => 0, 'confirmations' => 1);
}
// Ensure we are not paying out twice, ignore if the previous paid block is orphaned (-1 confirmations) and payout anyway
if ((!$aBlock['accounted'] && $aBlock['height'] > $aLastAccountedBlock['height']) || (@$aLastAccountedBlock['confirmations'] == -1)) {
$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']; $iCurrentUpstreamId = $aBlock['share_id'];
$aAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']); $aAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']);
@ -54,10 +67,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
if (empty($aAccountShares)) { if (empty($aAccountShares)) {
$log->logFatal('No shares found for this block, aborted: ' . $aBlock['height']); $log->logFatal('No shares found for this block, aborted: ' . $aBlock['height']);
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0013', 1, true);
$monitoring->setStatus($cron_name . "_message", "message", "No shares found for this block, aborted: " . $aBlock['height']);
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
} }
// Loop through all accounts that have found shares for this round // Loop through all accounts that have found shares for this round
@ -86,46 +96,40 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
// Update user share statistics // Update user share statistics
if (!$statistics->updateShareStatistics($aData, $aBlock['id'])) if (!$statistics->updateShareStatistics($aData, $aBlock['id']))
$log->logFatal('Failed to update share statistics for ' . $aData['username']); $log->logFatal('Failed to update share statistics for ' . $aData['username'] . ': ' . $statistics->getCronError());
// Add new credit transaction // Add new credit transaction
if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit', $aBlock['id'])) if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit', $aBlock['id']))
$log->logFatal('Failed to insert new Credit transaction to database for ' . $aData['username']); $log->logFatal('Failed to insert new Credit transaction to database for ' . $aData['username'] . ': ' . $transaction->getCronError());
// Add new fee debit for this block // Add new fee debit for this block
if ($aData['fee'] > 0 && $config['fees'] > 0) if ($aData['fee'] > 0 && $config['fees'] > 0)
if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee', $aBlock['id'])) if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee', $aBlock['id']))
$log->logFatal('Failed to insert new Fee transaction to database for ' . $aData['username']); $log->logFatal('Failed to insert new Fee transaction to database for ' . $aData['username'] . ': ' . $transaction->getCronError());
// Add new donation debit // Add new donation debit
if ($aData['donation'] > 0) if ($aData['donation'] > 0)
if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation', $aBlock['id'])) if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation', $aBlock['id']))
$log->logFatal('Failed to insert new Donation transaction to database for ' . $aData['username']); $log->logFatal('Failed to insert new Donation transaction to database for ' . $aData['username'] . ': ' . $transaction->getCronError());
} }
// Add block as accounted for into settings table // Add block as accounted for into settings table
$setting->setValue('last_accounted_block_height', $aBlock['height']); $setting->setValue('last_accounted_block_id', $aBlock['id']);
// Move counted shares to archive before this blockhash upstream share // Move counted shares to archive before this blockhash upstream share
if (!$share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId)) if (!$share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId))
$log->logError('Failed to copy shares to archive'); $log->logError('Failed to copy shares to archive: ' . $share->getCronError());
// Delete all accounted shares // Delete all accounted shares
if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) { if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) {
$log->logFatal('Failed to delete accounted shares from ' . $iPreviousShareId . ' to ' . $iCurrentUpstreamId . ', aborted'); $log->logFatal('Failed to delete accounted shares from ' . $iPreviousShareId . ' to ' . $iCurrentUpstreamId . ', aborted! Error: ' . $share->getCronError());
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0016', 1, true);
$monitoring->setStatus($cron_name . "_message", "message", "Failed to delete accounted shares from " . $iPreviousShareId . " to " . $iCurrentUpstreamId);
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
} }
// Mark this block as accounted for // Mark this block as accounted for
if (!$block->setAccounted($aBlock['id'])) { if (!$block->setAccounted($aBlock['id'])) {
$log->logFatal('Failed to mark block as accounted! Aborted.'); $log->logFatal('Failed to mark block as accounted! Aborted! Error: ' . $block->getCronError());
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0014', 1, true);
$monitoring->setStatus($cron_name . "_message", "message", "Failed to mark block " . $aBlock['height'] . " as accounted");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
} }
} else { } else {
$log->logFatal('Possible double payout detected. Aborted.'); $log->logFatal('Potential double payout detected. Aborted.');
$aMailData = array( $aMailData = array(
'email' => $setting->getValue('website_email'), 'email' => $setting->getValue('system_error_email'),
'subject' => 'Payout Failure: Double Payout', 'subject' => 'Payout Failure: Double Payout',
'Error' => 'Possible double payout detected', 'Error' => 'Possible double payout detected',
'BlockID' => $aBlock['id'], 'BlockID' => $aBlock['id'],
@ -133,11 +137,8 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
'Block Share ID' => $aBlock['share_id'] 'Block Share ID' => $aBlock['share_id']
); );
if (!$mail->sendMail('notifications/error', $aMailData)) if (!$mail->sendMail('notifications/error', $aMailData))
$log->logError(" Failed sending notifications: " . $notification->getError() . "\n"); $log->logFatal('Potential double payout detected. Aborted.');
$monitoring->setStatus($cron_name . "_active", "yesno", 0); $monitoring->endCronjob($cron_name, 'E0015', 1, true);
$monitoring->setStatus($cron_name . "_message", "message", 'Possible double payout detected. Aborted.');
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
} }
} }

View File

@ -29,17 +29,32 @@ SUBFOLDER=""
# # # #
################################################################ ################################################################
# Mac OS detection
OS=`uname`
case "$OS" in
Darwin) READLINK=$( which greadlink ) ;;
*) READLINK=$( which readlink ) ;;
esac
if [[ ! -x $READLINK ]]; then
echo "readlink not found, please install first";
exit 1;
fi
# My own name # My own name
ME=$( basename $0 ) ME=$( basename $0 )
# Overwrite some settings via command line arguments # Overwrite some settings via command line arguments
while getopts "hvp:d:" opt; do while getopts "hfvp:d:" opt; do
case "$opt" in case "$opt" in
h|\?) h|\?)
echo "Usage: $0 [-v] [-p PHP_BINARY] [-d SUBFOLDER]"; echo "Usage: $0 [-v] [-p PHP_BINARY] [-d SUBFOLDER]";
exit 0 exit 0
;; ;;
v) VERBOSE=1 ;; v) VERBOSE=1 ;;
f) PHP_OPTS="$PHP_OPTS -f";;
p) PHP_BIN=$OPTARG ;; p) PHP_BIN=$OPTARG ;;
d) SUBFOLDER=$OPTARG ;; d) SUBFOLDER=$OPTARG ;;
:) :)
@ -52,7 +67,7 @@ done
# Path to PID file, needs to be writable by user running this # Path to PID file, needs to be writable by user running this
PIDFILE="${BASEPATH}/${SUBFOLDER}/${ME}.pid" PIDFILE="${BASEPATH}/${SUBFOLDER}/${ME}.pid"
# Clean PIDFILE path # Clean PIDFILE path
PIDFILE=$(readlink -m "$PIDFILE") PIDFILE=$($READLINK -m "$PIDFILE")
# Create folders recursively if necessary # Create folders recursively if necessary
if ! $(mkdir -p $( dirname $PIDFILE)); then if ! $(mkdir -p $( dirname $PIDFILE)); then
@ -62,7 +77,7 @@ fi
# Find scripts path # Find scripts path
if [[ -L $0 ]]; then if [[ -L $0 ]]; then
CRONHOME=$( dirname $( readlink $0 ) ) CRONHOME=$( dirname $( $READLINK $0 ) )
else else
CRONHOME=$( dirname $0 ) CRONHOME=$( dirname $0 )
fi fi
@ -104,7 +119,7 @@ echo $PID > $PIDFILE
for cron in $CRONS; do for cron in $CRONS; do
[[ $VERBOSE == 1 ]] && echo "Running $cron, check logfile for details" [[ $VERBOSE == 1 ]] && echo "Running $cron, check logfile for details"
$PHP_BIN $cron $PHP_BIN $cron $PHP_OPTS
done done
# Remove pidfile # Remove pidfile

View File

@ -41,6 +41,16 @@ require_once(BASEPATH . 'include/config/global.inc.php');
// We include all needed files here, even though our templates could load them themself // We include all needed files here, even though our templates could load them themself
require_once(INCLUDE_DIR . '/autoloader.inc.php'); require_once(INCLUDE_DIR . '/autoloader.inc.php');
// Command line switches
array_shift($argv);
foreach ($argv as $option) {
switch ($option) {
case '-f':
$monitoring->setStatus($cron_name . "_disabled", "yesno", 0);
break;
}
}
// Load 3rd party logging library for running crons // Load 3rd party logging library for running crons
$log = new KLogger ( 'logs/' . $cron_name . '.txt' , KLogger::INFO ); $log = new KLogger ( 'logs/' . $cron_name . '.txt' , KLogger::INFO );
$log->LogDebug('Starting ' . $cron_name); $log->LogDebug('Starting ' . $cron_name);
@ -48,8 +58,13 @@ $log->LogDebug('Starting ' . $cron_name);
// Load the start time for later runtime calculations for monitoring // Load the start time for later runtime calculations for monitoring
$cron_start[$cron_name] = microtime(true); $cron_start[$cron_name] = microtime(true);
// Check if our cron is activated
if ($monitoring->isDisabled($cron_name)) {
$log->logFatal('Cronjob is currently disabled due to errors, use -f option to force running cron.');
$monitoring->endCronjob($cron_name, 'E0018', 1, true, false);
}
// Mark cron as running for monitoring // Mark cron as running for monitoring
$log->logDebug('Marking cronjob as running for monitoring'); $log->logDebug('Marking cronjob as running for monitoring');
$monitoring->setStatus($cron_name . '_active', 'yesno', 1);
$monitoring->setStatus($cron_name . '_starttime', 'date', time()); $monitoring->setStatus($cron_name . '_starttime', 'date', time());
?> ?>

View File

@ -28,12 +28,26 @@ require_once('shared.inc.php');
// Include additional file not set in autoloader // Include additional file not set in autoloader
require_once(CLASS_DIR . '/tools.class.php'); require_once(CLASS_DIR . '/tools.class.php');
// Fetch latest coin price via API call
if ($price = $tools->getPrice()) { if ($price = $tools->getPrice()) {
$log->logInfo("Price update: found $price as price"); $log->logDebug("Price update: found $price as price");
if (!$setting->setValue('price', $price)) if (!$setting->setValue('price', $price))
$log->logError("unable to update value in settings table"); $log->logError("unable to update value in settings table");
} else { } else {
$log->logFatal("failed to fetch API data: " . $tools->getError()); $log->logError("failed to fetch API data: " . $tools->getCronError());
}
// Update Uptime Robot status in Settings table via API call
if ($api_keys = $setting->getValue('monitoring_uptimerobot_api_keys')) {
if (!strstr($api_keys, '<API KEY>|<MONITOR ID>')) {
$monitoring->setTools($tools);
if (!$monitoring->storeUptimeRobotStatus()) {
$log->logError($monitoring->getCronError());
$monitoring->endCronjob($cron_name, 'E0017', 1, false, false);
}
}
} else {
$log->logDebug('Skipped Uptime Robot API update, missing api keys');
} }
require_once('cron_end.inc.php'); require_once('cron_end.inc.php');

View File

@ -1,3 +1,4 @@
ErrorDocument 404 /index.php?page=error&action=404
RedirectMatch 404 /templates(/|$) RedirectMatch 404 /templates(/|$)
RedirectMatch 404 /include(/|$) RedirectMatch 404 /include(/|$)
RedirectMatch 404 /.git(/|$) RedirectMatch 404 /.git(/|$)

View File

@ -1,10 +1,17 @@
<?php <?php
// SHA/Scrypt check
if (empty($config['algorithm']) || $config['algorithm'] == 'scrypt') {
$config['target_bits'] = 16;
} else {
$config['target_bits'] = 32;
}
// Default classes // Default classes
require_once(CLASS_DIR . '/debug.class.php'); require_once(CLASS_DIR . '/debug.class.php');
require_once(INCLUDE_DIR . '/lib/KLogger.php'); require_once(INCLUDE_DIR . '/lib/KLogger.php');
require_once(INCLUDE_DIR . '/database.inc.php'); require_once(INCLUDE_DIR . '/database.inc.php');
require_once(INCLUDE_DIR . '/config/memcache_keys.inc.php'); require_once(INCLUDE_DIR . '/config/memcache_keys.inc.php');
require_once(INCLUDE_DIR . '/config/error_codes.inc.php');
// We need to load these two first // We need to load these two first
require_once(CLASS_DIR . '/base.class.php'); require_once(CLASS_DIR . '/base.class.php');
@ -44,8 +51,8 @@ require_once(CLASS_DIR . '/invitation.class.php');
require_once(CLASS_DIR . '/share.class.php'); require_once(CLASS_DIR . '/share.class.php');
require_once(CLASS_DIR . '/worker.class.php'); require_once(CLASS_DIR . '/worker.class.php');
require_once(CLASS_DIR . '/statistics.class.php'); require_once(CLASS_DIR . '/statistics.class.php');
require_once(CLASS_DIR . '/roundstats.class.php');
require_once(CLASS_DIR . '/transaction.class.php'); require_once(CLASS_DIR . '/transaction.class.php');
require_once(CLASS_DIR . '/roundstats.class.php');
require_once(CLASS_DIR . '/notification.class.php'); require_once(CLASS_DIR . '/notification.class.php');
require_once(CLASS_DIR . '/news.class.php'); require_once(CLASS_DIR . '/news.class.php');
require_once(CLASS_DIR . '/api.class.php'); require_once(CLASS_DIR . '/api.class.php');

View File

@ -4,12 +4,21 @@
if (!defined('SECURITY')) if (!defined('SECURITY'))
die('Hacking attempt'); die('Hacking attempt');
// Our base class that defines /**
// some cross-class functions. * Our base class that we extend our other classes from
*
* It supplies some basic features as cross-linking with other classes
* after loading a newly created class.
**/
class Base { class Base {
private $sError = ''; private $sError = '';
private $sCronError = '';
protected $table = '';
private $values = array(), $types = ''; private $values = array(), $types = '';
public function getTableName() {
return $this->table;
}
public function setDebug($debug) { public function setDebug($debug) {
$this->debug = $debug; $this->debug = $debug;
} }
@ -19,6 +28,9 @@ class Base {
public function setMail($mail) { public function setMail($mail) {
$this->mail = $mail; $this->mail = $mail;
} }
public function setSalt($salt) {
$this->salt = $salt;
}
public function setSmarty($smarty) { public function setSmarty($smarty) {
$this->smarty = $smarty; $this->smarty = $smarty;
} }
@ -28,39 +40,106 @@ class Base {
public function setConfig($config) { public function setConfig($config) {
$this->config = $config; $this->config = $config;
} }
public function setErrorCodes(&$aErrorCodes) {
$this->aErrorCodes =& $aErrorCodes;
}
public function setToken($token) { public function setToken($token) {
$this->token = $token; $this->token = $token;
} }
public function setBlock($block) { public function setBlock($block) {
$this->block = $block; $this->block = $block;
} }
public function setTransaction($transaction) {
$this->transaction = $transaction;
}
public function setMemcache($memcache) {
$this->memcache = $memcache;
}
public function setStatistics($statistics) {
$this->statistics = $statistics;
}
public function setSetting($setting) { public function setSetting($setting) {
$this->setting = $setting; $this->setting = $setting;
} }
public function setTools($tools) {
$this->tools = $tools;
}
public function setBitcoin($bitcoin) { public function setBitcoin($bitcoin) {
$this->bitcoin = $bitcoin; $this->bitcoin = $bitcoin;
} }
public function setTokenType($tokentype) { public function setTokenType($tokentype) {
$this->tokentype = $tokentype; $this->tokentype = $tokentype;
} }
public function setShare($share) {
$this->share = $share;
}
public function setErrorMessage($msg) { public function setErrorMessage($msg) {
$this->sError = $msg; $this->sError = $msg;
// Default to same error for crons
$this->sCronError = $msg;
}
public function setCronMessage($msg) {
// Used to overwrite any errors with a custom cron one
$this->sCronError = $msg;
} }
public function getError() { public function getError() {
return $this->sError; return $this->sError;
} }
/**
* Additional information in error string for cronjobs logging
**/
public function getCronError() {
return $this->sCronError;
}
/**
* Get error message from error code array
* @param errCode string Error code string
* @param optional string Optional addtitional error strings to append
* @retrun string Error Message
**/
public function getErrorMsg($errCode='') {
if (!is_array($this->aErrorCodes)) return 'Error codes not loaded';
if (!array_key_exists($errCode, $this->aErrorCodes)) return 'Unknown Error Code: ' . $errCode;
if (func_num_args() > 1) {
$args = func_get_args();
array_shift($args);
$param_count = substr_count($this->aErrorCodes[$errCode], '%s');
if ($param_count == count($args)) {
return vsprintf($this->aErrorCodes[$errCode], $args);
} else {
return $this->aErrorCodes[$errCode] . ' (missing information to complete string)';
}
} else {
return $this->aErrorCodes[$errCode];
}
}
/**
* Get an element as an associated array
**/
protected function getAllAssoc($value, $field='id', $type='i') {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE $field = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param($type, $value) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc();
return false;
}
/** /**
* Get a single row from the table * Get a single row from the table
* @param value string Value to search for * @param value string Value to search for
* @param search Return column to search for * @param search Return column to search for
* @param field string Search column * @param field string Search column
* @param type string Type of value * @param type string Type of value
* @param lower bool try with LOWER comparision
* @return array Return result * @return array Return result
**/ **/
protected function getSingle($value, $search='id', $field='id', $type="i") { protected function getSingle($value, $search='id', $field='id', $type="i", $lower=false) {
$this->debug->append("STA " . __METHOD__, 4); $this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("SELECT $search FROM $this->table WHERE $field = ? LIMIT 1"); $sql = "SELECT $search FROM $this->table WHERE";
$lower ? $sql .= " LOWER($field) = LOWER(?)" : $sql .= " $field = ?";
$sql .= " LIMIT 1";
$stmt = $this->mysqli->prepare($sql);
if ($this->checkStmt($stmt)) { if ($this->checkStmt($stmt)) {
$stmt->bind_param($type, $value); $stmt->bind_param($type, $value);
$stmt->execute(); $stmt->execute();
@ -72,18 +151,38 @@ class Base {
return false; return false;
} }
/**
* Check if the prepared statement is valid
* @param $bState Statement return value
* @return bool true or false
**/
function checkStmt($bState) { function checkStmt($bState) {
$this->debug->append("STA " . __METHOD__, 4); $this->debug->append("STA " . __METHOD__, 4);
if ($bState ===! true) { if ($bState ===! true)
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error); return $this->sqlError();
$this->setErrorMessage('Internal application Error');
return false;
}
return true; return true;
} }
/**
* Catch SQL errors with this method
* @param error_code string Error code to read
**/
protected function sqlError($error_code='E0020') {
// More human-readable error for UI
if (func_num_args() == 0) {
$this->setErrorMessage($this->getErrorMsg($error_code));
} else {
$this->setErrorMessage(call_user_func_array(array($this, 'getErrorMsg'), func_get_args()));
}
// Default to SQL error for debug and cron errors
$this->debug->append($this->getErrorMsg('E0019', $this->mysqli->error));
$this->setCronMessage($this->getErrorMsg('E0019', $this->mysqli->error));
return false;
}
/** /**
* Update a single row in a table
* @param userID int Account ID * @param userID int Account ID
* Update a single row in a table
* @param field string Field to update * @param field string Field to update
* @return bool * @return bool
**/ **/
@ -94,7 +193,7 @@ class Base {
if ($this->checkStmt($stmt) && $stmt->bind_param($field['type'].'i', $field['value'], $id) && $stmt->execute()) if ($this->checkStmt($stmt) && $stmt->bind_param($field['type'].'i', $field['value'], $id) && $stmt->execute())
return true; return true;
$this->debug->append("Unable to update " . $field['name'] . " with " . $field['value'] . " for ID $id"); $this->debug->append("Unable to update " . $field['name'] . " with " . $field['value'] . " for ID $id");
return false; $this->sqlError();
} }
/** /**

View File

@ -22,6 +22,12 @@ class BitcoinWrapper extends BitcoinClient {
/** /**
* Wrap variouns methods to add caching * Wrap variouns methods to add caching
**/ **/
// Caching this, used for each can_connect call
public function getinfo() {
$this->oDebug->append("STA " . __METHOD__, 4);
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
return $this->memcache->setCache(__FUNCTION__, parent::getinfo(), 30);
}
public function getblockcount() { public function getblockcount() {
$this->oDebug->append("STA " . __METHOD__, 4); $this->oDebug->append("STA " . __METHOD__, 4);
if ($data = $this->memcache->get(__FUNCTION__)) return $data; if ($data = $this->memcache->get(__FUNCTION__)) return $data;
@ -48,15 +54,21 @@ class BitcoinWrapper extends BitcoinClient {
if ($data = $this->memcache->get(__FUNCTION__)) return $data; if ($data = $this->memcache->get(__FUNCTION__)) return $data;
try { try {
$dNetworkHashrate = $this->query('getmininginfo'); $dNetworkHashrate = $this->query('getmininginfo');
if (is_array($dNetworkHashrate) && array_key_exists('networkhashps', $dNetworkHashrate)) { if (is_array($dNetworkHashrate)) {
$dNetworkHashrate = $dNetworkHashrate['networkhashps']; if (array_key_exists('networkhashps', $dNetworkHashrate)) {
} else if (is_array($dNetworkHashrate) && array_key_exists('hashespersec', $dNetworkHashrate)) { $dNetworkHashrate = $dNetworkHashrate['networkhashps'];
$dNetworkHashrate = $dNetworkHashrate['hashespersec']; } else if (array_key_exists('hashespersec', $dNetworkHashrate)) {
} else if (is_array($dNetworkHashrate) && array_key_exists('netmhashps', $dNetworkHashrate)) { $dNetworkHashrate = $dNetworkHashrate['hashespersec'];
$dNetworkHashrate = $dNetworkHashrate['netmhashps'] * 1000 * 1000; } else if (array_key_exists('netmhashps', $dNetworkHashrate)) {
$dNetworkHashrate = $dNetworkHashrate['netmhashps'] * 1000 * 1000;
} else {
// Unsupported implementation
$dNetworkHashrate = 0;
}
} }
} catch (Exception $e) { } catch (Exception $e) {
return false; // getmininginfo does not exist, cache for an hour
return $this->memcache->setCache(__FUNCTION__, 0, 3600);
} }
return $this->memcache->setCache(__FUNCTION__, $dNetworkHashrate, 30); return $this->memcache->setCache(__FUNCTION__, $dNetworkHashrate, 30);
} }

View File

@ -1,32 +1,10 @@
<?php <?php
// Make sure we are called from index.php // Make sure we are called from index.php
if (!defined('SECURITY')) if (!defined('SECURITY')) die('Hacking attempt');
die('Hacking attempt');
class Block { class Block extends Base {
private $sError = ''; protected $table = 'blocks';
private $table = 'blocks';
// This defines each block
public $height, $blockhash, $confirmations, $time, $accounted;
public function __construct($debug, $mysqli, $config) {
$this->debug = $debug;
$this->mysqli = $mysqli;
$this->config = $config;
$this->debug->append("Instantiated Block class", 2);
}
// get and set methods
private function setErrorMessage($msg) {
$this->sError = $msg;
}
public function getError() {
return $this->sError;
}
public function getTableName() {
return $this->table;
}
/** /**
* Specific method to fetch the latest block found * Specific method to fetch the latest block found
@ -35,13 +13,9 @@ class Block {
**/ **/
public function getLast() { public function getLast() {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table ORDER BY height DESC LIMIT 1"); $stmt = $this->mysqli->prepare("SELECT * FROM $this->table ORDER BY height DESC LIMIT 1");
if ($this->checkStmt($stmt)) { if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
$stmt->execute();
$result = $stmt->get_result();
$stmt->close();
return $result->fetch_assoc(); return $result->fetch_assoc();
} return $this->sqlError();
return false;
} }
/** /**
@ -53,7 +27,31 @@ class Block {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE height = ? LIMIT 1"); $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE height = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $height) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param('i', $height) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc(); return $result->fetch_assoc();
return false; return $this->sqlError();
}
/**
* Get a specific block, by share_id
* @param share_id int Blocks share_id
* @return data array Block information from DB
**/
public function getBlockByShareId($share_id) {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE share_id = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $share_id) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc();
return $this->sqlError();
}
/**
* Get a specific block, by id
* @param share_id int Blocks share_id
* @return data array Block information from DB
**/
public function getBlockById($id) {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE id = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $id) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc();
return $this->sqlError();
} }
/** /**
@ -65,7 +63,7 @@ class Block {
$stmt = $this->mysqli->prepare("SELECT MAX(share_id) AS share_id FROM $this->table LIMIT 1"); $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()) if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->share_id; return $result->fetch_object()->share_id;
return false; return $this->sqlError();
} }
/** /**
@ -77,7 +75,7 @@ class Block {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE ISNULL(share_id) ORDER BY height $order"); $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE ISNULL(share_id) ORDER BY height $order");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
return false; return $this->sqlError();
} }
/** /**
@ -89,7 +87,7 @@ class Block {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE accounted = 0 ORDER BY height $order"); $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE accounted = 0 ORDER BY height $order");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
return false; return $this->sqlError();
} }
/** /**
@ -101,7 +99,7 @@ class Block {
$stmt = $this->mysqli->prepare("SELECT COUNT(id) AS blocks FROM $this->table"); $stmt = $this->mysqli->prepare("SELECT COUNT(id) AS blocks FROM $this->table");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return (int)$result->fetch_object()->blocks; return (int)$result->fetch_object()->blocks;
return false; return $this->sqlError();
} }
/** /**
@ -113,7 +111,7 @@ class Block {
$stmt = $this->mysqli->prepare("SELECT AVG(x.shares) AS average FROM (SELECT shares FROM $this->table WHERE height <= ? ORDER BY height DESC LIMIT ?) AS x"); $stmt = $this->mysqli->prepare("SELECT AVG(x.shares) AS average FROM (SELECT shares FROM $this->table WHERE height <= ? ORDER BY height DESC LIMIT ?) AS x");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $height, $limit) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $height, $limit) && $stmt->execute() && $result = $stmt->get_result())
return (float)$result->fetch_object()->average; return (float)$result->fetch_object()->average;
return false; return $this->sqlError();
} }
/** /**
@ -125,7 +123,7 @@ class Block {
$stmt = $this->mysqli->prepare("SELECT AVG(x.amount) AS average FROM (SELECT amount FROM $this->table ORDER BY height DESC LIMIT ?) AS x"); $stmt = $this->mysqli->prepare("SELECT AVG(x.amount) AS average FROM (SELECT amount FROM $this->table ORDER BY height DESC LIMIT ?) AS x");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $limit) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param('i', $limit) && $stmt->execute() && $result = $stmt->get_result())
return (float)$result->fetch_object()->average; return (float)$result->fetch_object()->average;
return false; return $this->sqlError();
} }
/** /**
@ -137,7 +135,7 @@ class Block {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE confirmations < ? AND confirmations > -1"); $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE confirmations < ? AND confirmations > -1");
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $confirmations) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param("i", $confirmations) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
return false; return $this->sqlError();
} }
/** /**
@ -148,13 +146,9 @@ class Block {
**/ **/
public function setConfirmations($block_id, $confirmations) { public function setConfirmations($block_id, $confirmations) {
$stmt = $this->mysqli->prepare("UPDATE $this->table SET confirmations = ? WHERE id = ?"); $stmt = $this->mysqli->prepare("UPDATE $this->table SET confirmations = ? WHERE id = ?");
if ($this->checkStmt($stmt)) { if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $confirmations, $block_id) && $stmt->execute())
$stmt->bind_param("ii", $confirmations, $block_id) or die($stmt->error);
$stmt->execute() or die("Failed");
$stmt->close();
return true; return true;
} return $this->sqlError();
return false;
} }
/** /**
@ -164,13 +158,9 @@ class Block {
**/ **/
public function getAll($order='DESC') { public function getAll($order='DESC') {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table ORDER BY height $order"); $stmt = $this->mysqli->prepare("SELECT * FROM $this->table ORDER BY height $order");
if ($this->checkStmt($stmt)) { if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
$stmt->execute();
$result = $stmt->get_result();
$stmt->close();
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
} return $this->sqlError();
return false;
} }
/** /**
@ -180,50 +170,21 @@ class Block {
**/ **/
public function addBlock($block) { public function addBlock($block) {
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (height, blockhash, confirmations, amount, difficulty, time) VALUES (?, ?, ?, ?, ?, ?)"); $stmt = $this->mysqli->prepare("INSERT INTO $this->table (height, blockhash, confirmations, amount, difficulty, time) VALUES (?, ?, ?, ?, ?, ?)");
if ($this->checkStmt($stmt)) { if ($this->checkStmt($stmt) && $stmt->bind_param('isiddi', $block['height'], $block['blockhash'], $block['confirmations'], $block['amount'], $block['difficulty'], $block['time']) && $stmt->execute())
$stmt->bind_param('isiddi', $block['height'], $block['blockhash'], $block['confirmations'], $block['amount'], $block['difficulty'], $block['time']);
if (!$stmt->execute()) {
$this->debug->append("Failed to execute statement: " . $stmt->error);
$this->setErrorMessage($stmt->error);
$stmt->close();
return false;
}
$stmt->close();
return true; return true;
} return $this->sqlError();
return false;
}
public function getLastUpstreamId() {
$stmt = $this->mysqli->prepare("
SELECT MAX(share_id) AS share_id FROM $this->table
");
if ($this->checkStmt($stmt) && $stmt->execute() && $stmt->bind_result($share_id) && $stmt->fetch())
return $share_id ? $share_id : 0;
// Catchall
return false;
} }
/** /**
* Update a single column within a single row * Get our last inserted upstream ID from table
* @param block_id int Block ID to update * @param none
* @param field string Column name to update * @return mixed upstream ID or 0, false on error
* @param value string Value to insert
* @return bool
**/ **/
private function updateSingle($block_id, $field, $value) { public function getLastUpstreamId() {
$stmt = $this->mysqli->prepare("UPDATE $this->table SET $field = ? WHERE id = ?"); $stmt = $this->mysqli->prepare("SELECT MAX(share_id) AS share_id FROM $this->table");
if ($this->checkStmt($stmt)) { if ($this->checkStmt($stmt) && $stmt->execute() && $stmt->bind_result($share_id) && $stmt->fetch())
$stmt->bind_param('ii', $value, $block_id); return $share_id ? $share_id : 0;
if (!$stmt->execute()) { return $this->sqlError();
$this->debug->append("Failed to update block ID $block_id with finder ID $account_id");
$stmt->close();
return false;
}
$stmt->close();
return true;
}
return false;
} }
/** /**
@ -233,7 +194,19 @@ class Block {
* @return bool * @return bool
**/ **/
public function setFinder($block_id, $account_id=NULL) { public function setFinder($block_id, $account_id=NULL) {
return $this->updateSingle($block_id, 'account_id', $account_id); $field = array( 'name' => 'account_id', 'value' => $account_id, 'type' => 'i' );
return $this->updateSingle($block_id, $field);
}
/**
* Set finding worker of a block
* @param block_id int Block ID
* @param worker_id int Worker ID of finder
* @return bool
**/
public function setFindingWorker($block_id, $worker=NULL) {
$field = array( 'name' => 'worker_name', 'value' => $worker, 'type' => 's' );
return $this->updateSingle($block_id, $field);
} }
/** /**
@ -243,7 +216,8 @@ class Block {
* @return bool * @return bool
**/ **/
public function setShareId($block_id, $share_id) { public function setShareId($block_id, $share_id) {
return $this->updateSingle($block_id, 'share_id', $share_id); $field = array( 'name' => 'share_id', 'value' => $share_id, 'type' => 'i');
return $this->updateSingle($block_id, $field);
} }
/** /**
@ -253,7 +227,8 @@ class Block {
* @return bool * @return bool
**/ **/
public function setShares($block_id, $shares=NULL) { public function setShares($block_id, $shares=NULL) {
return $this->updateSingle($block_id, 'shares', $shares); $field = array( 'name' => 'shares', 'value' => $shares, 'type' => 'i');
return $this->updateSingle($block_id, $field);
} }
/** /**
@ -263,21 +238,14 @@ class Block {
**/ **/
public function setAccounted($block_id=NULL) { public function setAccounted($block_id=NULL) {
if (empty($block_id)) return false; if (empty($block_id)) return false;
return $this->updateSingle($block_id, 'accounted', 1); $field = array( 'name' => 'accounted', 'value' => 1, 'type' => 'i');
} return $this->updateSingle($block_id, $field);
/**
* Helper function
**/
private function checkStmt($bState) {
if ($bState ===! true) {
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error);
$this->setErrorMessage('Internal application Error');
return false;
}
return true;
} }
} }
// Automatically load our class for furhter usage // Automatically load our class for furhter usage
$block = new Block($debug, $mysqli, $config); $block = new Block();
$block->setDebug($debug);
$block->setMysql($mysqli);
$block->setConfig($config);
$block->setErrorCodes($aErrorCodes);

View File

@ -16,9 +16,7 @@ class Invitation extends Base {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE account_id = ?"); $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE account_id = ?");
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result()) if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
$this->setErrorMessage('Unable to fetch invitiations send from your account'); $this->sqlError('E0021');
$this->debug->append('Failed to fetch invitations from database: ' . $this->mysqli->errro);
return false;
} }
/** /**
@ -31,9 +29,7 @@ class Invitation extends Base {
$stmt = $this->mysqli->prepare("SELECT count(id) AS total FROM $this->table WHERE account_id = ?"); $stmt = $this->mysqli->prepare("SELECT count(id) AS total FROM $this->table WHERE account_id = ?");
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $stmt->bind_result($total) && $stmt->fetch()) if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $stmt->bind_result($total) && $stmt->fetch())
return $total; return $total;
$this->setErrorMessage('Unable to fetch invitiations send from your account'); $this->sqlError('E0021');
$this->debug->append('Failed to fetch invitations from database: ' . $this->mysqli->errro);
return false;
} }
/** /**
@ -65,7 +61,7 @@ class Invitation extends Base {
**/ **/
public function setActivated($token_id) { public function setActivated($token_id) {
if (!$iInvitationId = $this->getByTokenId($token_id)) { if (!$iInvitationId = $this->getByTokenId($token_id)) {
$this->setErrorMessage('Unable to convert token ID to invitation ID'); $this->setErrorMessage($this->getErrorMsg('E0030'));
return false; return false;
} }
$field = array('name' => 'is_activated', 'type' => 'i', 'value' => 1); $field = array('name' => 'is_activated', 'type' => 'i', 'value' => 1);
@ -84,8 +80,9 @@ class Invitation extends Base {
$stmt = $this->mysqli->prepare("INSERT INTO $this->table ( account_id, email, token_id ) VALUES ( ?, ?, ?)"); $stmt = $this->mysqli->prepare("INSERT INTO $this->table ( account_id, email, token_id ) VALUES ( ?, ?, ?)");
if ($stmt && $stmt->bind_param('isi', $account_id, $email, $token_id) && $stmt->execute()) if ($stmt && $stmt->bind_param('isi', $account_id, $email, $token_id) && $stmt->execute())
return true; return true;
return false; $this->sqlError('E0022');
} }
/** /**
* Send an invitation out to a user * Send an invitation out to a user
* Uses the mail class to send mails * Uses the mail class to send mails
@ -97,39 +94,37 @@ class Invitation extends Base {
$this->debug->append("STA " . __METHOD__, 4); $this->debug->append("STA " . __METHOD__, 4);
// Check data input // Check data input
if (empty($aData['email']) || !filter_var($aData['email'], FILTER_VALIDATE_EMAIL)) { if (empty($aData['email']) || !filter_var($aData['email'], FILTER_VALIDATE_EMAIL)) {
$this->setErrorMessage( 'Invalid e-mail address' ); $this->setErrorMessage($this->getErrorMsg('E0023'));
return false; return false;
} }
if (preg_match('/[^a-z_\.\!\?\-0-9 ]/i', $aData['message'])) { if (preg_match('/[^a-z_\.\!\?\-0-9 ]/i', $aData['message'])) {
$this->setErrorMessage('Message may only contain alphanumeric characters'); $this->setErrorMessage($this->getErrorMsg('E0024'));
return false; return false;
} }
// Ensure this invitation does not exist yet nor do we have an account with that email // Ensure this invitation does not exist yet nor do we have an account with that email
if ($this->user->getEmail($aData['email'])) { if ($this->user->getEmail($aData['email'])) {
$this->setErrorMessage('This email is already registered as an account'); $this->setErrorMessage($this->getErrorMsg('E0025'));
return false; return false;
} }
if ($this->getByEmail($aData['email'])) { if ($this->getByEmail($aData['email'])) {
$this->setErrorMessage('A pending invitation for this address already exists'); $this->setErrorMessage($this->getErrorMsg('E0026'));
return false; return false;
} }
if (!$aData['token'] = $this->token->createToken('invitation', $account_id)) { if (!$aData['token'] = $this->token->createToken('invitation', $account_id)) {
$this->setErrorMessage('Unable to generate invitation token: ' . $this->token->getError()); $this->setErrorMessage($this->getErrorMsg('E0027', $this->token->getError()));
return false; return false;
} }
$aData['username'] = $this->user->getUserName($account_id); $aData['username'] = $this->user->getUserName($account_id);
$aData['subject'] = 'Pending Invitation'; $aData['subject'] = 'Pending Invitation';
if ($this->mail->sendMail('invitations/body', $aData)) { if ($this->mail->sendMail('invitations/body', $aData)) {
$aToken = $this->token->getToken($aData['token']); $aToken = $this->token->getToken($aData['token']);
if (!$this->createInvitation($account_id, $aData['email'], $aToken['id'])) { if (!$this->createInvitation($account_id, $aData['email'], $aToken['id']))
$this->setErrorMessage('Unable to create invitation record');
return false; return false;
}
return true; return true;
} else { } else {
$this->setErrorMessage('Unable to send email to recipient'); $this->setErrorMessage($this->getErrorMsg('E0028'));
} }
$this->setErrorMessage('Unable to send invitation'); $this->setErrorMessage($this->getErrorMsg('E0029'));
return false; return false;
} }
} }
@ -142,5 +137,5 @@ $invitation->setMail($mail);
$invitation->setUser($user); $invitation->setUser($user);
$invitation->setToken($oToken); $invitation->setToken($oToken);
$invitation->setConfig($config); $invitation->setConfig($config);
$invitation->setErrorCodes($aErrorCodes);
?> ?>

View File

@ -5,16 +5,6 @@ if (!defined('SECURITY'))
die('Hacking attempt'); die('Hacking attempt');
class Mail extends Base { class Mail extends Base {
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;
}
/** /**
* Mail form contact site admin * Mail form contact site admin
* @param senderName string senderName * @param senderName string senderName
@ -28,19 +18,19 @@ class Mail extends Base {
public function contactform($senderName, $senderEmail, $senderSubject, $senderMessage) { public function contactform($senderName, $senderEmail, $senderSubject, $senderMessage) {
$this->debug->append("STA " . __METHOD__, 4); $this->debug->append("STA " . __METHOD__, 4);
if (preg_match('/[^a-z_\.\!\?\-0-9\\s ]/i', $senderName)) { if (preg_match('/[^a-z_\.\!\?\-0-9\\s ]/i', $senderName)) {
$this->setErrorMessage('Username may only contain alphanumeric characters'); $this->setErrorMessage($this->getErrorMsg('E0024'));
return false; return false;
} }
if (empty($senderEmail) || !filter_var($senderEmail, FILTER_VALIDATE_EMAIL)) { if (empty($senderEmail) || !filter_var($senderEmail, FILTER_VALIDATE_EMAIL)) {
$this->setErrorMessage( 'Invalid e-mail address' ); $this->setErrorMessage($this->getErrorMsg('E0023'));
return false; return false;
} }
if (preg_match('/[^a-z_\.\!\?\-0-9\\s ]/i', $senderSubject)) { if (preg_match('/[^a-z_\.\!\?\-0-9\\s ]/i', $senderSubject)) {
$this->setErrorMessage('Subject may only contain alphanumeric characters'); $this->setErrorMessage($this->getErrorMsg('E0034'));
return false; return false;
} }
if (strlen(strip_tags($senderMessage)) < strlen($senderMessage)) { if (strlen(strip_tags($senderMessage)) < strlen($senderMessage)) {
$this->setErrorMessage('Your message may only contain alphanumeric characters'); $this->setErrorMessage($this->getErrorMsg('E0024'));
return false; return false;
} }
$aData['senderName'] = $senderName; $aData['senderName'] = $senderName;
@ -58,16 +48,28 @@ class Mail extends Base {
return false; return false;
} }
/**
* Send a mail with templating via Smarty
* @param template string Template name within the mail folder, no extension
* @param aData array Data array with some required fields
* SUBJECT : Mail Subject
* email : Destination address
**/
public function sendMail($template, $aData) { public function sendMail($template, $aData) {
// Make sure we don't load a cached filed
$this->smarty->clearCache(BASEPATH . 'templates/mail/' . $template . '.tpl');
$this->smarty->clearCache(BASEPATH . 'templates/mail/subject.tpl');
$this->smarty->assign('WEBSITENAME', $this->setting->getValue('website_name')); $this->smarty->assign('WEBSITENAME', $this->setting->getValue('website_name'));
$this->smarty->assign('SUBJECT', $aData['subject']); $this->smarty->assign('SUBJECT', $aData['subject']);
$this->smarty->assign('DATA', $aData); $this->smarty->assign('DATA', $aData);
$headers = 'From: Website Administration <' . $this->setting->getValue('website_email') . ">\n"; $headers = 'From: Website Administration <' . $this->setting->getValue('website_email') . ">\n";
$headers .= "MIME-Version: 1.0\n"; $headers .= "MIME-Version: 1.0\n";
$headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n"; $headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n";
if (strlen(@$aData['senderName']) > 0 && @strlen($aData['senderEmail']) > 0 )
$headers .= 'Reply-To: ' . $aData['senderName'] . ' <' . $aData['senderEmail'] . ">\n";
if (mail($aData['email'], $this->smarty->fetch(BASEPATH . 'templates/mail/subject.tpl'), $this->smarty->fetch(BASEPATH . 'templates/mail/' . $template . '.tpl'), $headers)) if (mail($aData['email'], $this->smarty->fetch(BASEPATH . 'templates/mail/subject.tpl'), $this->smarty->fetch(BASEPATH . 'templates/mail/' . $template . '.tpl'), $headers))
return true; return true;
$this->setErrorMessage('Unable to send mail'); $this->setErrorMessage($this->sqlError('E0031'));
return false; return false;
} }
} }
@ -79,4 +81,5 @@ $mail->setMysql($mysqli);
$mail->setSmarty($smarty); $mail->setSmarty($smarty);
$mail->setConfig($config); $mail->setConfig($config);
$mail->setSetting($setting); $mail->setSetting($setting);
$mail->setErrorCodes($aErrorCodes);
?> ?>

View File

@ -1,14 +1,65 @@
<?php <?php
// Make sure we are called from index.php // Make sure we are called from index.php
if (!defined('SECURITY')) if (!defined('SECURITY')) die('Hacking attempt');
die('Hacking attempt');
class Monitoring { class Monitoring extends Base {
public function __construct($debug, $mysqli) { protected $table = 'monitoring';
$this->debug = $debug;
$this->mysqli = $mysqli; /**
$this->table = 'monitoring'; * Store Uptime Robot status information as JSON in settings table
* @param none
* @return bool true on success, false on error
**/
public function storeUptimeRobotStatus() {
if ($api_keys = $this->setting->getValue('monitoring_uptimerobot_api_keys')) {
$aJSONData = array();
$url = 'http://api.uptimerobot.com';
$aMonitors = explode(',', $api_keys);
foreach ($aMonitors as $aData) {
$temp = explode('|', $aData);
$aMonitor['api_key'] = $temp[0];
$aMonitor['monitor_id'] = $temp[1];
$target = '/getMonitors?apiKey=' . $aMonitor['api_key'] . '&monitors=' . $aMonitor['monitor_id'] . '&format=json&noJsonCallback=1&customUptimeRatio=1-7-30&logs=1';
$aMonitorStatus = $this->tools->getApi($url, $target);
if (!$aMonitorStatus || @$aMonitorStatus['stat'] == 'fail') {
if (is_array($aMonitorStatus) && array_key_exists('message', @$aMonitorStatus)) {
$this->setErrorMessage($this->getErrorMsg('E0032', $aMonitorStatus['message']));
} else {
$this->setErrorMessage($this->getErrorMsg('E0032', $this->tools->getError()));
}
return false;
}
$aMonitorStatus['monitors']['monitor'][0]['customuptimeratio'] = explode('-', $aMonitorStatus['monitors']['monitor'][0]['customuptimeratio']);
$aAllMonitorsStatus[] = $aMonitorStatus['monitors']['monitor'][0];
}
if (!$this->setting->setValue('monitoring_uptimerobot_status', json_encode($aAllMonitorsStatus)) || !$this->setting->setValue('monitoring_uptimerobot_lastcheck', time())) {
$this->setErrorMessage($this->getErrorMsg('E0033'), $setting->getError());
return false;
}
}
return true;
}
/**
* Fetch Uptime Robot Status from settings table
* @param none
* @return array Data on success, false on failure
**/
public function getUptimeRobotStatus() {
if ($json = $this->setting->getValue('monitoring_uptimerobot_status'))
return json_decode($json, true);
return false;
}
/**
* Check that our cron is currently activated
* @param name string Cronjob name
* @return bool true or false
**/
public function isDisabled($name) {
$aStatus = $this->getStatus($name . '_disabled');
return $aStatus['value'];
} }
/** /**
@ -21,8 +72,7 @@ class Monitoring {
if ($query && $query->bind_param('s', $name) && $query->execute() && $result = $query->get_result()) { if ($query && $query->bind_param('s', $name) && $query->execute() && $result = $query->get_result()) {
return $result->fetch_assoc(); return $result->fetch_assoc();
} else { } else {
$this->debug->append("Failed to fetch variable $name from $this->table"); $this->sqlError();
return false;
} }
return $value; return $value;
} }
@ -44,6 +94,41 @@ class Monitoring {
$this->debug->append("Failed to set $name to $value"); $this->debug->append("Failed to set $name to $value");
return false; return false;
} }
/**
* End cronjob with an error message
* @param cron_name string Cronjob Name
* @param msgCode string Message code as stored in error_codes array
* @param exitCode int Exit code to pass on to exit function and monitor report
* @param fatal boolean Should we exit out entirely
* @return none
**/
public function endCronjob($cron_name, $msgCode, $exitCode=0, $fatal=false, $mail=true) {
$this->setStatus($cron_name . "_active", "yesno", 0);
$this->setStatus($cron_name . "_message", "message", $this->getErrorMsg($msgCode));
$this->setStatus($cron_name . "_status", "okerror", $exitCode);
$this->setStatus($cron_name . "_endtime", "date", time());
if ($mail) {
$aMailData = array(
'email' => $this->setting->getValue('system_error_email'),
'subject' => 'Cronjob Failure',
'Error Code' => $msgCode,
'Error Message' => $this->getErrorMsg($msgCode)
);
if (!$this->mail->sendMail('notifications/error', $aMailData))
$this->setErrorMessage('Failed to send mail notification');
}
if ($fatal) {
if ($exitCode != 0) $this->setStatus($cron_name . "_disabled", "yesno", 1);
exit($exitCode);
}
}
} }
$monitoring = new Monitoring($debug, $mysqli); $monitoring = new Monitoring();
$monitoring->setErrorCodes($aErrorCodes);
$monitoring->setConfig($config);
$monitoring->setDebug($debug);
$monitoring->setMail($mail);
$monitoring->setMysql($mysqli);
$monitoring->setSetting($setting);

View File

@ -5,13 +5,23 @@ if (!defined('SECURITY'))
die('Hacking attempt'); die('Hacking attempt');
class News extends Base { class News extends Base {
var $table = 'news'; protected $table = 'news';
/**
* Get activation status of post
* @param id int News ID
* @return bool true or false
**/
public function getActive($id) { public function getActive($id) {
$this->debug->append("STA " . __METHOD__, 5); $this->debug->append("STA " . __METHOD__, 5);
return $this->getSingle($id, 'active', 'id'); return $this->getSingle($id, 'active', 'id');
} }
/**
* Switch activation status
* @param id int News ID
* @return bool true or false
**/
public function toggleActive($id) { public function toggleActive($id) {
$this->debug->append("STA " . __METHOD__, 5); $this->debug->append("STA " . __METHOD__, 5);
$field = array('name' => 'active', 'type' => 'i', 'value' => !$this->getActive($id)); $field = array('name' => 'active', 'type' => 'i', 'value' => !$this->getActive($id));
@ -26,8 +36,7 @@ class News extends Base {
$stmt = $this->mysqli->prepare("SELECT n.*, a.username AS author FROM $this->table AS n LEFT JOIN " . $this->user->getTableName() . " AS a ON a.id = n.account_id WHERE active = 1 ORDER BY time DESC"); $stmt = $this->mysqli->prepare("SELECT n.*, a.username AS author FROM $this->table AS n LEFT JOIN " . $this->user->getTableName() . " AS a ON a.id = n.account_id WHERE active = 1 ORDER BY time DESC");
if ($stmt && $stmt->execute() && $result = $stmt->get_result()) if ($stmt && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
// Catchall return $this->sqlError('E0040');
return false;
} }
/** /**
@ -38,8 +47,7 @@ class News extends Base {
$stmt = $this->mysqli->prepare("SELECT n.*, a.username AS author FROM $this->table AS n LEFT JOIN " . $this->user->getTableName() . " AS a ON a.id = n.account_id ORDER BY time DESC"); $stmt = $this->mysqli->prepare("SELECT n.*, a.username AS author FROM $this->table AS n LEFT JOIN " . $this->user->getTableName() . " AS a ON a.id = n.account_id ORDER BY time DESC");
if ($stmt && $stmt->execute() && $result = $stmt->get_result()) if ($stmt && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
// Catchall return $this->sqlError('E0039');
return false;
} }
/** /**
@ -50,8 +58,7 @@ class News extends Base {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE id = ?"); $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE id = ?");
if ($stmt && $stmt->bind_param('i', $id) && $stmt->execute() && $result = $stmt->get_result()) if ($stmt && $stmt->bind_param('i', $id) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc(); return $result->fetch_assoc();
// Catchall return $this->sqlError('E0038');
return false;
} }
/** /**
@ -62,8 +69,7 @@ class News extends Base {
$stmt = $this->mysqli->prepare("UPDATE $this->table SET content = ?, header = ?, active = ? WHERE id = ?"); $stmt = $this->mysqli->prepare("UPDATE $this->table SET content = ?, header = ?, active = ? WHERE id = ?");
if ($stmt && $stmt->bind_param('ssii', $content, $header, $active, $id) && $stmt->execute() && $stmt->affected_rows == 1) if ($stmt && $stmt->bind_param('ssii', $content, $header, $active, $id) && $stmt->execute() && $stmt->affected_rows == 1)
return true; return true;
$this->setErrorMessage("Failed to update news entry $id"); return $this->sqlError('E0037');
return false;
} }
public function deleteNews($id) { public function deleteNews($id) {
@ -72,8 +78,7 @@ class News extends Base {
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id = ?"); $stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id = ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $id) && $stmt->execute() && $stmt->affected_rows == 1) if ($this->checkStmt($stmt) && $stmt->bind_param('i', $id) && $stmt->execute() && $stmt->affected_rows == 1)
return true; return true;
$this->setErrorMessage("Failed to delete news entry $id"); return $this->sqlError('E0036');
return false;
} }
/** /**
@ -89,9 +94,7 @@ class News extends Base {
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, header, content, active) VALUES (?,?,?,?)"); $stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, header, content, active) VALUES (?,?,?,?)");
if ($stmt && $stmt->bind_param('issi', $account_id, $aData['header'], $aData['content'], $active) && $stmt->execute()) if ($stmt && $stmt->bind_param('issi', $account_id, $aData['header'], $aData['content'], $active) && $stmt->execute())
return true; return true;
$this->debug->append("Failed to add news: " . $this->mysqli->error); return $this->sqlError('E0035');
$this->setErrorMessage("Unable to add new news: " . $this->mysqli->error);
return false;
} }
} }

View File

@ -27,9 +27,7 @@ class Notification extends Mail {
$stmt = $this->mysqli->prepare("SELECT id FROM $this->table WHERE data = ? AND active = 1 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) if ($stmt && $stmt->bind_param('s', $data) && $stmt->execute() && $stmt->store_result() && $stmt->num_rows == 1)
return true; return true;
// Catchall return $this->sqlError('E0041');
// Does not seem to have a notification set
return false;
} }
/** /**
@ -40,8 +38,7 @@ class Notification extends Mail {
$stmt =$this->mysqli->prepare("SELECT id, data FROM $this->table WHERE active = 1 AND type = ?"); $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()) if ($stmt && $stmt->bind_param('s', $strType) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
// Catchall return $this->sqlError('E0042');
return false;
} }
/** /**
@ -56,9 +53,7 @@ class Notification extends Mail {
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, type, data, active) VALUES (?, ?,?,1)"); $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()) if ($stmt && $stmt->bind_param('iss', $account_id, $type, $data) && $stmt->execute())
return true; return true;
$this->debug->append("Failed to add notification for $type with $data: " . $this->mysqli->error); return $this->sqlError('E0043');
$this->setErrorMessage("Unable to add new notification " . $this->mysqli->error);
return false;
} }
/** /**
@ -71,8 +66,7 @@ class Notification extends Mail {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE account_id = ? ORDER BY time DESC"); $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()) if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
// Catchall return $this->getError();
return false;
} }
/** /**
@ -91,10 +85,7 @@ class Notification extends Mail {
return $aData; return $aData;
} }
} }
// Catchall return $this->sqlError('E0045');
$this->setErrorMessage('Unable to fetch notification settings');
$this->debug->append('Failed fetching notification settings for ' . $account_id . ': ' . $this->mysqli->error);
return false;
} }
/** /**
@ -108,8 +99,7 @@ class Notification extends Mail {
if ($stmt && $stmt->bind_param('s', $strType) && $stmt->execute() && $result = $stmt->get_result()) { if ($stmt && $stmt->bind_param('s', $strType) && $stmt->execute() && $result = $stmt->get_result()) {
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
} }
// Catchall return $this->sqlError('E0046');
return false;
} }
/** /**
@ -142,7 +132,7 @@ class Notification extends Mail {
} }
} }
if ($failed > 0) { if ($failed > 0) {
$this->setErrorMessage('Failed to update ' . $failed . ' settings'); $this->setErrorMessage($this->getErrorMsg('E0047', $failed));
return false; return false;
} }
return true; return true;
@ -183,4 +173,5 @@ $notification->setMysql($mysqli);
$notification->setSmarty($smarty); $notification->setSmarty($smarty);
$notification->setConfig($config); $notification->setConfig($config);
$notification->setSetting($setting); $notification->setSetting($setting);
$notification->setErrorCodes($aErrorCodes);
?> ?>

View File

@ -4,7 +4,7 @@
if (!defined('SECURITY')) die('Hacking attempt'); if (!defined('SECURITY')) die('Hacking attempt');
class Payout Extends Base { class Payout Extends Base {
var $table = 'payouts'; protected $table = 'payouts';
/** /**
* Check if the user has an active payout request already * Check if the user has an active payout request already
@ -15,7 +15,7 @@ class Payout Extends Base {
$stmt = $this->mysqli->prepare("SELECT id FROM $this->table WHERE completed = 0 AND account_id = ? LIMIT 1"); $stmt = $this->mysqli->prepare("SELECT id FROM $this->table WHERE completed = 0 AND account_id = ? LIMIT 1");
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute( )&& $stmt->store_result() && $stmt->num_rows > 0) if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute( )&& $stmt->store_result() && $stmt->num_rows > 0)
return true; return true;
return false; return $this->sqlError('E0048');
} }
/** /**
@ -27,7 +27,7 @@ class Payout Extends Base {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE completed = 0"); $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE completed = 0");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
return false; return $this->sqlError('E0050');
} }
/** /**
@ -40,9 +40,7 @@ class Payout Extends Base {
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute()) { if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute()) {
return $stmt->insert_id; return $stmt->insert_id;
} }
$this->setErrorMessage('Unable to create new payout request'); return $this->sqlError('E0049');
$this->debug->append('Failed to create new payout request in database: ' . $this->mysqli->error);
return false;
} }
/** /**
@ -54,10 +52,13 @@ class Payout Extends Base {
$stmt = $this->mysqli->prepare("UPDATE $this->table SET completed = 1 WHERE id = ?"); $stmt = $this->mysqli->prepare("UPDATE $this->table SET completed = 1 WHERE id = ?");
if ($stmt && $stmt->bind_param('i', $id) && $stmt->execute()) if ($stmt && $stmt->bind_param('i', $id) && $stmt->execute())
return true; return true;
return false; return $this->sqlError('E0051');
} }
} }
$oPayout = new Payout(); $oPayout = new Payout();
$oPayout->setDebug($debug); $oPayout->setDebug($debug);
$oPayout->setMysql($mysqli); $oPayout->setMysql($mysqli);
$oPayout->setErrorCodes($aErrorCodes);
?>

View File

@ -4,41 +4,25 @@
if (!defined('SECURITY')) if (!defined('SECURITY'))
die('Hacking attempt'); die('Hacking attempt');
class RoundStats { class RoundStats extends Base {
private $sError = '';
private $tableTrans = 'transactions'; private $tableTrans = 'transactions';
private $tableStats = 'statistics_shares'; private $tableStats = 'statistics_shares';
private $tableBlocks = 'blocks'; private $tableBlocks = 'blocks';
private $tableUsers = 'accounts'; private $tableUsers = 'accounts';
public function __construct($debug, $mysqli, $config) {
$this->debug = $debug;
$this->mysqli = $mysqli;
$this->config = $config;
$this->debug->append("Instantiated RoundStats class", 2);
}
// get and set methods
private function setErrorMessage($msg) {
$this->sError = $msg;
}
public function getError() {
return $this->sError;
}
/** /**
* Get next block for round stats * Get next block for round stats
**/ **/
public function getNextBlock($iHeight=0) { public function getNextBlock($iHeight=0) {
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT height SELECT height
FROM $this->tableBlocks FROM " . $this->block->getTableName() . "
WHERE height > ? WHERE height > ?
ORDER BY height ASC ORDER BY height ASC
LIMIT 1"); LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->height; return $result->fetch_object()->height;
return false; return $this->sqlError();
} }
/** /**
@ -47,13 +31,44 @@ class RoundStats {
public function getPreviousBlock($iHeight=0) { public function getPreviousBlock($iHeight=0) {
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT height SELECT height
FROM $this->tableBlocks FROM " . $this->block->getTableName() . "
WHERE height < ? WHERE height < ?
ORDER BY height DESC ORDER BY height DESC
LIMIT 1"); LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->height; return $result->fetch_object()->height;
return false; return $this->sqlError();
}
/**
* search for block height
**/
public function searchForBlockHeight($iHeight=0) {
$stmt = $this->mysqli->prepare("
SELECT height
FROM " . $this->block->getTableName() . "
WHERE height >= ?
ORDER BY height ASC
LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->height;
return $this->sqlError();
}
/**
* get next block for stats paging
**/
public function getNextBlockForStats($iHeight=0, $limit=10) {
$stmt = $this->mysqli->prepare("
SELECT MAX(x.height) AS height
FROM (
SELECT height FROM " . $this->block->getTableName() . "
WHERE height >= ?
ORDER BY height ASC LIMIT ?
) AS x");
if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $iHeight, $limit) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->height;
return $this->sqlError();
} }
/** /**
@ -61,17 +76,19 @@ class RoundStats {
* @param height int Block Height * @param height int Block Height
* @return data array Block information from DB * @return data array Block information from DB
**/ **/
public function getDetailsForBlockHeight($iHeight=0, $isAdmin=0) { public function getDetailsForBlockHeight($iHeight=0) {
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT SELECT
b.id, height, blockhash, amount, confirmations, difficulty, FROM_UNIXTIME(time) as time, shares, b.id, height, blockhash, amount, confirmations, difficulty, FROM_UNIXTIME(time) as time, shares,
IF(a.is_anonymous, IF( ? , a.username, 'anonymous'), a.username) AS finder IF(a.is_anonymous, 'anonymous', a.username) AS finder,
FROM $this->tableBlocks as b ROUND((difficulty * 65535) / POW(2, (" . $this->config['difficulty'] . " -16)), 0) AS estshares,
LEFT JOIN $this->tableUsers AS a ON b.account_id = a.id (time - (SELECT time FROM $this->tableBlocks WHERE height < ? ORDER BY height DESC LIMIT 1)) AS round_time
WHERE b.height = ? LIMIT 1"); FROM " . $this->block->getTableName() . " as b
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $isAdmin, $iHeight) && $stmt->execute() && $result = $stmt->get_result()) LEFT JOIN " . $this->user->getTableName() . " AS a ON b.account_id = a.id
WHERE b.height = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iHeight, $iHeight) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc(); return $result->fetch_assoc();
return false; return $this->sqlError();
} }
/** /**
@ -79,22 +96,68 @@ class RoundStats {
* @param height int Block Height * @param height int Block Height
* @return data array Block information from DB * @return data array Block information from DB
**/ **/
public function getRoundStatsForAccounts($iHeight=0, $isAdmin=0) { public function getRoundStatsForAccounts($iHeight=0) {
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT SELECT
IF(a.is_anonymous, IF( ? , a.username, 'anonymous'), a.username) AS username, a.id,
a.username,
a.is_anonymous,
s.valid, s.valid,
s.invalid s.invalid
FROM $this->tableStats AS s FROM " . $this->statistics->getTableName() . " AS s
LEFT JOIN $this->tableBlocks AS b ON s.block_id = b.id LEFT JOIN " . $this->block->getTableName() . " AS b ON s.block_id = b.id
LEFT JOIN $this->tableUsers AS a ON a.id = s.account_id LEFT JOIN " . $this->user->getTableName() . " AS a ON a.id = s.account_id
WHERE b.height = ? WHERE b.height = ?
GROUP BY username ASC GROUP BY username ASC
ORDER BY valid DESC ORDER BY valid DESC
"); ");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $isAdmin, $iHeight) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result()) {
while ($row = $result->fetch_assoc()) {
$aData[$row['id']] = $row;
}
return $aData;
}
return $this->sqlError();
}
/**
* Get pplns statistics for round block height
* @param height int Block Height
* @return data array Block information from DB
**/
public function getPPLNSRoundStatsForAccounts($iHeight=0) {
$stmt = $this->mysqli->prepare("
SELECT
a.username,
a.is_anonymous,
s.pplns_valid,
s.pplns_invalid
FROM " . $this->statistics->getTableName() . " AS s
LEFT JOIN " . $this->block->getTableName() . " AS b ON s.block_id = b.id
LEFT JOIN " . $this->user->getTableName() . " AS a ON a.id = s.account_id
WHERE b.height = ?
GROUP BY username ASC
ORDER BY pplns_valid DESC
");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
return false; return $this->sqlError();
}
/**
* Get total valid pplns shares for block height
**/
public function getPPLNSRoundShares($iHeight=0) {
$stmt = $this->mysqli->prepare("
SELECT
SUM(s.pplns_valid) AS pplns_valid
FROM " . $this->statistics->getTableName() . " AS s
LEFT JOIN " . $this->block->getTableName() . " AS b ON s.block_id = b.id
WHERE b.height = ?
");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->pplns_valid;
return $this->sqlError();
} }
/** /**
@ -102,23 +165,25 @@ class RoundStats {
* @param height int Block Height * @param height int Block Height
* @return data array Block round transactions * @return data array Block round transactions
**/ **/
public function getAllRoundTransactions($iHeight=0, $admin) { public function getAllRoundTransactions($iHeight=0) {
$this->debug->append("STA " . __METHOD__, 4); $this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT SELECT
t.id AS id, t.id AS id,
IF(a.is_anonymous, IF( ? , a.username, 'anonymous'), a.username) AS username, a.id AS uid,
a.username AS username,
a.is_anonymous,
t.type AS type, t.type AS type,
t.amount AS amount t.amount AS amount
FROM $this->tableTrans AS t FROM " . $this->transaction->getTableName() . " AS t
LEFT JOIN $this->tableBlocks AS b ON t.block_id = b.id LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
LEFT JOIN $this->tableUsers AS a ON t.account_id = a.id LEFT JOIN " . $this->user->getTableName() . " AS a ON t.account_id = a.id
WHERE b.height = ? WHERE b.height = ? AND t.type = 'Credit'
ORDER BY id ASC"); ORDER BY amount DESC");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $admin, $iHeight) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
$this->debug->append('Unable to fetch transactions'); $this->debug->append('Unable to fetch transactions');
return false; return $this->sqlError();
} }
/** /**
@ -135,26 +200,93 @@ class RoundStats {
a.username AS username, a.username AS username,
t.type AS type, t.type AS type,
t.amount AS amount t.amount AS amount
FROM $this->tableTrans AS t FROM " . $this->transaction->getTableName() . " AS t
LEFT JOIN $this->tableBlocks AS b ON t.block_id = b.id LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
LEFT JOIN $this->tableUsers AS a ON t.account_id = a.id LEFT JOIN " . $this->user->getTableName() . " AS a ON t.account_id = a.id
WHERE b.height = ? AND a.id = ? WHERE b.height = ? AND a.id = ?
ORDER BY id ASC"); ORDER BY id ASC");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iHeight, $id) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iHeight, $id) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
$this->debug->append('Unable to fetch transactions'); $this->debug->append('Unable to fetch transactions');
return false; return $this->sqlError();
} }
private function checkStmt($bState) { /**
if ($bState ===! true) { * Get ALL last blocks from height for admin panel
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error); **/
$this->setErrorMessage('Internal application Error'); public function getAllReportBlocksFoundHeight($iHeight=0, $limit=10) {
return false; $stmt = $this->mysqli->prepare("
} SELECT
return true; height, shares
FROM " . $this->block->getTableName() . "
WHERE height <= ?
ORDER BY height DESC LIMIT ?");
if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $iHeight, $limit) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
return $this->sqlError();
} }
/**
* Get USER last blocks from height for admin panel
**/
public function getUserReportBlocksFoundHeight($iHeight=0, $limit=10, $iUser) {
$stmt = $this->mysqli->prepare("
SELECT
b.height, b.shares
FROM " . $this->block->getTableName() . " AS b
LEFT JOIN " . $this->statistics->getTableName() . " AS s ON s.block_id = b.id
LEFT JOIN " . $this->user->getTableName() . " AS a ON a.id = s.account_id
WHERE b.height <= ? AND a.id = ?
ORDER BY height DESC LIMIT ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $iHeight, $iUser, $limit) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
return $this->sqlError();
}
/**
* Get shares for block height for user admin panel
**/
public function getRoundStatsForUser($iHeight=0, $iUser) {
$stmt = $this->mysqli->prepare("
SELECT
s.valid,
s.invalid,
s.pplns_valid,
s.pplns_invalid
FROM " . $this->statistics->getTableName() . " AS s
LEFT JOIN " . $this->block->getTableName() . " AS b ON s.block_id = b.id
LEFT JOIN " . $this->user->getTableName() . " AS a ON a.id = s.account_id
WHERE b.height = ? AND a.id = ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iHeight, $iUser) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc();
return $this->sqlError();
}
/**
* Get credit transactions for round block height for admin panel
**/
public function getUserRoundTransHeight($iHeight=0, $iUser) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("
SELECT
IFNULL(t.amount, 0) AS amount
FROM " . $this->transaction->getTableName() . " AS t
LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
LEFT JOIN " . $this->user->getTableName() . " AS a ON t.account_id = a.id
WHERE b.height = ? AND t.type = 'Credit' AND t.account_id = ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iHeight, $iUser) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->amount;
$this->debug->append('Unable to fetch transactions');
return $this->sqlError();
}
} }
$roundstats = new RoundStats($debug, $mysqli, $config); $roundstats = new RoundStats();
$roundstats->setDebug($debug);
$roundstats->setMysql($mysqli);
$roundstats->setConfig($config);
$roundstats->setErrorCodes($aErrorCodes);
$roundstats->setUser($user);
$roundstats->setStatistics($statistics);
$roundstats->setBlock($block);
$roundstats->setTransaction($transaction);

View File

@ -1,15 +1,10 @@
<?php <?php
// Make sure we are called from index.php // Make sure we are called from index.php
if (!defined('SECURITY')) if (!defined('SECURITY')) die('Hacking attempt');
die('Hacking attempt');
class Setting { class Setting extends Base {
public function __construct($debug, $mysqli) { protected $table = 'settings';
$this->debug = $debug;
$this->mysqli = $mysqli;
$this->table = 'settings';
}
/** /**
* Fetch a value from our table * Fetch a value from our table
@ -17,18 +12,13 @@ class Setting {
* @return value string Value * @return value string Value
**/ **/
public function getValue($name) { public function getValue($name) {
$query = $this->mysqli->prepare("SELECT value FROM $this->table WHERE name=? LIMIT 1"); $stmt = $this->mysqli->prepare("SELECT value FROM $this->table WHERE name = ? LIMIT 1");
if ($query) { if ($this->checkStmt($stmt) && $stmt->bind_param('s', $name) && $stmt->execute() && $result = $stmt->get_result())
$query->bind_param('s', $name); if ($result->num_rows > 0)
$query->execute(); return $result->fetch_object()->value;
$query->bind_result($value); // Log error but return empty string
$query->fetch(); $this->sqlError();
$query->close(); return "";
} else {
$this->debug->append("Failed to fetch variable $name from $this->table");
return false;
}
return $value;
} }
/** /**
@ -41,13 +31,14 @@ class Setting {
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
INSERT INTO $this->table (name, value) INSERT INTO $this->table (name, value)
VALUES (?, ?) VALUES (?, ?)
ON DUPLICATE KEY UPDATE value = ? ON DUPLICATE KEY UPDATE value = ?");
");
if ($stmt && $stmt->bind_param('sss', $name, $value, $value) && $stmt->execute()) if ($stmt && $stmt->bind_param('sss', $name, $value, $value) && $stmt->execute())
return true; return true;
$this->debug->append("Failed to set $name to $value"); return $this->sqlError();
return false;
} }
} }
$setting = new Setting($debug, $mysqli); $setting = new Setting($debug, $mysqli);
$setting->setDebug($debug);
$setting->setMysql($mysqli);
$setting->setErrorCodes($aErrorCodes);

View File

@ -4,32 +4,14 @@
if (!defined('SECURITY')) if (!defined('SECURITY'))
die('Hacking attempt'); die('Hacking attempt');
class Share { class Share Extends Base {
private $sError = ''; protected $table = 'shares';
private $table = 'shares'; protected $tableArchive = 'shares_archive';
private $tableArchive = 'shares_archive';
private $oUpstream; private $oUpstream;
private $iLastUpstreamId; private $iLastUpstreamId;
// This defines each share // This defines each share
public $rem_host, $username, $our_result, $upstream_result, $reason, $solution, $time, $difficulty; public $rem_host, $username, $our_result, $upstream_result, $reason, $solution, $time, $difficulty;
public function __construct($debug, $mysqli, $user, $block, $config) {
$this->debug = $debug;
$this->mysqli = $mysqli;
$this->user = $user;
$this->config = $config;
$this->block = $block;
$this->debug->append("Instantiated Share class", 2);
}
// get and set methods
private function setErrorMessage($msg) {
$this->sError = $msg;
}
public function getError() {
return $this->sError;
}
/** /**
* Fetch archive tables name for this class * Fetch archive tables name for this class
* @param none * @param none
@ -38,13 +20,43 @@ class Share {
public function getArchiveTableName() { public function getArchiveTableName() {
return $this->tableArchive; return $this->tableArchive;
} }
/** /**
* Fetch normal table name for this class * Fetch a single share by ID
* @param none * @param id int Share ID
* @return data string Table name * @return array Share data
**/ **/
public function getTableName() { public function getShareById($id) {
return $this->table; return $this->getAllAssoc($id);
}
/**
* Update an entire shares data
**/
public function updateShareById($id, $data) {
$this->debug->append("STA " . __METHOD__, 4);
$sql = "UPDATE $this->table SET";
$start = true;
// Remove ID column
unset($data['id']);
foreach ($data as $column => $value) {
$start == true ? $sql .= " $column = ? " : $sql .= ", $column = ?";
$start = false;
switch($column) {
case 'difficulty':
$this->addParam('d', $value);
break;
default:
$this->addParam('s', $value);
break;
}
}
$sql .= " WHERE id = ? LIMIT 1";
$this->addParam('i', $id);
$stmt = $this->mysqli->prepare($sql);
if ($this->checkStmt($stmt) && call_user_func_array( array($stmt, 'bind_param'), $this->getParam()) && $stmt->execute())
return true;
return $this->sqlError();
} }
/** /**
@ -52,14 +64,10 @@ class Share {
* Used for PPS calculations without moving to archive * Used for PPS calculations without moving to archive
**/ **/
public function getLastInsertedShareId() { public function getLastInsertedShareId() {
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("SELECT MAX(id) AS id FROM $this->table");
SELECT MAX(id) AS id FROM $this->table
");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->id; return $result->fetch_object()->id;
// Catchall return $this->sqlError();
$this->setErrorMessage('Failed to fetch last inserted share ID');
return false;
} }
/** /**
@ -75,14 +83,9 @@ class Share {
WHERE our_result = 'Y' WHERE our_result = 'Y'
AND id > ? AND id <= ? AND id > ? AND id <= ?
"); ");
if ($this->checkStmt($stmt)) { if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute() && $result = $stmt->get_result())
$stmt->bind_param('ii', $previous_upstream, $current_upstream);
$stmt->execute();
$result = $stmt->get_result();
$stmt->close();
return $result->fetch_object()->total; return $result->fetch_object()->total;
} return $this->sqlError();
return false;
} }
/** /**
@ -108,7 +111,7 @@ class Share {
"); ");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
return false; return $this->sqlError();
} }
/** /**
@ -118,19 +121,17 @@ class Share {
$stmt = $this->mysqli->prepare("SELECT MAX(id) AS id FROM $this->table"); $stmt = $this->mysqli->prepare("SELECT MAX(id) AS id FROM $this->table");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->id; return $result->fetch_object()->id;
return false; return $this->sqlError();
} }
/** /**
* Fetch the highest available share ID from archive * Fetch the highest available share ID from archive
**/ **/
function getMaxArchiveShareId() { function getMaxArchiveShareId() {
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("SELECT MAX(share_id) AS share_id FROM $this->tableArchive");
SELECT MAX(share_id) AS share_id FROM $this->tableArchive
");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->share_id; return $result->fetch_object()->share_id;
return false; return $this->sqlError();
} }
/** /**
@ -161,7 +162,7 @@ class Share {
} }
if (is_array($aData)) return $aData; if (is_array($aData)) return $aData;
} }
return false; return $this->sqlError();
} }
/** /**
@ -185,8 +186,7 @@ class Share {
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $this->config['archive']['maxage']) && $stmt->execute()) if ($this->checkStmt($stmt) && $stmt->bind_param('i', $this->config['archive']['maxage']) && $stmt->execute())
return true; return true;
} }
// Catchall return $this->sqlError();
return false;
} }
/** /**
@ -202,20 +202,22 @@ class Share {
SELECT id, username, our_result, upstream_result, ?, time, IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty) AS difficulty SELECT id, username, our_result, upstream_result, ?, time, IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty) AS difficulty
FROM $this->table FROM $this->table
WHERE id > ? AND id <= ?"); WHERE id > ? AND id <= ?");
if ($this->checkStmt($archive_stmt) && $archive_stmt->bind_param('iii', $block_id, $previous_upstream, $current_upstream) && $archive_stmt->execute()) { if ($this->checkStmt($archive_stmt) && $archive_stmt->bind_param('iii', $block_id, $previous_upstream, $current_upstream) && $archive_stmt->execute())
$archive_stmt->close();
return true; return true;
} return $this->sqlError();
// Catchall
return false;
} }
/**
* Delete accounted shares from shares table
* @param current_upstream int Current highest upstream ID
* @param previous_upstream int Previous upstream ID
* @return bool true or false
**/
public function deleteAccountedShares($current_upstream, $previous_upstream=0) { public function deleteAccountedShares($current_upstream, $previous_upstream=0) {
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id > ? AND id <= ?"); $stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id > ? AND id <= ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute()) if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute())
return true; return true;
// Catchall return $this->sqlError();
return false;
} }
/** /**
* Set/get last found share accepted by upstream: id and accounts * Set/get last found share accepted by upstream: id and accounts
@ -229,7 +231,10 @@ class Share {
public function getUpstreamFinder() { public function getUpstreamFinder() {
return @$this->oUpstream->account; return @$this->oUpstream->account;
} }
public function getUpstreamId() { public function getUpstreamWorker() {
return @$this->oUpstream->worker;
}
public function getUpstreamShareId() {
return @$this->oUpstream->id; return @$this->oUpstream->id;
} }
/** /**
@ -240,7 +245,7 @@ class Share {
* @param last int Skips all shares up to last to find new share * @param last int Skips all shares up to last to find new share
* @return bool * @return bool
**/ **/
public function setUpstream($aBlock, $last=0) { public function findUpstreamShare($aBlock, $last=0) {
// Many use stratum, so we create our stratum check first // Many use stratum, so we create our stratum check first
$version = pack("I*", sprintf('%08d', $aBlock['version'])); $version = pack("I*", sprintf('%08d', $aBlock['version']));
$previousblockhash = pack("H*", swapEndian($aBlock['previousblockhash'])); $previousblockhash = pack("H*", swapEndian($aBlock['previousblockhash']));
@ -252,39 +257,39 @@ class Share {
$header_hex = implode(unpack("H*", $header_bin)); $header_hex = implode(unpack("H*", $header_bin));
// Stratum supported blockhash solution entry // Stratum supported blockhash solution entry
$stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution = ? LIMIT 1"); $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id FROM $this->table WHERE solution = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('s', $aBlock['hash']) && $stmt->execute() && $result = $stmt->get_result()) { if ($this->checkStmt($stmt) && $stmt->bind_param('s', $aBlock['hash']) && $stmt->execute() && $result = $stmt->get_result()) {
$this->oUpstream = $result->fetch_object(); $this->oUpstream = $result->fetch_object();
$this->share_type = 'startum_blockhash'; $this->share_type = 'stratum_blockhash';
if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id))
return true; return true;
} }
// Stratum scrypt hash check // Stratum scrypt hash check
$scrypt_hash = swapEndian(bin2hex(Scrypt::calc($header_bin, $header_bin, 1024, 1, 1, 32))); $scrypt_hash = swapEndian(bin2hex(Scrypt::calc($header_bin, $header_bin, 1024, 1, 1, 32)));
$stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution = ? LIMIT 1"); $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id FROM $this->table WHERE solution = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('s', $scrypt_hash) && $stmt->execute() && $result = $stmt->get_result()) { if ($this->checkStmt($stmt) && $stmt->bind_param('s', $scrypt_hash) && $stmt->execute() && $result = $stmt->get_result()) {
$this->oUpstream = $result->fetch_object(); $this->oUpstream = $result->fetch_object();
$this->share_type = 'startum_solution'; $this->share_type = 'stratum_solution';
if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id))
return true; return true;
} }
// Failed to fetch via startum solution, try pushpoold // Failed to fetch via startum solution, try pushpoold
// Fallback to pushpoold solution type // Fallback to pushpoold solution type
$ppheader = sprintf('%08d', $aBlock['version']) . word_reverse($aBlock['previousblockhash']) . word_reverse($aBlock['merkleroot']) . dechex($aBlock['time']) . $aBlock['bits'] . dechex($aBlock['nonce']); $ppheader = sprintf('%08d', $aBlock['version']) . word_reverse($aBlock['previousblockhash']) . word_reverse($aBlock['merkleroot']) . dechex($aBlock['time']) . $aBlock['bits'] . dechex($aBlock['nonce']);
$stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution LIKE CONCAT(?, '%') LIMIT 1"); $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id FROM $this->table WHERE solution LIKE CONCAT(?, '%') LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('s', $ppheader) && $stmt->execute() && $result = $stmt->get_result()) { if ($this->checkStmt($stmt) && $stmt->bind_param('s', $ppheader) && $stmt->execute() && $result = $stmt->get_result()) {
$this->oUpstream = $result->fetch_object(); $this->oUpstream = $result->fetch_object();
$this->share_type = 'pp_solution'; $this->share_type = 'pp_solution';
if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id))
return true; return true;
} }
// Still no match, try upstream result with timerange // Still no match, try upstream result with timerange
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT SELECT
SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id
FROM $this->table FROM $this->table
WHERE upstream_result = 'Y' WHERE upstream_result = 'Y'
AND id > ? AND id > ?
@ -294,14 +299,14 @@ class Share {
if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $last, $aBlock['time'], $aBlock['time']) && $stmt->execute() && $result = $stmt->get_result()) { if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $last, $aBlock['time'], $aBlock['time']) && $stmt->execute() && $result = $stmt->get_result()) {
$this->oUpstream = $result->fetch_object(); $this->oUpstream = $result->fetch_object();
$this->share_type = 'upstream_share'; $this->share_type = 'upstream_share';
if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id))
return true; return true;
} }
// We failed again, now we take ANY result matching the timestamp // We failed again, now we take ANY result matching the timestamp
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT SELECT
SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id
FROM $this->table FROM $this->table
WHERE our_result = 'Y' WHERE our_result = 'Y'
AND id > ? AND id > ?
@ -310,10 +315,10 @@ class Share {
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $last, $aBlock['time']) && $stmt->execute() && $result = $stmt->get_result()) { if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $last, $aBlock['time']) && $stmt->execute() && $result = $stmt->get_result()) {
$this->oUpstream = $result->fetch_object(); $this->oUpstream = $result->fetch_object();
$this->share_type = 'any_share'; $this->share_type = 'any_share';
if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id))
return true; return true;
} }
// Catchall $this->setErrorMessage($this->getErrorMsg('E0052', $aBlock['height']));
return false; return false;
} }
@ -332,21 +337,22 @@ class Share {
AND id <= ? AND @total < ? AND id <= ? AND @total < ?
ORDER BY id DESC ORDER BY id DESC
) AS b ) AS b
WHERE total <= ? WHERE total <= ?");
");
if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $current_upstream, $iCount, $iCount) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $current_upstream, $iCount, $iCount) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->id; return $result->fetch_object()->id;
return false; return $this->sqlError();
} }
/** /**
* Fetch the lowest needed share ID from archive * Fetch the lowest needed share ID from archive
**/ **/
function getMinArchiveShareId($iCount) { function getMinArchiveShareId($iCount) {
// We don't use baseline here to be more accurate
$iCount = $iCount * pow(2, ($this->config['difficulty'] - 16));
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT MIN(b.share_id) AS share_id FROM SELECT MIN(b.share_id) AS share_id FROM
( (
SELECT share_id, @total := @total + (IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty) / POW(2, (" . $this->config['difficulty'] . " - 16))) AS total SELECT share_id, @total := @total + IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty) AS total
FROM $this->tableArchive, (SELECT @total := 0) AS a FROM $this->tableArchive, (SELECT @total := 0) AS a
WHERE our_result = 'Y' WHERE our_result = 'Y'
AND @total < ? AND @total < ?
@ -356,20 +362,14 @@ class Share {
"); ");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iCount, $iCount) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iCount, $iCount) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->share_id; return $result->fetch_object()->share_id;
return false; return $this->sqlError();
}
/**
* Helper function
**/
private function checkStmt($bState) {
if ($bState ===! true) {
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error);
$this->setErrorMessage('Internal application Error');
return false;
}
return true;
} }
} }
$share = new Share($debug, $mysqli, $user, $block, $config); $share = new Share();
$share->setDebug($debug);
$share->setMysql($mysqli);
$share->setConfig($config);
$share->setUser($user);
$share->setBlock($block);
$share->setErrorCodes($aErrorCodes);

View File

@ -10,31 +10,10 @@ if (!defined('SECURITY'))
* Statistics should be non-intrusive and not change any * Statistics should be non-intrusive and not change any
* rows in our database to ensure data integrity for the backend * rows in our database to ensure data integrity for the backend
**/ **/
class Statistics { class Statistics extends Base {
private $sError = ''; protected $table = 'statistics_shares';
private $table = 'statistics_shares';
private $getcache = true; private $getcache = true;
public function __construct($debug, $mysqli, $config, $share, $user, $block, $memcache) {
$this->debug = $debug;
$this->mysqli = $mysqli;
$this->share = $share;
$this->config = $config;
$this->user = $user;
$this->block = $block;
$this->memcache = $memcache;
$this->debug->append("Instantiated Share class", 2);
}
/* Some basic get and set methods
**/
private function setErrorMessage($msg) {
$this->sError = $msg;
}
public function getError() {
return $this->sError;
}
// Disable fetching values from cache // Disable fetching values from cache
public function setGetCache($set=false) { public function setGetCache($set=false) {
$this->getcache = $set; $this->getcache = $set;
@ -43,13 +22,74 @@ class Statistics {
return $this->getcache; return $this->getcache;
} }
private function checkStmt($bState) { /**
if ($bState ===! true) { * Get our first block found
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error); *
$this->setErrorMessage('Failed to prepare statement'); **/
return false; public function getFirstBlockFound() {
} $this->debug->append("STA " . __METHOD__, 4);
return true; if ($data = $this->memcache->get(__FUNCTION__)) return $data;
$stmt = $this->mysqli->prepare("
SELECT IFNULL(MIN(time), 0) AS time FROM " . $this->block->getTableName());
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->time;
return false;
}
/**
* Fetch last found blocks by time
**/
function getLastBlocksbyTime() {
$this->debug->append("STA " . __METHOD__, 4);
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
$stmt = $this->mysqli->prepare("
SELECT
COUNT(id) AS Total,
IFNULL(SUM(IF(confirmations > 0, 1, 0)), 0) AS TotalValid,
IFNULL(SUM(IF(confirmations = -1, 1, 0)), 0) AS TotalOrphan,
IFNULL(SUM(IF(confirmations > 0, difficulty, 0)), 0) AS TotalDifficulty,
IFNULL(ROUND(SUM(IF(confirmations > -1, shares, 0))), 0) AS TotalShares,
IFNULL(ROUND(SUM(IF(confirmations > -1, POW(2, ( 32 - " . $this->config['target_bits'] . " )) * difficulty / POW(2, (" . $this->config['difficulty'] . " -16)), 0))), 0) AS TotalEstimatedShares,
IFNULL(SUM(IF(confirmations > -1, amount, 0)), 0) AS TotalAmount,
IFNULL(SUM(IF(FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 3600 SECOND), 1, 0)), 0) AS 1HourTotal,
IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 3600 SECOND), 1, 0)), 0) AS 1HourValid,
IFNULL(SUM(IF(confirmations = -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 3600 SECOND), 1, 0)), 0) AS 1HourOrphan,
IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 3600 SECOND), difficulty, 0)), 0) AS 1HourDifficulty,
IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 3600 SECOND), shares, 0))), 0) AS 1HourShares,
IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 3600 SECOND), POW(2, ( 32 - " . $this->config['target_bits'] . " )) * difficulty / POW(2, (" . $this->config['difficulty'] . " -16)), 0))), 0) AS 1HourEstimatedShares,
IFNULL(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 3600 SECOND), amount, 0)), 0) AS 1HourAmount,
IFNULL(SUM(IF(FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 86400 SECOND), 1, 0)), 0) AS 24HourTotal,
IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 86400 SECOND), 1, 0)), 0) AS 24HourValid,
IFNULL(SUM(IF(confirmations = -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 86400 SECOND), 1, 0)), 0) AS 24HourOrphan,
IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 86400 SECOND), difficulty, 0)), 0) AS 24HourDifficulty,
IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 86400 SECOND), shares, 0))), 0) AS 24HourShares,
IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 86400 SECOND), POW(2, ( 32 - " . $this->config['target_bits'] . " )) * difficulty / POW(2, (" . $this->config['difficulty'] . " -16)), 0))), 0) AS 24HourEstimatedShares,
IFNULL(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 86400 SECOND), amount, 0)), 0) AS 24HourAmount,
IFNULL(SUM(IF(FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 604800 SECOND), 1, 0)), 0) AS 7DaysTotal,
IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 604800 SECOND), 1, 0)), 0) AS 7DaysValid,
IFNULL(SUM(IF(confirmations = -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 604800 SECOND), 1, 0)), 0) AS 7DaysOrphan,
IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 604800 SECOND), difficulty, 0)), 0) AS 7DaysDifficulty,
IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 604800 SECOND), shares, 0))), 0) AS 7DaysShares,
IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 604800 SECOND), POW(2, ( 32 - " . $this->config['target_bits'] . " )) * difficulty / POW(2, (" . $this->config['difficulty'] . " -16)), 0))), 0) AS 7DaysEstimatedShares,
IFNULL(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 604800 SECOND), amount, 0)), 0) AS 7DaysAmount,
IFNULL(SUM(IF(FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 2419200 SECOND), 1, 0)), 0) AS 4WeeksTotal,
IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 2419200 SECOND), 1, 0)), 0) AS 4WeeksValid,
IFNULL(SUM(IF(confirmations = -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 2419200 SECOND), 1, 0)), 0) AS 4WeeksOrphan,
IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 2419200 SECOND), difficulty, 0)), 0) AS 4WeeksDifficulty,
IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 2419200 SECOND), shares, 0))), 0) AS 4WeeksShares,
IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 2419200 SECOND), POW(2, ( 32 - " . $this->config['target_bits'] . " )) * difficulty / POW(2, (" . $this->config['difficulty'] . " -16)), 0))), 0) AS 4WeeksEstimatedShares,
IFNULL(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 2419200 SECOND), amount, 0)), 0) AS 4WeeksAmount,
IFNULL(SUM(IF(FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 29030400 SECOND), 1, 0)), 0) AS 12MonthTotal,
IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 29030400 SECOND), 1, 0)), 0) AS 12MonthValid,
IFNULL(SUM(IF(confirmations = -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 29030400 SECOND), 1, 0)), 0) AS 12MonthOrphan,
IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 29030400 SECOND), difficulty, 0)), 0) AS 12MonthDifficulty,
IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 29030400 SECOND), shares, 0))), 0) AS 12MonthShares,
IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 29030400 SECOND), POW(2, ( 32 - " . $this->config['target_bits'] . " )) * difficulty / POW(2, (" . $this->config['difficulty'] . " -16)), 0))), 0) AS 12MonthEstimatedShares,
IFNULL(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 29030400 SECOND), amount, 0)), 0) AS 12MonthAmount
FROM " . $this->block->getTableName());
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $this->memcache->setCache(__FUNCTION__, $result->fetch_assoc());
return $this->sqlError();
} }
/** /**
@ -65,18 +105,88 @@ class Statistics {
b.*, b.*,
a.username AS finder, a.username AS finder,
a.is_anonymous AS is_anonymous, a.is_anonymous AS is_anonymous,
ROUND((difficulty * 65535) / POW(2, (" . $this->config['difficulty'] . " -16)), 0) AS estshares ROUND((difficulty * POW(2, 32 - " . $this->config['target_bits'] . ")) / POW(2, (" . $this->config['difficulty'] . " -16)), 0) AS estshares
FROM " . $this->block->getTableName() . " AS b FROM " . $this->block->getTableName() . " AS b
LEFT JOIN " . $this->user->getTableName() . " AS a LEFT JOIN " . $this->user->getTableName() . " AS a
ON b.account_id = a.id ON b.account_id = a.id
ORDER BY height DESC LIMIT ?"); ORDER BY height DESC LIMIT ?");
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result())
return $this->memcache->setCache(__FUNCTION__ . $limit, $result->fetch_all(MYSQLI_ASSOC), 5); return $this->memcache->setCache(__FUNCTION__ . $limit, $result->fetch_all(MYSQLI_ASSOC), 5);
// Catchall return $this->sqlError();
$this->debug->append("Failed to find blocks:" . $this->mysqli->error);
return false;
} }
/**
* Get our last $limit blocks found by height
* @param limit int Last limit blocks
* @return array
**/
public function getBlocksFoundHeight($iHeight=0, $limit=10) {
$this->debug->append("STA " . __METHOD__, 4);
if ($data = $this->memcache->get(__FUNCTION__ . $iHeight . $limit)) return $data;
$stmt = $this->mysqli->prepare("
SELECT
b.*,
a.username AS finder,
a.is_anonymous AS is_anonymous,
ROUND((difficulty * POW(2, 32 - " . $this->config['target_bits'] . ")) / POW(2, (" . $this->config['difficulty'] . " -16)), 0) AS estshares
FROM " . $this->block->getTableName() . " AS b
LEFT JOIN " . $this->user->getTableName() . " AS a
ON b.account_id = a.id
WHERE b.height <= ?
ORDER BY height DESC LIMIT ?");
if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $iHeight, $limit) && $stmt->execute() && $result = $stmt->get_result())
return $this->memcache->setCache(__FUNCTION__ . $iHeight . $limit, $result->fetch_all(MYSQLI_ASSOC), 5);
return $this->sqlError();
}
/**
* Get SUM of blocks found and generated Coins for each Account
* @param limit int Last limit blocks
* @return array
**/
public function getBlocksSolvedbyAccount($limit=25) {
$this->debug->append("STA " . __METHOD__, 4);
if ($data = $this->memcache->get(__FUNCTION__ . $limit)) return $data;
$stmt = $this->mysqli->prepare("
SELECT
b.*,
a.username AS finder,
a.is_anonymous AS is_anonymous,
COUNT(b.id) AS solvedblocks,
SUM(b.amount) AS generatedcoins
FROM " . $this->block->getTableName() . " AS b
LEFT JOIN " . $this->user->getTableName() . " AS a
ON b.account_id = a.id
WHERE confirmations > 0
GROUP BY finder
ORDER BY solvedblocks DESC LIMIT ?");
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result())
return $this->memcache->setCache(__FUNCTION__ . $limit, $result->fetch_all(MYSQLI_ASSOC), 5);
return $this->sqlError();
}
/**
* Get SUM of blocks found and generated Coins for each worker
* @param limit int Last limit blocks
* @return array
**/
public function getBlocksSolvedbyWorker($account_id, $limit=25) {
$this->debug->append("STA " . __METHOD__, 4);
if ($data = $this->memcache->get(__FUNCTION__ . $account_id . $limit)) return $data;
$stmt = $this->mysqli->prepare("
SELECT
worker_name AS finder,
COUNT(id) AS solvedblocks,
SUM(amount) AS generatedcoins
FROM " . $this->block->getTableName() . "
WHERE account_id = ? AND worker_name != 'unknown'
GROUP BY finder
ORDER BY solvedblocks DESC LIMIT ?");
if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $account_id, $limit) && $stmt->execute() && $result = $stmt->get_result())
return $this->memcache->setCache(__FUNCTION__ . $account_id . $limit, $result->fetch_all(MYSQLI_ASSOC), 5);
return $this->sqlError();
}
/** /**
* Currently the only function writing to the database * Currently the only function writing to the database
* Stored per block user statistics of valid and invalid shares * Stored per block user statistics of valid and invalid shares
@ -88,9 +198,41 @@ class Statistics {
$this->debug->append("STA " . __METHOD__, 4); $this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, valid, invalid, block_id) VALUES (?, ?, ?, ?)"); $stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, valid, invalid, block_id) VALUES (?, ?, ?, ?)");
if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $aStats['id'], $aStats['valid'], $aStats['invalid'], $iBlockId) && $stmt->execute()) return true; if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $aStats['id'], $aStats['valid'], $aStats['invalid'], $iBlockId) && $stmt->execute()) return true;
// Catchall return $this->sqlError();
$this->debug->append("Failed to update share stats: " . $this->mysqli->error); }
return false;
/**
* update user statistics of valid and invalid pplns shares
**/
public function updatePPLNSShareStatistics($aStats, $iBlockId) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("
UPDATE $this->table SET pplns_valid = ?, pplns_invalid = ? WHERE account_id = ? AND block_id = ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $aStats['valid'], $aStats['invalid'], $aStats['id'], $iBlockId) && $stmt->execute()) return true;
return $this->sqlError();
}
/**
* insert user statistics of valid and invalid pplns shares "rbpplns"
**/
public function insertPPLNSShareStatistics($aStats, $iBlockId) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, valid, invalid, pplns_valid, pplns_invalid, block_id) VALUES (?, 0, 0, ?, ?, ?)");
if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $aStats['id'], $aStats['valid'], $aStats['invalid'], $iBlockId) && $stmt->execute()) return true;
return $this->sqlError();
}
/**
* Fetch the share ID from stats for rbpplns
**/
function getIdShareStatistics($aStats, $iBlockId) {
$stmt = $this->mysqli->prepare("
SELECT id AS id FROM $this->table
WHERE account_id = ? AND block_id = ?
");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $aStats['id'], $iBlockId) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->id;
return $this->sqlError();
} }
/** /**
@ -106,20 +248,20 @@ class Statistics {
SELECT SELECT
( (
( (
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0) AS hashrate
FROM " . $this->share->getTableName() . " FROM " . $this->share->getTableName() . "
WHERE time > DATE_SUB(now(), INTERVAL ? SECOND) WHERE time > DATE_SUB(now(), INTERVAL ? SECOND)
AND our_result = 'Y'
) + ( ) + (
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0) AS hashrate
FROM " . $this->share->getArchiveTableName() . " FROM " . $this->share->getArchiveTableName() . "
WHERE time > DATE_SUB(now(), INTERVAL ? SECOND) WHERE time > DATE_SUB(now(), INTERVAL ? SECOND)
AND our_result = 'Y'
) )
) AS hashrate ) AS hashrate
FROM DUAL"); FROM DUAL");
// Catchall
if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $interval, $interval, $interval, $interval) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->hashrate); if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $interval, $interval, $interval, $interval) && $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); return $this->sqlError();
return false;
} }
/** /**
@ -137,17 +279,17 @@ class Statistics {
SELECT ROUND(COUNT(id) / ?, 2) AS sharerate SELECT ROUND(COUNT(id) / ?, 2) AS sharerate
FROM " . $this->share->getTableName() . " FROM " . $this->share->getTableName() . "
WHERE time > DATE_SUB(now(), INTERVAL ? SECOND) WHERE time > DATE_SUB(now(), INTERVAL ? SECOND)
AND our_result = 'Y'
) + ( ) + (
SELECT ROUND(COUNT(id) / ?, 2) AS sharerate SELECT ROUND(COUNT(id) / ?, 2) AS sharerate
FROM " . $this->share->getArchiveTableName() . " FROM " . $this->share->getArchiveTableName() . "
WHERE time > DATE_SUB(now(), INTERVAL ? SECOND) WHERE time > DATE_SUB(now(), INTERVAL ? SECOND)
AND our_result = 'Y'
) )
) AS sharerate ) AS sharerate
FROM DUAL"); FROM DUAL");
if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $interval, $interval, $interval, $interval) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->sharerate); if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $interval, $interval, $interval, $interval) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->sharerate);
// Catchall return $this->sqlError();
$this->debug->append("Failed to fetch share rate: " . $this->mysqli->error);
return false;
} }
/** /**
@ -179,9 +321,7 @@ class Statistics {
WHERE UNIX_TIMESTAMP(time) > IFNULL((SELECT MAX(time) FROM " . $this->block->getTableName() . "), 0)"); WHERE UNIX_TIMESTAMP(time) > IFNULL((SELECT MAX(time) FROM " . $this->block->getTableName() . "), 0)");
if ( $this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() ) if ( $this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() )
return $this->memcache->setCache(STATISTICS_ROUND_SHARES, $result->fetch_assoc()); return $this->memcache->setCache(STATISTICS_ROUND_SHARES, $result->fetch_assoc());
// Catchall return $this->sqlError();
$this->debug->append("Failed to fetch round shares: " . $this->mysqli->error);
return false;
} }
/** /**
@ -229,9 +369,7 @@ class Statistics {
$data['share_id'] = $this->share->getMaxShareId(); $data['share_id'] = $this->share->getMaxShareId();
return $this->memcache->setCache(STATISTICS_ALL_USER_SHARES, $data); return $this->memcache->setCache(STATISTICS_ALL_USER_SHARES, $data);
} }
// Catchall return $this->sqlError();
$this->debug->append("Unable to fetch all users round shares: " . $this->mysqli->error);
return false;
} }
/** /**
@ -261,18 +399,15 @@ class Statistics {
AND u.id = ?"); AND u.id = ?");
if ($stmt && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result()) if ($stmt && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result())
return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_assoc()); return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_assoc());
// Catchall return $this->sqlError();
$this->debug->append("Unable to fetch user round shares: " . $this->mysqli->error);
return false;
} }
/** /**
* Admin panel specific query * Admin panel specific query
* @return data array invlid and valid shares for all accounts * @return data array User settings and shares
**/ **/
public function getAllUserStats($filter='%') { public function getAllUserStats($filter='%') {
$this->debug->append("STA " . __METHOD__, 4); $this->debug->append("STA " . __METHOD__, 4);
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $filter)) return $data;
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT SELECT
a.id AS id, a.id AS id,
@ -281,22 +416,25 @@ class Statistics {
a.no_fees as no_fees, a.no_fees as no_fees,
a.username AS username, a.username AS username,
a.donate_percent AS donate_percent, a.donate_percent AS donate_percent,
a.email AS email, a.email AS email
ROUND(IFNULL(SUM(IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS shares
FROM " . $this->user->getTableName() . " AS a FROM " . $this->user->getTableName() . " AS a
LEFT JOIN " . $this->share->getTableName() . " AS s
ON a.username = SUBSTRING_INDEX( s.username, '.', 1 )
WHERE WHERE
a.username LIKE ? a.username LIKE ?
GROUP BY username GROUP BY username
ORDER BY username"); ORDER BY username");
if ($this->checkStmt($stmt) && $stmt->bind_param('s', $filter) && $stmt->execute() && $result = $stmt->get_result()) { 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)); // Add our cached shares to the users
while ($row = $result->fetch_assoc()) {
$row['shares'] = $this->getUserShares($row['id']);
$aUsers[] = $row;
}
return $aUsers;
} }
return $this->sqlError();
} }
/** /**
* Same as getUserShares for Hashrate * Fetch total user hashrate based on shares and archived shares
* @param account_id integer User ID * @param account_id integer User ID
* @return data integer Current Hashrate in khash/s * @return data integer Current Hashrate in khash/s
**/ **/
@ -305,27 +443,71 @@ class Statistics {
if ($this->getGetCache() && $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(" $stmt = $this->mysqli->prepare("
SELECT SELECT
( IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0), 0) AS hashrate
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate FROM (
FROM " . $this->share->getTableName() . " AS s, SELECT
" . $this->user->getTableName() . " AS u s.id, s.our_result, IF(s.difficulty = 0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty) AS difficulty
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 ) FROM
AND s.time > DATE_SUB(now(), INTERVAL ? SECOND) shares AS s,
AND u.id = ? accounts AS u
) + ( WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate AND s.time > DATE_SUB(now(), INTERVAL ? SECOND)
FROM " . $this->share->getArchiveTableName() . " AS s, AND s.our_result = 'Y'
" . $this->user->getTableName() . " AS u AND u.id = ?
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 ) UNION
AND s.time > DATE_SUB(now(), INTERVAL ? SECOND) SELECT
AND u.id = ? s.share_id, s.our_result, IF(s.difficulty = 0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty) AS difficulty
) AS hashrate FROM
FROM DUAL"); shares_archive AS s,
if ($this->checkStmt($stmt) && $stmt->bind_param("iiiiii", $interval, $interval, $account_id, $interval, $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result() ) accounts AS u
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND s.time > DATE_SUB(now(), INTERVAL ? SECOND)
AND s.our_result = 'Y'
AND u.id = ?
) AS temp");
if ($this->checkStmt($stmt) && $stmt->bind_param("iiiii", $interval, $interval, $account_id, $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result() )
return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->hashrate); return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->hashrate);
// Catchall return $this->sqlError();
$this->debug->append("Failed to fetch hashrate: " . $this->mysqli->error); }
return false;
public function getUserUnpaidPPSShares($account_id, $last_paid_pps_id) {
$this->debug->append("STA " . __METHOD__, 4);
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
$stmt = $this->mysqli->prepare("
SELECT
ROUND(IFNULL(SUM(IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS total
FROM " . $this->share->getTableName() . " AS s
JOIN " . $this->user->getTableName() . " AS a
ON a.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND a.id = ?
AND s.id > ?
WHERE our_result = 'Y'");
if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $account_id, $last_paid_pps_id) && $stmt->execute() && $result = $stmt->get_result() )
return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->total);
return $this->sqlError();
}
/**
* Get average share difficulty across all workers for user
* @param account_id int Account ID
* @param interval int Data interval in seconds
* @return double Share difficulty or 0
**/
public function getUserShareDifficulty($account_id, $interval=600) {
$this->debug->append("STA " . __METHOD__, 4);
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
$stmt = $this->mysqli->prepare("
SELECT
IFNULL(AVG(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)), 0) AS avgsharediff,
COUNT(s.id) AS total
FROM " . $this->share->getTableName() . " AS s JOIN " . $this->user->getTableName() . " AS a
ON a.username = SUBSTRING_INDEX( s.username, '.', 1 )
WHERE s.time > DATE_SUB(now(), INTERVAL ? SECOND)
AND our_result = 'Y'
AND a.id = ?");
if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result() )
return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->avgsharediff);
return $this->sqlError();
} }
/** /**
@ -338,29 +520,31 @@ class Statistics {
if ($this->getGetCache() && $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(" $stmt = $this->mysqli->prepare("
SELECT SELECT
( IFNULL(COUNT(*) / ?, 0) AS sharerate
( FROM (
SELECT COUNT(s.id) / ? AS sharerate SELECT
FROM " . $this->share->getTableName() . " AS s, s.id
" . $this->user->getTableName() . " AS u FROM
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 ) shares AS s,
AND s.time > DATE_SUB(now(), INTERVAL ? SECOND) accounts AS u
AND u.id = ? WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
) + ( AND s.time > DATE_SUB(now(), INTERVAL ? SECOND)
SELECT COUNT(s.id) / ? AS sharerate AND s.our_result = 'Y'
FROM " . $this->share->getArchiveTableName() . " AS s, AND u.id = ?
" . $this->user->getTableName() . " AS u UNION
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 ) SELECT
AND s.time > DATE_SUB(now(), INTERVAL ? SECOND) s.share_id
AND u.id = ? FROM
) shares_archive AS s,
) AS sharerate accounts AS u
FROM DUAL"); WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
if ($this->checkStmt($stmt) && $stmt->bind_param("iiiiii", $interval, $interval, $account_id, $interval, $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result() ) AND s.time > DATE_SUB(now(), INTERVAL ? SECOND)
AND s.our_result = 'Y'
AND u.id = ?
) AS temp");
if ($this->checkStmt($stmt) && $stmt->bind_param("iiiii", $interval, $interval, $account_id, $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result() )
return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->sharerate); return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->sharerate);
// Catchall return $this->sqlError();
$this->debug->append("Failed to fetch sharerate: " . $this->mysqli->error);
return false;
} }
/** /**
@ -372,17 +556,16 @@ class Statistics {
$this->debug->append("STA " . __METHOD__, 4); $this->debug->append("STA " . __METHOD__, 4);
if ($data = $this->memcache->get(__FUNCTION__ . $worker_id)) return $data; if ($data = $this->memcache->get(__FUNCTION__ . $worker_id)) return $data;
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / 600 / 1000), 0) AS hashrate SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / 600 / 1000), 0) AS hashrate
FROM " . $this->share->getTableName() . " AS s, FROM " . $this->share->getTableName() . " AS s,
" . $this->user->getTableName() . " AS u " . $this->user->getTableName() . " AS u
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 ) WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND our_result = 'Y'
AND s.time > DATE_SUB(now(), INTERVAL 600 SECOND) AND s.time > DATE_SUB(now(), INTERVAL 600 SECOND)
AND u.id = ?"); AND u.id = ?");
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $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__ . $worker_id, $result->fetch_object()->hashrate); return $this->memcache->setCache(__FUNCTION__ . $worker_id, $result->fetch_object()->hashrate);
// Catchall return $this->sqlError();
$this->debug->append("Failed to fetch hashrate: " . $this->mysqli->error);
return false;
} }
/** /**
@ -397,28 +580,32 @@ class Statistics {
switch ($type) { switch ($type) {
case 'shares': case 'shares':
if ($data = $this->memcache->get(STATISTICS_ALL_USER_SHARES)) { if ($data = $this->memcache->get(STATISTICS_ALL_USER_SHARES)) {
// Use global cache to build data // Use global cache to build data, if we have any data there
$max = 0; if (!empty($data['data']) && is_array($data['data'])) {
foreach($data['data'] as $key => $aUser) { foreach($data['data'] as $key => $aUser) {
$shares[$key] = $aUser['valid']; $shares[$key] = $aUser['valid'];
$username[$key] = $aUser['username']; $username[$key] = $aUser['username'];
}
array_multisort($shares, SORT_DESC, $username, SORT_ASC, $data['data']);
$count = 0;
foreach ($data['data'] as $key => $aUser) {
if ($count == $limit) break;
$count++;
$data_new[$key]['shares'] = $aUser['valid'];
$data_new[$key]['account'] = $aUser['username'];
$data_new[$key]['donate_percent'] = $aUser['donate_percent'];
$data_new[$key]['is_anonymous'] = $aUser['is_anonymous'];
}
return $data_new;
} }
array_multisort($shares, SORT_DESC, $username, SORT_ASC, $data['data']);
foreach ($data['data'] as $key => $aUser) {
$data_new[$key]['shares'] = $aUser['valid'];
$data_new[$key]['account'] = $aUser['username'];
$data_new[$key]['donate_percent'] = $aUser['donate_percent'];
$data_new[$key]['is_anonymous'] = $aUser['is_anonymous'];
}
return $data_new;
} }
// No cached data, fallback to SQL and cache in local cache // No cached data, fallback to SQL and cache in local cache
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT SELECT
a.username AS account,
a.donate_percent AS donate_percent, a.donate_percent AS donate_percent,
a.is_anonymous AS is_anonymous, a.is_anonymous AS is_anonymous,
ROUND(IFNULL(SUM(IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS shares, ROUND(IFNULL(SUM(IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS shares
SUBSTRING_INDEX( s.username, '.', 1 ) AS account
FROM " . $this->share->getTableName() . " AS s FROM " . $this->share->getTableName() . " AS s
LEFT JOIN " . $this->user->getTableName() . " AS a LEFT JOIN " . $this->user->getTableName() . " AS a
ON SUBSTRING_INDEX( s.username, '.', 1 ) = a.username ON SUBSTRING_INDEX( s.username, '.', 1 ) = a.username
@ -428,22 +615,21 @@ class Statistics {
LIMIT ?"); LIMIT ?");
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result())
return $this->memcache->setCache(__FUNCTION__ . $type . $limit, $result->fetch_all(MYSQLI_ASSOC)); return $this->memcache->setCache(__FUNCTION__ . $type . $limit, $result->fetch_all(MYSQLI_ASSOC));
$this->debug->append("Fetching shares failed: "); return $this->sqlError();
return false;
break; break;
case 'hashes': case 'hashes':
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT SELECT
a.username AS account,
a.donate_percent AS donate_percent, a.donate_percent AS donate_percent,
a.is_anonymous AS is_anonymous, a.is_anonymous AS is_anonymous,
IFNULL(ROUND(SUM(t1.difficulty) * 65536/600/1000, 2), 0) AS hashrate, IFNULL(ROUND(SUM(t1.difficulty) * POW(2, " . $this->config['target_bits'] . ") / 600 / 1000, 2), 0) AS hashrate
SUBSTRING_INDEX( t1.username, '.', 1 ) AS account
FROM FROM
( (
SELECT IFNULL(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0) AS difficulty, username FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y' SELECT id, IFNULL(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0) AS difficulty, username FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y'
UNION ALL UNION
SELECT IFNULL(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0) AS difficulty, username FROM " . $this->share->getArchiveTableName() ." WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y' SELECT share_id, IFNULL(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0) AS difficulty, username FROM " . $this->share->getArchiveTableName() ." WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y'
) AS t1 ) AS t1
LEFT JOIN " . $this->user->getTableName() . " AS a LEFT JOIN " . $this->user->getTableName() . " AS a
ON SUBSTRING_INDEX( t1.username, '.', 1 ) = a.username ON SUBSTRING_INDEX( t1.username, '.', 1 ) = a.username
@ -451,8 +637,7 @@ class Statistics {
ORDER BY hashrate DESC LIMIT ?"); ORDER BY hashrate DESC LIMIT ?");
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result())
return $this->memcache->setCache(__FUNCTION__ . $type . $limit, $result->fetch_all(MYSQLI_ASSOC)); return $this->memcache->setCache(__FUNCTION__ . $type . $limit, $result->fetch_all(MYSQLI_ASSOC));
$this->debug->append("Fetching shares failed: " . $this->mysqli->error); return $this->sqlError();
return false;
break; break;
} }
} }
@ -467,20 +652,24 @@ class Statistics {
if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data; if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT SELECT
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate, a.id,
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * POW(2, " . $this->config['target_bits'] . ") / 3600 / 1000), 0) AS hashrate,
HOUR(s.time) AS hour 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 WHERE time < NOW() - INTERVAL 1 HOUR
AND our_result = 'Y'
AND time > NOW() - INTERVAL 25 HOUR AND time > NOW() - INTERVAL 25 HOUR
AND a.username = SUBSTRING_INDEX( s.username, '.', 1 ) AND a.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND a.id = ? AND a.id = ?
GROUP BY HOUR(time) GROUP BY HOUR(time)
UNION ALL UNION
SELECT SELECT
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate, share_id,
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * POW(2, " . $this->config['target_bits'] . ") / 3600 / 1000), 0) AS hashrate,
HOUR(s.time) AS hour HOUR(s.time) AS hour
FROM " . $this->share->getArchiveTableName() . " AS s, accounts AS a FROM " . $this->share->getArchiveTableName() . " AS s, accounts AS a
WHERE time < NOW() - INTERVAL 1 HOUR WHERE time < NOW() - INTERVAL 1 HOUR
AND our_result = 'Y'
AND time > NOW() - INTERVAL 25 HOUR AND time > NOW() - INTERVAL 25 HOUR
AND a.username = SUBSTRING_INDEX( s.username, '.', 1 ) AND a.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND a.id = ? AND a.id = ?
@ -493,9 +682,7 @@ class Statistics {
while ($row = $result->fetch_assoc()) $aData[$row['hour']] = $row['hashrate']; while ($row = $result->fetch_assoc()) $aData[$row['hour']] = $row['hashrate'];
return $this->memcache->setCache(__FUNCTION__ . $account_id, $aData); return $this->memcache->setCache(__FUNCTION__ . $account_id, $aData);
} }
// Catchall return $this->sqlError();
$this->debug->append("Failed to fetch hourly hashrate: " . $this->mysqli->error);
return false;
} }
/** /**
@ -508,19 +695,23 @@ class Statistics {
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__)) return $data; if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__)) return $data;
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT SELECT
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate, id,
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * POW(2, " . $this->config['target_bits'] . ") / 3600 / 1000), 0) AS hashrate,
HOUR(s.time) AS hour HOUR(s.time) AS hour
FROM " . $this->share->getTableName() . " AS s FROM " . $this->share->getTableName() . " AS s
WHERE time < NOW() - INTERVAL 1 HOUR WHERE time < NOW() - INTERVAL 1 HOUR
AND time > NOW() - INTERVAL 25 HOUR AND time > NOW() - INTERVAL 25 HOUR
AND our_result = 'Y'
GROUP BY HOUR(time) GROUP BY HOUR(time)
UNION ALL UNION
SELECT SELECT
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate, share_id,
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * POW(2, " . $this->config['target_bits'] . ") / 3600 / 1000), 0) AS hashrate,
HOUR(s.time) AS hour HOUR(s.time) AS hour
FROM " . $this->share->getArchiveTableName() . " AS s FROM " . $this->share->getArchiveTableName() . " AS s
WHERE time < NOW() - INTERVAL 1 HOUR WHERE time < NOW() - INTERVAL 1 HOUR
AND time > NOW() - INTERVAL 25 HOUR AND time > NOW() - INTERVAL 25 HOUR
AND our_result = 'Y'
GROUP BY HOUR(time)"); GROUP BY HOUR(time)");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) { if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) {
$iStartHour = date('G'); $iStartHour = date('G');
@ -530,35 +721,95 @@ class Statistics {
while ($row = $result->fetch_assoc()) $aData[$row['hour']] = (int) $row['hashrate']; while ($row = $result->fetch_assoc()) $aData[$row['hour']] = (int) $row['hashrate'];
return $this->memcache->setCache(__FUNCTION__, $aData); return $this->memcache->setCache(__FUNCTION__, $aData);
} }
// Catchall return $this->sqlError();
$this->debug->append("Failed to fetch hourly hashrate: " . $this->mysqli->error);
return false;
} }
/** /**
* get user estimated payouts based on share counts * get user estimated payouts based on share counts
* @param aRoundShares array Round shares * @param value1 mixed Round shares OR share rate
* @param aUserShares array User shares * @param value2 mixed User shares OR share difficulty
* @param dDonate double User donation setting * @param dDonate double User donation setting
* @param bNoFees bool User no-fees option setting * @param bNoFees bool User no-fees option setting
* @return aEstimates array User estimations * @return aEstimates array User estimations
**/ **/
public function getUserEstimates($aRoundShares, $aUserShares, $dDonate, $bNoFees) { public function getUserEstimates($value1, $value2, $dDonate, $bNoFees, $ppsvalue=0) {
$this->debug->append("STA " . __METHOD__, 4); $this->debug->append("STA " . __METHOD__, 4);
// Fetch some user information that we need if ($this->config['payout_system'] != 'pps') {
if (@$aRoundShares['valid'] > 0 && @$aUserShares['valid'] > 0) { if (@$value1['valid'] > 0 && @$value2['valid'] > 0) {
$aEstimates['block'] = round(( (int)$aUserShares['valid'] / (int)$aRoundShares['valid'] ) * (float)$this->config['reward'], 8); $aEstimates['block'] = round(( (int)$value2['valid'] / (int)$value1['valid'] ) * (float)$this->config['reward'], 8);
$bNoFees == 0 ? $aEstimates['fee'] = round(((float)$this->config['fees'] / 100) * (float)$aEstimates['block'], 8) : $aEstimates['fee'] = 0; $bNoFees == 0 ? $aEstimates['fee'] = round(((float)$this->config['fees'] / 100) * (float)$aEstimates['block'], 8) : $aEstimates['fee'] = 0;
$aEstimates['donation'] = round((( (float)$dDonate / 100) * ((float)$aEstimates['block'] - (float)$aEstimates['fee'])), 8); $aEstimates['donation'] = round((( (float)$dDonate / 100) * ((float)$aEstimates['block'] - (float)$aEstimates['fee'])), 8);
$aEstimates['payout'] = round((float)$aEstimates['block'] - (float)$aEstimates['donation'] - (float)$aEstimates['fee'], 8); $aEstimates['payout'] = round((float)$aEstimates['block'] - (float)$aEstimates['donation'] - (float)$aEstimates['fee'], 8);
} else {
$aEstimates['block'] = 0;
$aEstimates['fee'] = 0;
$aEstimates['donation'] = 0;
$aEstimates['payout'] = 0;
}
} else { } else {
$aEstimates['block'] = 0; // Hack so we can use this method for PPS estimates too
$aEstimates['fee'] = 0; // value1 = shares/s
$aEstimates['donation'] = 0; // value2 = avg share difficulty
$aEstimates['payout'] = 0; if (@$value1 > 0 && @$value2 > 0) {
$hour = 60 * 60;
$pps = $value1 * $value2 * $ppsvalue;
$aEstimates['hours1'] = $pps * $hour;
$aEstimates['hours24'] = $pps * 24 * $hour;
$aEstimates['days7'] = $pps * 24 * 7 * $hour;
$aEstimates['days14'] = $pps * 14 * 24 * $hour;
$aEstimates['days30'] = $pps * 30 * 24 * $hour;
} else {
$aEstimates['hours1'] = 0;
$aEstimates['hours24'] = 0;
$aEstimates['days7'] = 0;
$aEstimates['days14'] = 0;
$aEstimates['days30'] = 0;
}
} }
return $aEstimates; return $aEstimates;
} }
/**
* Get pool stats last 24 hours
* @param limit int Last number of hours
* @return array
**/
public function getPoolStatsHours($hour=24) {
$this->debug->append("STA " . __METHOD__, 4);
if ($data = $this->memcache->get(__FUNCTION__ . $hour)) return $data;
$stmt = $this->mysqli->prepare("
SELECT
IFNULL(COUNT(id), 0) as count,
IFNULL(AVG(difficulty), 0) as average,
IFNULL(ROUND(SUM((POW(2, ( 32 - " . $this->config['target_bits'] . " )) * difficulty) / POW(2, (" . $this->config['difficulty'] . " -16))), 0), 0) AS expected,
IFNULL(ROUND(SUM(shares)), 0) as shares,
IFNULL(SUM(amount), 0) as rewards
FROM " . $this->block->getTableName() . "
WHERE FROM_UNIXTIME(time) > DATE_SUB(now(), INTERVAL ? HOUR)
AND confirmations >= 1");
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $hour) && $stmt->execute() && $result = $stmt->get_result())
return $this->memcache->setCache(__FUNCTION__ . $hour, $result->fetch_assoc());
return $this->sqlError();
}
/**
* Caclulate estimated shares based on network difficulty and pool difficulty
* @param dDiff double Network difficulty
* @return shares integer Share count
**/
public function getEstimatedShares($dDiff) {
return round((POW(2, (32 - $this->config['target_bits'])) * $dDiff) / pow(2, ($this->config['difficulty'] - 16)));
}
} }
$statistics = new Statistics($debug, $mysqli, $config, $share, $user, $block, $memcache); $statistics = new Statistics();
$statistics->setDebug($debug);
$statistics->setMysql($mysqli);
$statistics->setShare($share);
$statistics->setUser($user);
$statistics->setBlock($block);
$statistics->setMemcache($memcache);
$statistics->setConfig($config);
$statistics->setErrorCodes($aErrorCodes);
?>

View File

@ -4,7 +4,7 @@
if (!defined('SECURITY')) die('Hacking attempt'); if (!defined('SECURITY')) die('Hacking attempt');
class Token Extends Base { class Token Extends Base {
var $table = 'tokens'; protected $table = 'tokens';
/** /**
* Fetch a token from our table * Fetch a token from our table
@ -15,7 +15,7 @@ class Token Extends Base {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE token = ? LIMIT 1"); $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE token = ? LIMIT 1");
if ($stmt && $stmt->bind_param('s', $strToken) && $stmt->execute() && $result = $stmt->get_result()) if ($stmt && $stmt->bind_param('s', $strToken) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc(); return $result->fetch_assoc();
return false; return $this->sqlError();
} }
/** /**
@ -36,9 +36,7 @@ class Token Extends Base {
"); ");
if ($stmt && $stmt->bind_param('sii', $strToken, $iToken_id, $account_id) && $stmt->execute()) if ($stmt && $stmt->bind_param('sii', $strToken, $iToken_id, $account_id) && $stmt->execute())
return $strToken; return $strToken;
$this->setErrorMessage('Unable to create new token'); return $this->sqlError();
$this->debug->append('Failed to create new token in database: ' . $this->mysqli->error);
return false;
} }
/** /**
@ -50,7 +48,7 @@ class Token Extends Base {
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE token = ? LIMIT 1"); $stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE token = ? LIMIT 1");
if ($stmt && $stmt->bind_param('s', $token) && $stmt->execute()) if ($stmt && $stmt->bind_param('s', $token) && $stmt->execute())
return true; return true;
return false; return $this->sqlError();
} }
} }
@ -58,3 +56,4 @@ $oToken = new Token();
$oToken->setDebug($debug); $oToken->setDebug($debug);
$oToken->setMysql($mysqli); $oToken->setMysql($mysqli);
$oToken->setTokenType($tokentype); $oToken->setTokenType($tokentype);
$oToken->setErrorCodes($aErrorCodes);

View File

@ -5,7 +5,8 @@ if (!defined('SECURITY'))
die('Hacking attempt'); die('Hacking attempt');
class Token_Type Extends Base { class Token_Type Extends Base {
var $table = 'token_types'; protected $table = 'token_types';
/** /**
* Return ID for specific token * Return ID for specific token
* @param strName string Token Name * @param strName string Token Name
@ -19,3 +20,4 @@ class Token_Type Extends Base {
$tokentype = new Token_Type(); $tokentype = new Token_Type();
$tokentype->setDebug($debug); $tokentype->setDebug($debug);
$tokentype->setMysql($mysqli); $tokentype->setMysql($mysqli);
$tokentype->setErrorCodes($aErrorCodes);

View File

@ -17,7 +17,7 @@ class Tools extends Base {
* @param auth array Optional authentication data to be sent with * @param auth array Optional authentication data to be sent with
* @return dec array JSON decoded PHP array * @return dec array JSON decoded PHP array
**/ **/
private function getApi($url, $target, $auth=NULL) { public function getApi($url, $target, $auth=NULL) {
static $ch = null; static $ch = null;
static $ch = null; static $ch = null;
if (is_null($ch)) { if (is_null($ch)) {
@ -32,9 +32,15 @@ class Tools extends Base {
// run the query // run the query
$res = curl_exec($ch); $res = curl_exec($ch);
if ($res === false) throw new Exception('Could not get reply: '.curl_error($ch)); if ($res === false) {
$this->setErrorMessage('Could not get reply: '.curl_error($ch));
return false;
}
$dec = json_decode($res, true); $dec = json_decode($res, true);
if (!$dec) throw new Exception('Invalid data received, please make sure connection is working and requested API exists'); if (!$dec) {
$this->setErrorMessage('Invalid data received, please make sure connection is working and requested API exists');
return false;
}
return $dec; return $dec;
} }

View File

@ -5,7 +5,7 @@ if (!defined('SECURITY'))
die('Hacking attempt'); die('Hacking attempt');
class Transaction extends Base { class Transaction extends Base {
private $sError = '', $table = 'transactions'; protected $table = 'transactions';
public $num_rows = 0, $insert_id = 0; public $num_rows = 0, $insert_id = 0;
/** /**
@ -18,14 +18,13 @@ class Transaction extends Base {
* @param coin_address string Coin address for this transaction [optional] * @param coin_address string Coin address for this transaction [optional]
* @return bool * @return bool
**/ **/
public function addTransaction($account_id, $amount, $type='Credit', $block_id=NULL, $coin_address=NULL) { public function addTransaction($account_id, $amount, $type='Credit', $block_id=NULL, $coin_address=NULL, $txid=NULL) {
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, amount, block_id, type, coin_address) VALUES (?, ?, ?, ?, ?)"); $stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, amount, block_id, type, coin_address, txid) VALUES (?, ?, ?, ?, ?, ?)");
if ($this->checkStmt($stmt) && $stmt->bind_param("idiss", $account_id, $amount, $block_id, $type, $coin_address) && $stmt->execute()) { if ($this->checkStmt($stmt) && $stmt->bind_param("idisss", $account_id, $amount, $block_id, $type, $coin_address, $txid) && $stmt->execute()) {
$this->insert_id = $stmt->insert_id; $this->insert_id = $stmt->insert_id;
return true; return true;
} }
$this->setErrorMessage("Failed to store transaction"); return $this->sqlError();
return false;
} }
/* /*
@ -35,16 +34,20 @@ class Transaction extends Base {
* @param bool boolean True or False * @param bool boolean True or False
**/ **/
public function setArchived($account_id, $txid) { public function setArchived($account_id, $txid) {
// Update all paid out transactions as archived
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
UPDATE $this->table AS t UPDATE $this->table AS t
LEFT JOIN " . $this->block->getTableName() . " AS b LEFT JOIN " . $this->block->getTableName() . " AS b
ON b.id = t.block_id ON b.id = t.block_id
SET t.archived = 1 SET t.archived = 1
WHERE ( t.account_id = ? AND t.id <= ? AND b.confirmations >= ? ) WHERE t.archived = 0
OR ( t.account_id = ? AND t.id <= ? AND t.type IN ( 'Credit_PPS', 'Donation_PPS', 'Fee_PPS', 'TXFee', 'Debit_MP', 'Debit_AP' ) )"); AND (
if ($this->checkStmt($stmt) && $stmt->bind_param('iiiii', $account_id, $txid, $this->config['confirmations'], $account_id, $txid) && $stmt->execute()) ( t.account_id = ? AND t.id <= ? AND b.confirmations >= ? )
OR ( t.account_id = ? AND t.id <= ? AND t.type IN ( 'Credit_PPS', 'Donation_PPS', 'Fee_PPS', 'TXFee', 'Debit_MP', 'Debit_AP' ) )
)");
if ($this->checkStmt($stmt) && $stmt->bind_param('iiiii', $account_id, $txid, $this->config['confirmations'], $account_id, $txid) && $stmt->execute())
return true; return true;
return false; return $this->sqlError();
} }
/** /**
@ -53,9 +56,16 @@ class Transaction extends Base {
* @return data array type and total * @return data array type and total
**/ **/
public function getTransactionSummary($account_id=NULL) { public function getTransactionSummary($account_id=NULL) {
$sql = "SELECT SUM(t.amount) AS total, t.type AS type FROM $this->table AS t"; if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
$sql = "
SELECT
SUM(t.amount) AS total, t.type AS type
FROM transactions AS t
LEFT OUTER JOIN blocks AS b
ON b.id = t.block_id
WHERE ( b.confirmations > 0 OR b.id IS NULL )";
if (!empty($account_id)) { if (!empty($account_id)) {
$sql .= " WHERE t.account_id = ? "; $sql .= " AND t.account_id = ? ";
$this->addParam('i', $account_id); $this->addParam('i', $account_id);
} }
$sql .= " GROUP BY t.type"; $sql .= " GROUP BY t.type";
@ -74,9 +84,10 @@ class Transaction extends Base {
while ($row = $result->fetch_assoc()) { while ($row = $result->fetch_assoc()) {
$aData[$row['type']] = $row['total']; $aData[$row['type']] = $row['total'];
} }
return $aData; // Cache data for a while, query takes long on many rows
return $this->memcache->setCache(__FUNCTION__ . $account_id, $aData, 60);
} }
return false; return $this->sqlError();
} }
/** /**
@ -98,6 +109,7 @@ class Transaction extends Base {
t.amount AS amount, t.amount AS amount,
t.coin_address AS coin_address, t.coin_address AS coin_address,
t.timestamp AS timestamp, t.timestamp AS timestamp,
t.txid AS txid,
b.height AS height, b.height AS height,
b.blockhash AS blockhash, b.blockhash AS blockhash,
b.confirmations AS confirmations b.confirmations AS confirmations
@ -144,7 +156,7 @@ class Transaction extends Base {
} }
} }
if (!empty($aFilter)) { if (!empty($aFilter)) {
empty($account_id) ? $sql .= " WHERE " : $sql .= " AND "; empty($account_id) ? $sql .= " WHERE " : $sql .= " AND ";
$sql .= implode(' AND ', $aFilter); $sql .= implode(' AND ', $aFilter);
} }
} }
@ -163,8 +175,7 @@ class Transaction extends Base {
$this->num_rows = $row_count; $this->num_rows = $row_count;
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
} }
$this->debug->append('Failed to fetch transactions: ' . $this->mysqli->error); return $this->sqlError();
return false;
} }
/** /**
@ -180,8 +191,7 @@ class Transaction extends Base {
} }
return $aData; return $aData;
} }
$this->debug->append('Failed to fetch transaction types: ' . $this->mysqli->error); return $this->sqlError();
return false;
} }
/** /**
@ -208,11 +218,11 @@ class Transaction extends Base {
t.type = 'Donation_PPS' t.type = 'Donation_PPS'
) )
GROUP BY a.username GROUP BY a.username
ORDER BY donation DESC
"); ");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
$this->debug->append("Failed to fetch website donors: " . $this->mysqli->error); return $this->sqlError();
return false;
} }
/** /**
@ -236,10 +246,7 @@ class Transaction extends Base {
WHERE archived = 0"); WHERE archived = 0");
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->bind_param('ii', $this->config['confirmations'], $this->config['confirmations']) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch())
return $dBalance; return $dBalance;
// Catchall return $this->sqlError();
$this->setErrorMessage('Unable to find locked credits for all users');
$this->debug->append('MySQL query failed : ' . $this->mysqli->error);
return false;
} }
/** /**
@ -272,14 +279,17 @@ class Transaction extends Base {
"); ");
if ($this->checkStmt($stmt) && $stmt->bind_param("iiiii", $this->config['confirmations'], $this->config['confirmations'], $this->config['confirmations'], $this->config['confirmations'], $account_id) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param("iiiii", $this->config['confirmations'], $this->config['confirmations'], $this->config['confirmations'], $this->config['confirmations'], $account_id) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc(); return $result->fetch_assoc();
$this->debug->append('Failed to fetch users balance: ' . $this->mysqli->error); return $this->sqlError();
return false;
} }
} }
$transaction = new Transaction(); $transaction = new Transaction();
$transaction->setMemcache($memcache);
$transaction->setDebug($debug); $transaction->setDebug($debug);
$transaction->setMysql($mysqli); $transaction->setMysql($mysqli);
$transaction->setConfig($config); $transaction->setConfig($config);
$transaction->setBlock($block); $transaction->setBlock($block);
$transaction->setUser($user); $transaction->setUser($user);
$transaction->setErrorCodes($aErrorCodes);
?>

View File

@ -4,39 +4,12 @@
if (!defined('SECURITY')) if (!defined('SECURITY'))
die('Hacking attempt'); die('Hacking attempt');
class User { class User extends Base {
private $sError = ''; protected $table = 'accounts';
private $userID = false; private $userID = false;
private $table = 'accounts';
private $user = array(); private $user = array();
public function __construct($debug, $mysqli, $salt, $config) {
$this->debug = $debug;
$this->mysqli = $mysqli;
$this->salt = $salt;
$this->config = $config;
$this->debug->append("Instantiated User class", 2);
}
// get and set methods // get and set methods
public function setMail($mail) {
$this->mail = $mail;
}
public function setToken($token) {
$this->token = $token;
}
public function setBitcoin($bitcoin) {
$this->bitcoin = $bitcoin;
}
public function setSetting($setting) {
$this->setting = $setting;
}
private function setErrorMessage($msg) {
$this->sError = $msg;
}
public function getError() {
return $this->sError;
}
private function getHash($string) { private function getHash($string) {
return hash('sha256', $string.$this->salt); return hash('sha256', $string.$this->salt);
} }
@ -174,31 +147,6 @@ class User {
return $pin_hash === $row_pin; return $pin_hash === $row_pin;
} }
/**
* Get a single row from the table
* @param value string Value to search for
* @param search Return column to search for
* @param field string Search column
* @param type string Type of value
* @return array Return result
**/
private function getSingle($value, $search='id', $field='id', $type="i", $lower=false) {
$this->debug->append("STA " . __METHOD__, 4);
$sql = "SELECT $search FROM $this->table WHERE";
$lower ? $sql .= " LOWER($field) = LOWER(?)" : $sql .= " $field = ?";
$sql .= " LIMIT 1";
$stmt = $this->mysqli->prepare($sql);
if ($this->checkStmt($stmt)) {
$stmt->bind_param($type, $value);
$stmt->execute();
$stmt->bind_result($retval);
$stmt->fetch();
$stmt->close();
return $retval;
}
return false;
}
/** /**
* Get all users that have auto payout setup * Get all users that have auto payout setup
* @param none * @param none
@ -243,31 +191,6 @@ class User {
return $dPercent; return $dPercent;
} }
/**
* 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;
}
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;
}
/** /**
* Update the accounts password * Update the accounts password
* @param userID int User ID * @param userID int User ID
@ -438,12 +361,34 @@ class User {
// Enforce generation of a new Session ID and delete the old // Enforce generation of a new Session ID and delete the old
session_regenerate_id(true); session_regenerate_id(true);
// Enforce a page reload and point towards login with referrer included, if supplied // Enforce a page reload and point towards login with referrer included, if supplied
$location = @$_SERVER['HTTPS'] ? 'https' . '://' . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'] : 'http' . '://' . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF']; $port = ($_SERVER["SERVER_PORT"] == "80" or $_SERVER["SERVER_PORT"] == "443") ? "" : (":".$_SERVER["SERVER_PORT"]);
$location = @$_SERVER['HTTPS'] ? 'https://' . $_SERVER['SERVER_NAME'] . $port . $_SERVER['PHP_SELF'] : 'http://' . $_SERVER['SERVER_NAME'] . $port . $_SERVER['PHP_SELF'];
if (!empty($from)) $location .= '?page=login&to=' . urlencode($from); if (!empty($from)) $location .= '?page=login&to=' . urlencode($from);
// if (!headers_sent()) header('Location: ' . $location); // if (!headers_sent()) header('Location: ' . $location);
exit('<meta http-equiv="refresh" content="0; url=' . $location . '"/>'); exit('<meta http-equiv="refresh" content="0; url=' . $location . '"/>');
} }
/**
* Get all users for admin panel
**/
public function getAllUsers($filter='%') {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("
SELECT
a.id AS id,
a.username AS username
FROM " . $this->getTableName() . " AS a
WHERE a.username LIKE ?
GROUP BY username");
if ($this->checkStmt($stmt) && $stmt->bind_param('s', $filter) && $stmt->execute() && $result = $stmt->get_result()) {
while ($row = $result->fetch_assoc()) {
$aData[$row['id']] = $row['username'];
}
return $aData;
}
return false;
}
/** /**
* Fetch this classes table name * Fetch this classes table name
* @return table string This classes table name * @return table string This classes table name
@ -493,7 +438,7 @@ class User {
**/ **/
public function register($username, $password1, $password2, $pin, $email1='', $email2='', $strToken='') { public function register($username, $password1, $password2, $pin, $email1='', $email2='', $strToken='') {
$this->debug->append("STA " . __METHOD__, 4); $this->debug->append("STA " . __METHOD__, 4);
if (strlen($username > 40)) { if (strlen($username) > 40) {
$this->setErrorMessage('Username exceeding character limit'); $this->setErrorMessage('Username exceeding character limit');
return false; return false;
} }
@ -687,7 +632,11 @@ class User {
} }
// Make our class available automatically // Make our class available automatically
$user = new User($debug, $mysqli, SALT, $config); $user = new User();
$user->setDebug($debug);
$user->setMysql($mysqli);
$user->setSalt(SALT);
$user->setConfig($config);
$user->setMail($mail); $user->setMail($mail);
$user->setToken($oToken); $user->setToken($oToken);
$user->setBitcoin($bitcoin); $user->setBitcoin($bitcoin);

View File

@ -1,38 +1,10 @@
<?php <?php
// Make sure we are called from index.php // Make sure we are called from index.php
if (!defined('SECURITY')) if (!defined('SECURITY')) die('Hacking attempt');
die('Hacking attempt');
class Worker { class Worker extends Base {
private $sError = ''; protected $table = 'pool_worker';
private $table = 'pool_worker';
public function __construct($debug, $mysqli, $user, $share, $config) {
$this->debug = $debug;
$this->mysqli = $mysqli;
$this->user = $user;
$this->share = $share;
$this->config = $config;
$this->debug->append("Instantiated Worker class", 2);
}
// get and set methods
private function setErrorMessage($msg) {
$this->sError = $msg;
}
public function getError() {
return $this->sError;
}
private function checkStmt($bState) {
if ($bState ===! true) {
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error);
$this->setErrorMessage('Internal application Error');
return false;
}
return true;
}
/** /**
* Update worker list for a user * Update worker list for a user
@ -61,9 +33,7 @@ class Worker {
} }
if ($iFailed == 0) if ($iFailed == 0)
return true; return true;
// Catchall return $this->sqlError('E0053', $iFailed);
$this->setErrorMessage('Failed to update ' . $iFailed . ' worker.');
return false;
} }
/** /**
@ -71,20 +41,21 @@ class Worker {
* @param none * @param none
* @return data array Workers in IDLE state and monitoring enabled * @return data array Workers in IDLE state and monitoring enabled
**/ **/
public function getAllIdleWorkers() { public function getAllIdleWorkers($interval=600) {
$this->debug->append("STA " . __METHOD__, 4); $this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT account_id, id, username SELECT w.account_id AS account_id, w.id AS id, w.username AS username
FROM " . $this->table . " AS w FROM " . $this->share->getTableName() . " AS s
WHERE monitor = 1 RIGHT JOIN " . $this->getTableName() . " AS w
AND ( ON w.username = s.username
SELECT IFNULL(SUM(IF(our_result = 'Y', 1, 0)), 0) FROM " . $this->share->getTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND s.time > DATE_SUB(now(), INTERVAL ? SECOND)
) = 0"); AND our_result = 'Y'
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) WHERE w.monitor = 1
AND s.id IS NULL
");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $interval) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
// Catchall return $this->sqlError('E0054');
$this->setErrorMessage("Unable to fetch IDLE, monitored workers");
return false;
} }
/** /**
@ -92,7 +63,7 @@ class Worker {
* @param id int Worker ID * @param id int Worker ID
* @return mixed array Worker details * @return mixed array Worker details
**/ **/
public function getWorker($id) { public function getWorker($id, $interval=600) {
$this->debug->append("STA " . __METHOD__, 4); $this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT id, username, password, monitor, SELECT id, username, password, monitor,
@ -100,35 +71,34 @@ class Worker {
( SELECT COUNT(id) FROM " . $this->share->getArchiveTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS count_all_archive, ( SELECT COUNT(id) FROM " . $this->share->getArchiveTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS count_all_archive,
( (
SELECT SELECT
IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536/600/1000), 0), 0) AS hashrate IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0), 0) AS hashrate
FROM " . $this->share->getTableName() . " FROM " . $this->share->getTableName() . "
WHERE WHERE
username = w.username username = w.username
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND time > DATE_SUB(now(), INTERVAL ? SECOND)
) + ( ) + (
SELECT SELECT
IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536/600/1000), 0), 0) AS hashrate IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0), 0) AS hashrate
FROM " . $this->share->getArchiveTableName() . " FROM " . $this->share->getArchiveTableName() . "
WHERE WHERE
username = w.username username = w.username
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND time > DATE_SUB(now(), INTERVAL ? SECOND)
) AS hashrate, ) AS hashrate,
( (
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all, 2), 0) SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all, 2), 0)
FROM " . $this->share->getTableName() . " FROM " . $this->share->getTableName() . "
WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND)
) + ( ) + (
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all_archive, 2), 0) SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all_archive, 2), 0)
FROM " . $this->share->getArchiveTableName() . " FROM " . $this->share->getArchiveTableName() . "
WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND)
) AS difficulty ) AS difficulty
FROM $this->table AS w FROM $this->table AS w
WHERE id = ? WHERE id = ?
"); ");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $id) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param('iiiiiii', $interval, $interval, $interval, $interval, $interval, $interval, $id) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc(); return $result->fetch_assoc();
// Catchall return $this->sqlError('E0055');
return false;
} }
/** /**
@ -136,44 +106,90 @@ class Worker {
* @param account_id int User ID * @param account_id int User ID
* @return mixed array Workers and their settings or false * @return mixed array Workers and their settings or false
**/ **/
public function getWorkers($account_id) { public function getWorkers($account_id, $interval=600) {
$this->debug->append("STA " . __METHOD__, 4); $this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare(" $stmt = $this->mysqli->prepare("
SELECT id, username, password, monitor, SELECT id, username, password, monitor,
( SELECT COUNT(id) FROM " . $this->share->getTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS count_all, ( SELECT COUNT(id) FROM " . $this->share->getTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND)) AS count_all,
( SELECT COUNT(id) FROM " . $this->share->getArchiveTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS count_all_archive, ( SELECT COUNT(id) FROM " . $this->share->getArchiveTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND)) AS count_all_archive,
( (
SELECT SELECT
IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536/600/1000), 0), 0) AS hashrate IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0), 0) AS hashrate
FROM " . $this->share->getTableName() . " FROM " . $this->share->getTableName() . "
WHERE WHERE
username = w.username username = w.username
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND time > DATE_SUB(now(), INTERVAL ? SECOND)
) + ( ) + (
SELECT SELECT
IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536/600/1000), 0), 0) AS hashrate IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0), 0) AS hashrate
FROM " . $this->share->getArchiveTableName() . " FROM " . $this->share->getArchiveTableName() . "
WHERE WHERE
username = w.username username = w.username
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND time > DATE_SUB(now(), INTERVAL ? SECOND)
) AS hashrate, ) AS hashrate,
( (
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all, 2), 0) SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all, 2), 0)
FROM " . $this->share->getTableName() . " FROM " . $this->share->getTableName() . "
WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND)
) + ( ) + (
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all_archive, 2), 0) SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all_archive, 2), 0)
FROM " . $this->share->getArchiveTableName() . " FROM " . $this->share->getArchiveTableName() . "
WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND)
) AS difficulty ) AS difficulty
FROM $this->table AS w FROM $this->table AS w
WHERE account_id = ?"); WHERE account_id = ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->bind_param('iiiiiiiii', $interval, $interval, $interval, $interval, $interval, $interval, $interval, $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC); return $result->fetch_all(MYSQLI_ASSOC);
// Catchall return $this->sqlError('E0056');
$this->setErrorMessage('Failed to fetch workers for your account'); }
$this->debug->append('Fetching workers failed: ' . $this->mysqli->error);
return false; /**
* Fetch all workers for admin panel
* @param limit int
* @return mixed array Workers and their settings or false
**/
public function getAllWorkers($iLimit=0, $interval=600) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("
SELECT id, username, password, monitor,
IFNULL(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0) AS difficulty,
(
SELECT
IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0), 0) AS hashrate
FROM " . $this->share->getTableName() . "
WHERE
username = w.username
AND time > DATE_SUB(now(), INTERVAL ? SECOND)
) + (
SELECT
IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0), 0) AS hashrate
FROM " . $this->share->getArchiveTableName() . "
WHERE
username = w.username
AND time > DATE_SUB(now(), INTERVAL ? SECOND)
) AS hashrate,
((
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)), 2), 0)
FROM " . $this->share->getTableName() . "
WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND)
) + (
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)), 2), 0)
FROM " . $this->share->getArchiveTableName() . "
WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND)
)) / ((
SELECT COUNT(id)
FROM " . $this->share->getTableName() . "
WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND)
) + (
SELECT COUNT(id)
FROM " . $this->share->getArchiveTableName() . "
WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND)
)) AS avg_difficulty
FROM $this->table AS w
ORDER BY hashrate DESC LIMIT ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('iiiiiiiii', $interval, $interval, $interval, $interval, $interval, $interval, $interval, $interval, $iLimit) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
return $this->sqlError('E0057');
} }
/** /**
@ -183,10 +199,15 @@ class Worker {
**/ **/
public function getCountAllActiveWorkers() { public function getCountAllActiveWorkers() {
$this->debug->append("STA " . __METHOD__, 4); $this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("SELECT IFNULL(IF(our_result='Y', COUNT(DISTINCT username), 0), 0) AS total FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)"); if ($data = $this->memcache->get(__FUNCTION__)) return $data;
$stmt = $this->mysqli->prepare("
SELECT COUNT(DISTINCT(username)) AS total
FROM " . $this->share->getTableName() . "
WHERE our_result = 'Y'
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->total; return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->total);
return false; return $this->sqlError();
} }
/** /**
@ -201,22 +222,20 @@ class Worker {
public function addWorker($account_id, $workerName, $workerPassword) { public function addWorker($account_id, $workerName, $workerPassword) {
$this->debug->append("STA " . __METHOD__, 4); $this->debug->append("STA " . __METHOD__, 4);
if ('' === $workerName || '' === $workerPassword) { if ('' === $workerName || '' === $workerPassword) {
$this->setErrorMessage('Worker name and/or password may not be empty'); $this->setErrorMessage($this->getErrorMsg('E0058'));
return false; return false;
} }
$username = $this->user->getUserName($account_id); $username = $this->user->getUserName($account_id);
$workerName = "$username.$workerName"; $workerName = "$username.$workerName";
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, username, password) VALUES(?, ?, ?)"); $stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, username, password) VALUES(?, ?, ?)");
if ($this->checkStmt($stmt)) { if ($this->checkStmt($stmt) && $stmt->bind_param('iss', $account_id, $workerName, $workerPassword)) {
$stmt->bind_param('iss', $account_id, $workerName, $workerPassword);
if (!$stmt->execute()) { if (!$stmt->execute()) {
$this->setErrorMessage( 'Failed to add worker' ); if ($stmt->sqlstate == '23000') return $this->sqlError('E0059');
if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Worker already exists' ); } else {
return false; return true;
} }
return true;
} }
return false; return $this->sqlError('E0060');
} }
/** /**
@ -228,16 +247,19 @@ class Worker {
public function deleteWorker($account_id, $id) { public function deleteWorker($account_id, $id) {
$this->debug->append("STA " . __METHOD__, 4); $this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE account_id = ? AND id = ?"); $stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE account_id = ? AND id = ?");
if ($this->checkStmt($stmt)) { if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $account_id, $id) && $stmt->execute() && $stmt->affected_rows == 1)
$stmt->bind_param('ii', $account_id, $id);
if ($stmt->execute() && $stmt->affected_rows == 1) {
return true; return true;
} else { return $this->sqlError('E0061');
$this->setErrorMessage( 'Unable to delete worker' );
}
}
return false;
} }
} }
$worker = new Worker($debug, $mysqli, $user, $share, $config); $worker = new Worker();
$worker->setDebug($debug);
$worker->setMysql($mysqli);
$worker->setMemcache($memcache);
$worker->setShare($share);
$worker->setConfig($config);
$worker->setUser($user);
$worker->setErrorCodes($aErrorCodes);
?>

View File

@ -123,6 +123,13 @@ $aSettings['statistics'][] = array(
'name' => 'statistics_block_count', 'value' => $setting->getValue('statistics_block_count'), 'name' => 'statistics_block_count', 'value' => $setting->getValue('statistics_block_count'),
'tooltip' => 'Blocks to fetch for the block statistics page.' 'tooltip' => 'Blocks to fetch for the block statistics page.'
); );
$aSettings['statistics'][] = array(
'display' => 'Show block average', 'type' => 'select',
'options' => array( 0 => 'No', 1 => 'Yes' ),
'default' => 0,
'name' => 'statistics_show_block_average', 'value' => $setting->getValue('statistics_show_block_average'),
'tooltip' => 'Show block average in block statistics page.'
);
$aSettings['statistics'][] = array( $aSettings['statistics'][] = array(
'display' => 'Pool Hashrate Modifier', 'type' => 'select', 'display' => 'Pool Hashrate Modifier', 'type' => 'select',
'options' => array( '1' => 'KH/s', '0.001' => 'MH/s', '0.000001' => 'GH/s' ), 'options' => array( '1' => 'KH/s', '0.001' => 'MH/s', '0.000001' => 'GH/s' ),
@ -144,6 +151,28 @@ $aSettings['statistics'][] = array(
'name' => 'statistics_personal_hashrate_modifier', 'value' => $setting->getValue('statistics_personal_hashrate_modifier'), 'name' => 'statistics_personal_hashrate_modifier', 'value' => $setting->getValue('statistics_personal_hashrate_modifier'),
'tooltip' => 'Auto-adjust displayed personal hashrates to a certain limit.' 'tooltip' => 'Auto-adjust displayed personal hashrates to a certain limit.'
); );
$aSettings['statistics'][] = array(
'display' => 'Enable Google analytics', 'type' => 'select',
'options' => array( 0 => 'No', 1 => 'Yes' ),
'default' => 0,
'name' => 'statistics_analytics_enabled', 'value' => $setting->getValue('statistics_analytics_enabled'),
'tooltip' => 'Enable or Disable Google Analytics.'
);
$aSettings['statistics'][] = array(
'display' => 'Google Analytics Code', 'type' => 'textarea',
'size' => 20,
'height' => 12,
'default' => 'Code from Google Analytics',
'name' => 'statistics_analytics_code', 'value' => $setting->getValue('statistics_analytics_code'),
'tooltip' => '.'
);
$aSettings['acl'][] = array(
'display' => 'Hide news post author', 'type' => 'select',
'options' => array( 0 => 'No', 1 => 'Yes' ),
'default' => 0,
'name' => 'acl_hide_news_author', 'value' => $setting->getValue('acl_hide_news_author'),
'tooltip' => 'Should the news author username be hidden.'
);
$aSettings['acl'][] = array( $aSettings['acl'][] = array(
'display' => 'Pool Statistics', 'type' => 'select', 'display' => 'Pool Statistics', 'type' => 'select',
'options' => array( 0 => 'Private', 1 => 'Public'), 'options' => array( 0 => 'Private', 1 => 'Public'),
@ -166,11 +195,25 @@ $aSettings['acl'][] = array(
'tooltip' => 'Make the round statistics page private (users only) or public.' 'tooltip' => 'Make the round statistics page private (users only) or public.'
); );
$aSettings['acl'][] = array( $aSettings['acl'][] = array(
'display' => 'Round Transactions', 'type' => 'select', 'display' => 'Block Finder Statistics', 'type' => 'select',
'options' => array( 0 => 'Admins', 1 => 'Public'), 'options' => array( 0 => 'Private', 1 => 'Public'),
'default' => 0, 'default' => 1,
'name' => 'acl_round_transactions', 'value' => $setting->getValue('acl_round_transactions'), 'name' => 'acl_blockfinder_statistics', 'value' => $setting->getValue('acl_blockfinder_statistics'),
'tooltip' => 'Display all transactions regardless of admin status.' 'tooltip' => 'Make the Block Finder Statistics page private (users only) or public.'
);
$aSettings['acl'][] = array(
'display' => 'Uptime Statistics', 'type' => 'select',
'options' => array( 0 => 'Private', 1 => 'Public'),
'default' => 1,
'name' => 'acl_uptime_statistics', 'value' => $setting->getValue('acl_uptime_statistics'),
'tooltip' => 'Make the uptime statistics page private (users only) or public.'
);
$aSettings['system'][] = array(
'display' => 'E-mail address for system error notifications', 'type' => 'text',
'size' => 25,
'default' => 'test@example.com',
'name' => 'system_error_email', 'value' => $setting->getValue('system_error_email'),
'tooltip' => 'The email address for system errors notifications, like cronjobs failures.'
); );
$aSettings['system'][] = array( $aSettings['system'][] = array(
'display' => 'Disable e-mail confirmations', 'type' => 'select', 'display' => 'Disable e-mail confirmations', 'type' => 'select',
@ -228,6 +271,34 @@ $aSettings['system'][] = array(
'name' => 'disable_contactform', 'value' => $setting->getValue('disable_contactform'), 'name' => 'disable_contactform', 'value' => $setting->getValue('disable_contactform'),
'tooltip' => 'Enable or Disable Contactform. Users will not be able to use the contact form.' 'tooltip' => 'Enable or Disable Contactform. Users will not be able to use the contact form.'
); );
$aSettings['system'][] = array(
'display' => 'Disable Donors Page', 'type' => 'select',
'options' => array( 0 => 'No', 1 => 'Yes'),
'default' => 1,
'name' => 'disable_donors', 'value' => $setting->getValue('disable_donors'),
'tooltip' => 'Showing Donors page in Navigation.'
);
$aSettings['system'][] = array(
'display' => 'Disable About Page', 'type' => 'select',
'options' => array( 0 => 'No', 1 => 'Yes'),
'default' => 1,
'name' => 'disable_about', 'value' => $setting->getValue('disable_about'),
'tooltip' => 'Showing About page in Navigation.'
);
$aSettings['system'][] = array(
'display' => 'Disable Live Dashboard', 'type' => 'select',
'options' => array( 0 => 'No', 1 => 'Yes'),
'default' => 0,
'name' => 'disable_dashboard', 'value' => $setting->getValue('disable_dashboard'),
'tooltip' => 'Disable live updates on the dashboard to reduce server load.'
);
$aSettings['system'][] = array(
'display' => 'Disable Dashboard API', 'type' => 'select',
'options' => array( 0 => 'No', 1 => 'Yes'),
'default' => 0,
'name' => 'disable_dashboard_api', 'value' => $setting->getValue('disable_dashboard_api'),
'tooltip' => 'Disable dashboard API entirely to reduce server load.'
);
$aSettings['recaptcha'][] = array( $aSettings['recaptcha'][] = array(
'display' => 'Enable re-Captcha', 'type' => 'select', 'display' => 'Enable re-Captcha', 'type' => 'select',
'options' => array( 0 => 'No', 1 => 'Yes' ), 'options' => array( 0 => 'No', 1 => 'Yes' ),
@ -249,5 +320,12 @@ $aSettings['recaptcha'][] = array(
'name' => 'recaptcha_public_key', 'value' => $setting->getValue('recaptcha_public_key'), 'name' => 'recaptcha_public_key', 'value' => $setting->getValue('recaptcha_public_key'),
'tooltip' => 'Your public key as given by your re-Captcha account.' 'tooltip' => 'Your public key as given by your re-Captcha account.'
); );
$aSettings['monitoring'][] = array(
'display' => 'Uptime Robot Private API Key', 'type' => 'text',
'size' => 100,
'default' => '<API KEY>|<MONITOR ID>,<API KEY>|<MONITOR ID>, ...',
'name' => 'monitoring_uptimerobot_api_keys', 'value' => $setting->getValue('monitoring_uptimerobot_api_keys'),
'tooltip' => 'Create per-monitor API keys and save them here to propagate your uptime statistics.'
);
?> ?>

View File

@ -0,0 +1,71 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
$aErrorCodes['OK'] = 'OK';
$aErrorCodes['E0001'] = 'Out of Order Share Detected';
$aErrorCodes['E0002'] = 'Upstream shares not found';
$aErrorCodes['E0003'] = 'Failed to change shares order';
$aErrorCodes['E0004'] = 'Failed to reset previous block';
$aErrorCodes['E0005'] = 'Unable to fetch blocks upstream share';
$aErrorCodes['E0006'] = 'Unable to conenct to RPC server backend';
$aErrorCodes['E0007'] = 'Out of Order Share detected, autofixed';
$aErrorCodes['E0008'] = 'Failed to delete archived shares';
$aErrorCodes['E0009'] = 'Cron disabled by admin';
$aErrorCodes['E0010'] = 'Unable set payout as processed';
$aErrorCodes['E0011'] = 'No new unaccounted blocks';
$aErrorCodes['E0012'] = 'No share_id found for block';
$aErrorCodes['E0013'] = 'No shares found for block';
$aErrorCodes['E0014'] = 'Failed marking block as accounted';
$aErrorCodes['E0015'] = 'Potential Double Payout detected';
$aErrorCodes['E0016'] = 'Failed to delete accounted shares';
$aErrorCodes['E0017'] = 'Failed to update Uptime Robot status';
$aErrorCodes['E0018'] = 'Cron disbaled due to errors';
$aErrorCodes['E0019'] = "SQL Query failed: %s";
$aErrorCodes['E0020'] = 'Internal error while executing SQL';
$aErrorCodes['E0021'] = 'Unable to fetch invitiations send from your account';
$aErrorCodes['E0022'] = 'Unable to create invitation record';
$aErrorCodes['E0023'] = 'Invalid E-Mail Address';
$aErrorCodes['E0024'] = 'Message may only contain alphanumeric characters';
$aErrorCodes['E0025'] = 'This email is already registered as an account';
$aErrorCodes['E0026'] = 'A pending invitation for this address already exists';
$aErrorCodes['E0027'] = 'Unable to generate invitation token: %s';
$aErrorCodes['E0028'] = 'Unable to send email to recipient';
$aErrorCodes['E0029'] = 'Unable to send invitation';
$aErrorCodes['E0030'] = 'Unable to fetch a valid token for this invitation';
$aErrorCodes['E0031'] = 'Failed to send e-mail via mail() function';
$aErrorCodes['E0032'] = 'Failed to run API call: %s';
$aErrorCodes['E0033'] = 'Failed to store uptime status: %s';
$aErrorCodes['E0034'] = 'Subjcet may only contain alphanumeric characters';
$aErrorCodes['E0035'] = 'Failed to add news record';
$aErrorCodes['E0036'] = 'Failed to delete news record';
$aErrorCodes['E0037'] = 'Failed to update news record';
$aErrorCodes['E0038'] = 'Failed to fetch news record entry';
$aErrorCodes['E0039'] = 'Failed to fetch news records';
$aErrorCodes['E0040'] = 'Failed to fetch active news records';
$aErrorCodes['E0041'] = 'Failed to fetch existing notification records';
$aErrorCodes['E0042'] = 'Failed to fetch active notification records';
$aErrorCodes['E0043'] = 'Unable to add new notification';
$aErrorCodes['E0044'] = 'Failed to fetch notifications for user account';
$aErrorCodes['E0045'] = 'Failed fetching notification settings for user account';
$aErrorCodes['E0046'] = 'Failed to fetch notification setting for user account';
$aErrorCodes['E0047'] = "Failed to update %s settings";
$aErrorCodes['E0048'] = 'Failed to check for existing active payouts';
$aErrorCodes['E0049'] = 'Unable to create new payout request';
$aErrorCodes['E0050'] = 'Failed to fetch unprocessed payouts';
$aErrorCodes['E0051'] = 'Failed to mark payout as processed';
$aErrorCodes['E0052'] = 'Unable to find valid upstream share for block: %s';
$aErrorCodes['E0053'] = 'Failed to update %s workers';
$aErrorCodes['E0054'] = 'Unable to fetch IDLE, monitored workers';
$aErrorCodes['E0055'] = 'Failed fetching worker details';
$aErrorCodes['E0056'] = 'Failed to fetch workers for your account';
$aErrorCodes['E0057'] = 'Failed to fetch workers for admin panel';
$aErrorCodes['E0058'] = 'Worker name and/or password must not be empty';
$aErrorCodes['E0059'] = 'Worker already exists';
$aErrorCodes['E0060'] = 'Failed to add new worker';
$aErrorCodes['E0061'] = 'Failed to delete worker';
$aErrorCodes['E0062'] = 'Block has no share_id, not running payouts';
$aErrorCodes['E0063'] = 'Upstream share already assigned to previous block';
$aErrorCodes['E0064'] = 'Failed to create transaction record';
?>

View File

@ -20,6 +20,17 @@ define('DEBUG', 0);
// SALT used to hash passwords // SALT used to hash passwords
define('SALT', 'PLEASEMAKEMESOMETHINGRANDOM'); define('SALT', 'PLEASEMAKEMESOMETHINGRANDOM');
/**
* Underlying coin algorithm that you are mining on. Set this to whatever your coin needs:
*
* Options:
* sha256d : SHA coins like Bitcoin
* scrypt : Scrypt based coins like Litecoin
* Default:
* scrypt : Scrypt is default
**/
$config['algorithm'] = 'scrypt';
/** /**
* Database configuration * Database configuration
* *
@ -58,6 +69,18 @@ $config['wallet']['host'] = 'localhost:19334';
$config['wallet']['username'] = 'testnet'; $config['wallet']['username'] = 'testnet';
$config['wallet']['password'] = 'testnet'; $config['wallet']['password'] = 'testnet';
/**
* Getting Started Config
*
* This is displayed on GettingStarted Page
* to make it more dynamic
*
*
**/
$config['gettingstarted']['coinname'] = 'Litecoin';
$config['gettingstarted']['coinurl'] = 'http://www.litecoin.org';
$config['gettingstarted']['stratumport'] = '3333';
/** /**
* API configuration to fetch prices for set currency * API configuration to fetch prices for set currency
* *
@ -86,7 +109,6 @@ $config['price']['url'] = 'https://btc-e.com';
$config['price']['target'] = '/api/2/ltc_usd/ticker'; $config['price']['target'] = '/api/2/ltc_usd/ticker';
$config['price']['currency'] = 'USD'; $config['price']['currency'] = 'USD';
/** /**
* Automatic payout thresholds * Automatic payout thresholds
* *
@ -123,6 +145,20 @@ $config['accounts']['invitations']['count'] = 5;
// Currency system used in this pool, default: `LTC` // Currency system used in this pool, default: `LTC`
$config['currency'] = 'LTC'; $config['currency'] = 'LTC';
/**
* Coin Target in seconds
*
* Explanation
* Target time for coins to be generated
*
* Fastcoin: 12 seconds
* Litecoin: 2,5 minutes = 150 seconds
* Feathercoin: 2,5 minutes = 150 seconds
* Bitcoin: 10 minutes = 600 seconds
*
**/
$config['cointarget'] = '150';
/** /**
* Default transaction fee to apply to user transactions * Default transaction fee to apply to user transactions
* *
@ -231,9 +267,20 @@ $config['fees'] = 0;
* type = `blockavg` * type = `blockavg`
* blockcount = 10 * blockcount = 10
**/ **/
/**
* $config['pplns']['shares']['type'] = 'dynamic';
* Dynamic target adjustment allows the blockavg target to adjust faster to share counts
* while still tracking round share averages by using a percentage of the current round shares
* to alter the pplns blockavg target this is useful with the nature of many alt coins low and fast
* adjusting difficulties and quick round times
* reverse_payout is useful to even out payouts for fast round times when even steady miners
* are missing share submissions for the current round
**/
$config['pplns']['shares']['default'] = 4000000; $config['pplns']['shares']['default'] = 4000000;
$config['pplns']['shares']['type'] = 'blockavg'; $config['pplns']['shares']['type'] = 'blockavg';
$config['pplns']['blockavg']['blockcount'] = 10; $config['pplns']['blockavg']['blockcount'] = 10;
$config['pplns']['reverse_payout'] = false; // add user shares from archive even if user not in current round
$config['pplns']['dynamic']['percent'] = 30; // percentage of round shares factored into block average when using dynamic type
// Pool target difficulty as set in pushpoold configuration file // Pool target difficulty as set in pushpoold configuration file
// Please also read this for stratum: https://github.com/TheSerapher/php-mpos/wiki/FAQ // Please also read this for stratum: https://github.com/TheSerapher/php-mpos/wiki/FAQ
@ -297,10 +344,6 @@ $config['pps']['reward']['default'] = 50;
$config['pps']['reward']['type'] = 'blockavg'; $config['pps']['reward']['type'] = 'blockavg';
$config['pps']['blockavg']['blockcount'] = 10; $config['pps']['blockavg']['blockcount'] = 10;
// pps base payout target, default 16 = difficulty 1 shares for vardiff
// (1/(65536 * difficulty) * reward) = (reward / (pow(2,32) * difficulty) * pow(2, 16))
$config['pps_target'] = 16; // do not change unless you know what it does
/** /**
* Memcache configuration * Memcache configuration
* *
@ -413,4 +456,19 @@ $config['cookie']['secure'] = false;
**/ **/
$config['smarty']['cache'] = 0; $config['smarty']['cache'] = 0;
$config['smarty']['cache_lifetime'] = 30; $config['smarty']['cache_lifetime'] = 30;
/**
* System load setting
*
* This will disable loading of some API calls in case the system
* loads exceeds the defined max setting. Useful to temporaily suspend
* live statistics on a server that is too busy to deal with requests.
*
* Options
* max = float, maximum system load
*
* Defaults:
* max = 10.0
**/
$config['system']['load']['max'] = 10.0;
?> ?>

View File

@ -3,9 +3,15 @@
// Make sure we are called from index.php // Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt'); if (!defined('SECURITY')) die('Hacking attempt');
$aDonors = $transaction->getDonations(); if ($setting->getValue('disable_donors')) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Donors are currently disabled. Please try again later.', 'TYPE' => 'errormsg');
$smarty->assign("CONTENT", "disabled.tpl");
} else {
$aDonors = $transaction->getDonations();
// Tempalte specifics
$smarty->assign("DONORS", $aDonors);
$smarty->assign("CONTENT", "default.tpl");
}
// Tempalte specifics
$smarty->assign("DONORS", $aDonors);
$smarty->assign("CONTENT", "default.tpl");
?> ?>

View File

@ -4,6 +4,12 @@
if (!defined('SECURITY')) if (!defined('SECURITY'))
die('Hacking attempt'); die('Hacking attempt');
// Tempalte specifics if ($setting->getValue('disable_about')) {
$smarty->assign("CONTENT", "default.tpl"); $_SESSION['POPUP'][] = array('CONTENT' => 'Donors are currently disabled. Please try again later.', 'TYPE' => 'errormsg');
$smarty->assign("CONTENT", "disabled.tpl");
} else {
// Tempalte specifics
$smarty->assign("CONTENT", "default.tpl");
}
?> ?>

View File

@ -11,7 +11,7 @@ if ($user->isAuthenticated()) {
if ($notification->updateSettings($_SESSION['USERDATA']['id'], $_REQUEST['data'])) { if ($notification->updateSettings($_SESSION['USERDATA']['id'], $_REQUEST['data'])) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Updated notification settings'); $_SESSION['POPUP'][] = array('CONTENT' => 'Updated notification settings');
} else { } else {
$_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update settings', 'TYPE' => 'errormsg'); $_SESSION['POPUP'][] = array('CONTENT' => $notification->getError(), 'TYPE' => 'errormsg');
} }
} }

View File

@ -28,6 +28,7 @@ case 'prop':
// Data array for template // Data array for template
foreach ($aCrons as $strCron) { foreach ($aCrons as $strCron) {
$aCronStatus[$strCron] = array( $aCronStatus[$strCron] = array(
'disabled' => $monitoring->getStatus($strCron . '_disabled'),
'exit' => $monitoring->getStatus($strCron . '_status'), 'exit' => $monitoring->getStatus($strCron . '_status'),
'active' => $monitoring->getStatus($strCron . '_active'), 'active' => $monitoring->getStatus($strCron . '_active'),
'runtime' => $monitoring->getStatus($strCron . '_runtime'), 'runtime' => $monitoring->getStatus($strCron . '_runtime'),

View File

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

View File

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

View File

@ -31,23 +31,11 @@ if (@$_POST['query']) {
$aUsers = $statistics->getAllUserStats($_POST['query']); $aUsers = $statistics->getAllUserStats($_POST['query']);
// Add additional stats to each user // 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) { foreach ($aUsers as $iKey => $aUser) {
$aBalance = $transaction->getBalance($aUser['id']); $aBalance = $transaction->getBalance($aUser['id']);
$aUser['balance'] = $aBalance['confirmed']; $aUser['balance'] = $aBalance['confirmed'];
$aUser['hashrate'] = $statistics->getUserHashrate($aUser['id']); $aUser['hashrate'] = $statistics->getUserHashrate($aUser['id']);
if ($aUser['shares'] > 0) { $aUser['estimates'] = $statistics->getUserEstimates($aRoundShares, $aUser['shares'], $aUser['donate_percent'], $aUser['no_fees']);
$aUser['payout']['est_block'] = round(( (int)$aUser['shares'] / (int)$aRoundShares['valid'] ) * (int)$config['reward'], 3);
$aUser['payout']['est_fee'] = round(($config['fees'] / 100) * $aUser['payout']['est_block'], 3);
$aUser['payout']['est_donation'] = round((( $aUser['donate_percent'] / 100) * ($aUser['payout']['est_block'] - $aUser['payout']['est_fee'])), 3);
$aUser['payout']['est_payout'] = round($aUser['payout']['est_block'] - $aUser['payout']['est_donation'] - $aUser['payout']['est_fee'], 3);
} else {
$aUser['payout']['est_block'] = 0;
$aUser['payout']['est_fee'] = 0;
$aUser['payout']['est_donation'] = 0;
$aUser['payout']['est_payout'] = 0;
}
$aUsers[$iKey] = $aUser; $aUsers[$iKey] = $aUser;
} }
// Assign our variables // Assign our variables

View File

@ -13,13 +13,14 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
$debug->append('No cached version available, fetching from backend', 3); $debug->append('No cached version available, fetching from backend', 3);
if ($bitcoin->can_connect() === true){ if ($bitcoin->can_connect() === true){
$dBalance = $bitcoin->query('getbalance'); $dBalance = $bitcoin->query('getbalance');
$dGetInfo = $bitcoin->query('getinfo'); $aGetInfo = $bitcoin->query('getinfo');
if (is_array($dGetInfo) && array_key_exists('newmint', $dGetInfo)) { if (is_array($aGetInfo) && array_key_exists('newmint', $aGetInfo)) {
$dNewmint = $dGetInfo['newmint']; $dNewmint = $aGetInfo['newmint'];
} else { } else {
$dNewmint = -1; $dNewmint = -1;
} }
} else { } else {
$aGetInfo = array('errors' => 'Unable to connect');
$dBalance = 0; $dBalance = 0;
$dNewmint = -1; $dNewmint = -1;
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg'); $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
@ -45,6 +46,7 @@ $smarty->assign("BALANCE", $dBalance);
$smarty->assign("COLDCOINS", $dColdCoins); $smarty->assign("COLDCOINS", $dColdCoins);
$smarty->assign("LOCKED", $dLockedBalance); $smarty->assign("LOCKED", $dLockedBalance);
$smarty->assign("NEWMINT", $dNewmint); $smarty->assign("NEWMINT", $dNewmint);
$smarty->assign("COININFO", $aGetInfo);
// Tempalte specifics // Tempalte specifics
$smarty->assign("CONTENT", "default.tpl"); $smarty->assign("CONTENT", "default.tpl");

View File

@ -3,8 +3,22 @@
// Make sure we are called from index.php // Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt'); if (!defined('SECURITY')) die('Hacking attempt');
// Check if the API is activated // Check if the system is enabled
$api->isActive(); if ($setting->getValue('disable_dashboard_api')) {
echo $api->get_json(array('error' => 'disabled'));
die();
}
// System load check
if ($load = @sys_getloadavg()) {
if (isset($config['system']['load']['max']) && $load[0] > $config['system']['load']['max']) {
header('HTTP/1.1 503 Too busy, try again later');
die('Server too busy. Please try again later.');
}
}
// Supress master template
$supress_master = 1;
// Check user token and access level permissions // Check user token and access level permissions
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']); $user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
@ -32,27 +46,85 @@ $dPoolHashrate = $statistics->getCurrentHashrate($interval);
if ($dPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $dPoolHashrate; if ($dPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $dPoolHashrate;
$dPersonalHashrate = $statistics->getUserHashrate($user_id, $interval); $dPersonalHashrate = $statistics->getUserHashrate($user_id, $interval);
$dPersonalSharerate = $statistics->getUserSharerate($user_id, $interval); $dPersonalSharerate = $statistics->getUserSharerate($user_id, $interval);
$dPersonalShareDifficulty = $statistics->getUserShareDifficulty($user_id, $interval);
$statistics->setGetCache(true); $statistics->setGetCache(true);
// Use caches for this one // Use caches for this one
$aUserRoundShares = $statistics->getUserShares($user_id); $aUserRoundShares = $statistics->getUserShares($user_id);
$aRoundShares = $statistics->getRoundShares(); $aRoundShares = $statistics->getRoundShares();
$aEstimates = $statistics->getUserEstimates($aRoundShares, $aUserRoundShares, $user->getUserDonatePercent($user_id), $user->getUserNoFee($user_id));
if ($config['payout_system'] != 'pps') {
$aEstimates = $statistics->getUserEstimates($aRoundShares, $aUserRoundShares, $user->getUserDonatePercent($user_id), $user->getUserNoFee($user_id));
$dUnpaidShares = 0;
} else {
$dUnpaidShares = $statistics->getUserUnpaidPPSShares($user_id, $setting->getValue('pps_last_share_id'));
if ($config['pps']['reward']['type'] == 'blockavg' && $block->getBlockCount() > 0) {
$pps_reward = round($block->getAvgBlockReward($config['pps']['blockavg']['blockcount']));
} else {
if ($config['pps']['reward']['type'] == 'block') {
if ($aLastBlock = $block->getLast()) {
$pps_reward = $aLastBlock['amount'];
} else {
$pps_reward = $config['pps']['reward']['default'];
}
} else {
$pps_reward = $config['pps']['reward']['default'];
}
}
$ppsvalue = round($pps_reward / (pow(2,32) * $dDifficulty) * pow(2, $config['target_bits']), 12);
$aEstimates = $statistics->getUserEstimates($dPersonalSharerate, $dPersonalShareDifficulty, $user->getUserDonatePercent($user_id), $user->getUserNoFee($user_id), $ppsvalue);
}
$iTotalRoundShares = $aRoundShares['valid'] + $aRoundShares['invalid'];
if ($iTotalRoundShares > 0) {
$dUserInvalidPercent = round($aUserRoundShares['invalid'] / $iTotalRoundShares * 100, 2);
$dPoolInvalidPercent = round($aRoundShares['invalid'] / $iTotalRoundShares * 100, 2);
} else {
$dUserInvalidPercent = 0;
$dPoolInvalidPercent = 0;
}
// Apply pool modifiers // Apply pool modifiers
$dPersonalHashrateAdjusted = $dPersonalHashrate * $dPersonalHashrateModifier; $dPersonalHashrateAdjusted = $dPersonalHashrate * $dPersonalHashrateModifier;
$dPoolHashrateAdjusted = $dPoolHashrate * $dPoolHashrateModifier; $dPoolHashrateAdjusted = $dPoolHashrate * $dPoolHashrateModifier;
$dNetworkHashrateAdjusted = $dNetworkHashrate / 1000 * $dNetworkHashrateModifier; $dNetworkHashrateAdjusted = $dNetworkHashrate / 1000 * $dNetworkHashrateModifier;
// Worker information
$aWorkers = $worker->getWorkers($user_id, $interval);
// Coin price
$aPrice = $setting->getValue('price');
// Round progress
$iEstShares = $statistics->getEstimatedShares($dDifficulty);
if ($iEstShares > 0 && $aRoundShares['valid'] > 0) {
$dEstPercent = round(100 / $iEstShares * $aRoundShares['valid'], 2);
} else {
$dEstPercent = 0;
}
// Output JSON format // Output JSON format
$data = array( $data = array(
'raw' => array( 'personal' => array( 'hashrate' => $dPersonalHashrate ), 'pool' => array( 'hashrate' => $dPoolHashrate ), 'network' => array( 'hashrate' => $dNetworkHashrate / 1000 ) ), 'raw' => array( 'personal' => array( 'hashrate' => $dPersonalHashrate ), 'pool' => array( 'hashrate' => $dPoolHashrate ), 'network' => array( 'hashrate' => $dNetworkHashrate / 1000 ) ),
'personal' => array ( 'hashrate' => $dPersonalHashrateAdjusted, 'sharerate' => $dPersonalSharerate, 'shares' => $aUserRoundShares, 'balance' => $transaction->getBalance($user_id), 'estimates' => $aEstimates), 'personal' => array (
'pool' => array( 'hashrate' => $dPoolHashrateAdjusted, 'shares' => $aRoundShares ), 'hashrate' => $dPersonalHashrateAdjusted, 'sharerate' => $dPersonalSharerate, 'sharedifficulty' => $dPersonalShareDifficulty,
'shares' => array('valid' => $aUserRoundShares['valid'], 'invalid' => $aUserRoundShares['invalid'], 'invalid_percent' => $dUserInvalidPercent, 'unpaid' => $dUnpaidShares ),
'balance' => $transaction->getBalance($user_id), 'estimates' => $aEstimates, 'workers' => $aWorkers ),
'pool' => array(
'info' => array(
'name' => $setting->getValue('website_name'),
'currency' => $config['currency']
),
'workers' => $worker->getCountAllActiveWorkers(), 'hashrate' => $dPoolHashrateAdjusted,
'shares' => array( 'valid' => $aRoundShares['valid'], 'invalid' => $aRoundShares['invalid'], 'invalid_percent' => $dPoolInvalidPercent, 'estimated' => $iEstShares, 'progress' => $dEstPercent ),
'price' => $aPrice,
'difficulty' => pow(2, $config['difficulty'] - 16),
'target_bits' => $config['difficulty']
),
'system' => array( 'load' => sys_getloadavg() ),
'network' => array( 'hashrate' => $dNetworkHashrateAdjusted, 'difficulty' => $dDifficulty, 'block' => $iBlock ), 'network' => array( 'hashrate' => $dNetworkHashrateAdjusted, 'difficulty' => $dDifficulty, 'block' => $iBlock ),
); );
echo $api->get_json($data);
// Supress master template echo $api->get_json($data);
$supress_master = 1;
?> ?>

View File

@ -0,0 +1,64 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
// Check if the API is activated
$api->isActive();
// Fetch RPC information
if ($bitcoin->can_connect() === true) {
$dNetworkHashrate = $bitcoin->getnetworkhashps();
$dDifficulty = $bitcoin->getdifficulty();
$iBlock = $bitcoin->getblockcount();
} else {
$dNetworkHashrate = 0;
$dDifficulty = 1;
$iBlock = 0;
}
// Some settings
if ( ! $interval = $setting->getValue('statistics_ajax_data_interval')) $interval = 300;
if ( ! $dPoolHashrateModifier = $setting->getValue('statistics_pool_hashrate_modifier') ) $dPoolHashrateModifier = 1;
if ( ! $dNetworkHashrateModifier = $setting->getValue('statistics_network_hashrate_modifier') ) $dNetworkHashrateModifier = 1;
// Fetch raw data
$statistics->setGetCache(false);
$dPoolHashrate = $statistics->getCurrentHashrate($interval);
if ($dPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $dPoolHashrate;
$statistics->setGetCache(true);
// Apply pool modifiers
$dPoolHashrateAdjusted = $dPoolHashrate * $dPoolHashrateModifier;
$dNetworkHashrateAdjusted = $dNetworkHashrate / 1000 * $dNetworkHashrateModifier;
// Use caches for this one
$aRoundShares = $statistics->getRoundShares();
$iTotalRoundShares = $aRoundShares['valid'] + $aRoundShares['invalid'];
if ($iTotalRoundShares > 0) {
$dPoolInvalidPercent = round($aRoundShares['invalid'] / $iTotalRoundShares * 100, 2);
} else {
$dUserInvalidPercent = 0;
$dPoolInvalidPercent = 0;
}
// Round progress
$iEstShares = $statistics->getEstimatedShares($dDifficulty);
if ($iEstShares > 0 && $aRoundShares['valid'] > 0) {
$dEstPercent = round(100 / $iEstShares * $aRoundShares['valid'], 2);
} else {
$dEstPercent = 0;
}
// Output JSON format
$data = array(
'raw' => array( 'workers' => $worker->getCountAllActiveWorkers(), 'pool' => array( 'hashrate' => $dPoolHashrate ) ),
'pool' => array( 'workers' => $worker->getCountAllActiveWorkers(), 'hashrate' => $dPoolHashrateAdjusted, 'estimated' => $iEstShares, 'progress' => $dEstPercent ),
'network' => array( 'hashrate' => $dNetworkHashrateAdjusted, 'difficulty' => $dDifficulty, 'block' => $iBlock ),
);
echo $api->get_json($data);
// Supress master template
$supress_master = 1;
?>

View File

@ -47,6 +47,7 @@ if (!empty($aLastBlock)) {
// Output JSON format // Output JSON format
$data = array( $data = array(
'pool_name' => $setting->getValue('website_name'),
'hashrate' => $iCurrentPoolHashrate, 'hashrate' => $iCurrentPoolHashrate,
'efficiency' => $dEfficiency, 'efficiency' => $dEfficiency,
'workers' => $worker->getCountAllActiveWorkers(), 'workers' => $worker->getCountAllActiveWorkers(),

View File

@ -0,0 +1,65 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
// Check user token and access level permissions
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
// Some settings
if ( ! $interval = $setting->getValue('statistics_ajax_data_interval')) $interval = 300;
// Fetch raw data
$aContributorsHashes = $statistics->getTopContributors('hashes', 15);
$aContributorsShares = $statistics->getTopContributors('shares', 15);
// Honor the anonymous flag
foreach ($aContributorsHashes as $iKey => $aData) {
if ($user->isAdmin($user_id)) {
$aContributorsHashes = array(
'account' => $aData['account'],
'hashrate' => $aData['hashrate']
);
} else if ($aData['is_anonymous'] == 1) {
$aContributorsHashes = array(
'account' => 'anonymous',
'hashrate' => $aData['hashrate']
);
} else {
$aContributorsHashes = array(
'account' => $aData['account'],
'hashrate' => $aData['hashrate']
);
}
}
// Honor the anonymous flag
foreach ($aContributorsShares as $iKey => $aData) {
if ($user->isAdmin($user_id)) {
$aContributorsShares[$iKey] = array(
'account' => $aData['account'],
'shares' => $aData['shares']
);
} else if ($aData['is_anonymous'] == 1) {
$aContributorsShares[$iKey] = array(
'account' => 'anonymous',
'shares' => $aData['shares']
);
} else {
$aContributorsShares[$iKey] = array(
'account' => $aData['account'],
'shares' => $aData['shares']
);
}
}
// Output JSON format
$data = array(
'hashes' => $aContributorsHashes,
'shares' => $aContributorsShares
);
echo $api->get_json($data);
// Supress master template
$supress_master = 1;
?>

View File

@ -9,12 +9,16 @@ $api->isActive();
// Check user token // Check user token
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']); $user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
// Fetch transaction summary
$aTransactionSummary = $transaction->getTransactionSummary($user_id);
// Output JSON format // Output JSON format
$data = array( $data = array(
'username' => $user->getUsername($user_id), 'username' => $user->getUsername($user_id),
'shares' => $statistics->getUserShares($user_id), 'shares' => $statistics->getUserShares($user_id),
'hashrate' => $statistics->getUserHashrate($user_id), 'hashrate' => $statistics->getUserHashrate($user_id),
'sharerate' => $statistics->getUserSharerate($user_id) 'sharerate' => $statistics->getUserSharerate($user_id),
'transactions' => $aTransactionSummary
); );
echo $api->get_json($data); echo $api->get_json($data);

View File

@ -6,35 +6,28 @@ if (!defined('SECURITY')) die('Hacking attempt');
if ($user->isAuthenticated()) { if ($user->isAuthenticated()) {
if (! $interval = $setting->getValue('statistics_ajax_data_interval')) $interval = 300; if (! $interval = $setting->getValue('statistics_ajax_data_interval')) $interval = 300;
// Defaults to get rid of PHP Notice warnings // Defaults to get rid of PHP Notice warnings
$dNetworkHashrate = 0;
$dDifficulty = 1; $dDifficulty = 1;
$aRoundShares = 1; $aRoundShares = 1;
// Only run these if the user is logged in
$aRoundShares = $statistics->getRoundShares(); $aRoundShares = $statistics->getRoundShares();
$dDifficulty = 1;
$dNetworkHashrate = 1;
$iBlock = 0;
if ($bitcoin->can_connect() === true) { if ($bitcoin->can_connect() === true) {
$dDifficulty = $bitcoin->query('getdifficulty'); $dDifficulty = $bitcoin->getdifficulty();
if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) $dNetworkHashrate = $bitcoin->getnetworkhashps();
$dDifficulty = $dDifficulty['proof-of-work']; $iBlock = $bitcoin->getblockcount();
}
// Always fetch this since we need for ministats header
$aRoundShares = $statistics->getRoundShares();
if ($bitcoin->can_connect() === true) {
$dDifficulty = $bitcoin->query('getdifficulty');
if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty))
$dDifficulty = $dDifficulty['proof-of-work'];
try { $dNetworkHashrate = $bitcoin->query('getnetworkhashps') / 1000; } catch (Exception $e) {
// Maybe we are SHA
try { $dNetworkHashrate = $bitcoin->query('gethashespersec') / 1000; } catch (Exception $e) {
$dNetworkHashrate = 0;
}
$dNetworkHashrate = 0;
}
} else {
$dNetworkHashrate = 0;
} }
// Fetch some data // Fetch some data
// Round progress
$iEstShares = $statistics->getEstimatedShares($dDifficulty);
if ($iEstShares > 0 && $aRoundShares['valid'] > 0) {
$dEstPercent = round(100 / $iEstShares * $aRoundShares['valid'], 2);
} else {
$dEstPercent = 0;
}
if (!$iCurrentActiveWorkers = $worker->getCountAllActiveWorkers()) $iCurrentActiveWorkers = 0; if (!$iCurrentActiveWorkers = $worker->getCountAllActiveWorkers()) $iCurrentActiveWorkers = 0;
$iCurrentPoolHashrate = $statistics->getCurrentHashrate(); $iCurrentPoolHashrate = $statistics->getCurrentHashrate();
$iCurrentPoolShareRate = $statistics->getCurrentShareRate(); $iCurrentPoolShareRate = $statistics->getCurrentShareRate();
@ -43,6 +36,10 @@ if ($user->isAuthenticated()) {
if ($iCurrentPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $iCurrentPoolHashrate; if ($iCurrentPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $iCurrentPoolHashrate;
// Make it available in Smarty // Make it available in Smarty
$smarty->assign('DISABLED_DASHBOARD', $setting->getValue('disable_dashboard'));
$smarty->assign('DISABLED_DASHBOARD_API', $setting->getValue('disable_dashboard_api'));
$smarty->assign('ESTIMATES', array('shares' => $iEstShares, 'percent' => $dEstPercent));
$smarty->assign('NETWORK', array('difficulty' => $dDifficulty, 'block' => $iBlock));
$smarty->assign('INTERVAL', $interval / 60); $smarty->assign('INTERVAL', $interval / 60);
$smarty->assign('CONTENT', 'default.tpl'); $smarty->assign('CONTENT', 'default.tpl');
} }

View File

@ -0,0 +1,9 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
// Tempalte specifics
$smarty->assign("CONTENT", "default.tpl");
?>

View File

@ -0,0 +1,9 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
// Tempalte specifics
$smarty->assign("CONTENT", "default.tpl");
?>

View File

@ -4,6 +4,10 @@
if (!defined('SECURITY')) if (!defined('SECURITY'))
die('Hacking attempt'); die('Hacking attempt');
$smarty->assign("SITESTRATUMPORT", $config['gettingstarted']['stratumport']);
$smarty->assign("SITECOINNAME", $config['gettingstarted']['coinname']);
$smarty->assign("SITECOINURL", $config['gettingstarted']['coinurl']);
// Tempalte specifics // Tempalte specifics
$smarty->assign("CONTENT", "default.tpl"); $smarty->assign("CONTENT", "default.tpl");
?> ?>

View File

@ -16,6 +16,8 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
$aNews[$key]['content'] = Markdown::defaultTransform($aData['content']); $aNews[$key]['content'] = Markdown::defaultTransform($aData['content']);
} }
} }
$smarty->assign("HIDEAUTHOR", $setting->getValue('acl_hide_news_author'));
$smarty->assign("NEWS", $aNews); $smarty->assign("NEWS", $aNews);
} else { } else {
$debug->append('Using cached page', 3); $debug->append('Using cached page', 3);

View File

@ -7,7 +7,8 @@ if ($setting->getValue('maintenance') && !$user->isAdmin($user->getUserId($_POST
$_SESSION['POPUP'][] = array('CONTENT' => 'You are not allowed to login during maintenace.', 'TYPE' => 'info'); $_SESSION['POPUP'][] = array('CONTENT' => 'You are not allowed to login during maintenace.', 'TYPE' => 'info');
} else if ($user->checkLogin(@$_POST['username'], @$_POST['password']) ) { } else if ($user->checkLogin(@$_POST['username'], @$_POST['password']) ) {
empty($_POST['to']) ? $to = $_SERVER['PHP_SELF'] : $to = $_POST['to']; empty($_POST['to']) ? $to = $_SERVER['PHP_SELF'] : $to = $_POST['to'];
$location = @$_SERVER['HTTPS'] === true ? 'https' : 'http' . '://' . $_SERVER['SERVER_NAME'] . $to; $port = ($_SERVER["SERVER_PORT"] == "80" or $_SERVER["SERVER_PORT"] == "443") ? "" : (":".$_SERVER["SERVER_PORT"]);
$location = @$_SERVER['HTTPS'] === true ? 'https://' . $_SERVER['SERVER_NAME'] . $port . $to : 'http://' . $_SERVER['SERVER_NAME'] . $port . $to;
if (!headers_sent()) header('Location: ' . $location); if (!headers_sent()) header('Location: ' . $location);
exit('<meta http-equiv="refresh" content="0; url=' . $location . '"/>'); exit('<meta http-equiv="refresh" content="0; url=' . $location . '"/>');
} else if (@$_POST['username'] && @$_POST['password']) { } else if (@$_POST['username'] && @$_POST['password']) {

View File

@ -16,6 +16,7 @@ if (is_array($aNews)) {
} }
// Tempalte specifics // Tempalte specifics
$smarty->assign("HIDEAUTHOR", $settings->getValue('acl_hide_news_author'));
$smarty->assign("NEWS", $aNews); $smarty->assign("NEWS", $aNews);
$smarty->assign("CONTENT", "default.tpl"); $smarty->assign("CONTENT", "default.tpl");
?> ?>

View File

@ -23,7 +23,7 @@ if ($setting->getValue('disable_invitations') && $setting->getValue('lock_regist
if ($rsp->is_valid) { if ($rsp->is_valid) {
$smarty->assign("RECAPTCHA", recaptcha_get_html($setting->getValue('recaptcha_public_key'))); $smarty->assign("RECAPTCHA", recaptcha_get_html($setting->getValue('recaptcha_public_key')));
isset($_POST['token']) ? $token = $_POST['token'] : $token = ''; isset($_POST['token']) ? $token = $_POST['token'] : $token = '';
if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $token)) { if ($user->register(@$_POST['username'], @$_POST['password1'], @$_POST['password2'], @$_POST['pin'], @$_POST['email1'], @$_POST['email2'], $token)) {
! $setting->getValue('accounts_confirm_email_disabled') ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); ! $setting->getValue('accounts_confirm_email_disabled') ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login');
} else { } else {
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg');
@ -39,7 +39,7 @@ if ($setting->getValue('disable_invitations') && $setting->getValue('lock_regist
// Captcha disabled // Captcha disabled
} else { } else {
isset($_POST['token']) ? $token = $_POST['token'] : $token = ''; isset($_POST['token']) ? $token = $_POST['token'] : $token = '';
if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $token)) { if ($user->register(@$_POST['username'], @$_POST['password1'], @$_POST['password2'], @$_POST['pin'], @$_POST['email1'], @$_POST['email2'], $token)) {
! $setting->getValue('accounts_confirm_email_disabled') ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); ! $setting->getValue('accounts_confirm_email_disabled') ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login');
} else { } else {
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg');

View File

@ -0,0 +1,29 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
// Grab Block Finder
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
$debug->append('No cached version available, fetching from backend', 3);
$getBlocksSolvedbyAccount = $statistics->getBlocksSolvedbyAccount();
$smarty->assign("BLOCKSSOLVEDBYACCOUNT", $getBlocksSolvedbyAccount);
if(isset($_SESSION['USERDATA']['id'])){
$getBlocksSolvedbyWorker = $statistics->getBlocksSolvedbyWorker($_SESSION['USERDATA']['id']);
$smarty->assign("BLOCKSSOLVEDBYWORKER", $getBlocksSolvedbyWorker);
}
} else {
$debug->append('Using cached page', 3);
}
// Public / private page detection
if ($setting->getValue('acl_blockfinder_statistics')) {
$smarty->assign("CONTENT", "finder.tpl");
} else if ($user->isAuthenticated()) {
$smarty->assign("CONTENT", "finder.tpl");
} else {
$smarty->assign("CONTENT", "default.tpl");
}
?>

View File

@ -8,11 +8,84 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
$debug->append('No cached version available, fetching from backend', 3); $debug->append('No cached version available, fetching from backend', 3);
// Grab the last blocks found // Grab the last blocks found
$setting->getValue('statistics_block_count') ? $iLimit = $setting->getValue('statistics_block_count') : $iLimit = 20; $setting->getValue('statistics_block_count') ? $iLimit = $setting->getValue('statistics_block_count') : $iLimit = 20;
$aBlocksFoundData = $statistics->getBlocksFound($iLimit); if (@$_REQUEST['limit'] && !empty($_REQUEST['limit']) && is_numeric($_REQUEST['limit'])) {
$iLimit = $_REQUEST['limit'];
if ( $iLimit > 40 )
$iLimit = 40;
}
$iHeight = 0;
if (@$_REQUEST['next'] && !empty($_REQUEST['height']) && is_numeric($_REQUEST['height'])) {
$iHeight = @$roundstats->getNextBlockForStats($_REQUEST['height'], $iLimit);
if (!$iHeight) {
$iBlock = $block->getLast();
$iHeight = $iBlock['height'];
}
} else if (@$_REQUEST['prev'] && !empty($_REQUEST['height']) && is_numeric($_REQUEST['height'])) {
$iHeight = $_REQUEST['height'];
} else if (empty($_REQUEST['height'])) {
$aBlock = $block->getLast();
$iHeight = $aBlock['height'];
}
$test = false;
if (@$_REQUEST['test'] && $user->isAdmin($_SESSION['USERDATA']['id'])) {
$test = true;
$count = 10;
$percent = 30;
if (@$_REQUEST['count'] && is_numeric($_REQUEST['count']))
$count = $_REQUEST['count'];
if (@$_REQUEST['percent'] && is_numeric($_REQUEST['percent']))
$percent = $_REQUEST['percent'];
}
$aBlocksFoundData = $statistics->getBlocksFoundHeight($iHeight, $iLimit);
$use_average = false;
if ($config['payout_system'] == 'pplns') {
foreach($aBlocksFoundData as $key => $aData) {
$aBlocksFoundData[$key]['pplns_shares'] = $roundstats->getPPLNSRoundShares($aData['height']);
if ($setting->getValue('statistics_show_block_average') && !$test) {
$aBlocksFoundData[$key]['block_avg'] = round($block->getAvgBlockShares($aData['height'], $config['pplns']['blockavg']['blockcount']));
$use_average = true;
}
}
} else if ($config['payout_system'] == 'prop' || $config['payout_system'] == 'pps') {
if ($setting->getValue('statistics_show_block_average') && !$test) {
foreach($aBlocksFoundData as $key => $aData) {
$aBlocksFoundData[$key]['block_avg'] = round($block->getAvgBlockShares($aData['height'], $config['pplns']['blockavg']['blockcount']));
$use_average = true;
}
}
}
// show test data in graph
if ($test) {
$use_average = true;
foreach($aBlocksFoundData as $key => $aData) {
if ($_REQUEST['test'] == 1) {
$aBlocksFoundData[$key]['block_avg'] = round($block->getAvgBlockShares($aData['height'], $count));
} else if ($_REQUEST['test'] == 2) {
$aBlocksFoundData[$key]['block_avg'] = round($block->getAvgBlockShares($aData['height'], $count) * (100 - $percent) / 100 + $aData['shares'] * $percent / 100);
}
}
}
$iHours = 24;
$aPoolStatistics = $statistics->getPoolStatsHours($iHours);
$iFirstBlockFound = $statistics->getFirstBlockFound();
$iTimeSinceFirstBlockFound = (time() - $iFirstBlockFound);
// Coin target block generation time, default to 150 (2.5 minutes)
@$config['cointarget'] ? $smarty->assign("COINGENTIME", $config['cointarget']) : $smarty->assign("COINGENTIME", 150);
// Past blocks found, max 4 weeks back
$iFoundBlocksByTime = $statistics->getLastBlocksbyTime();
// Propagate content our template // Propagate content our template
$smarty->assign("FIRSTBLOCKFOUND", $iTimeSinceFirstBlockFound);
$smarty->assign("LASTBLOCKSBYTIME", $iFoundBlocksByTime);
$smarty->assign("BLOCKSFOUND", $aBlocksFoundData); $smarty->assign("BLOCKSFOUND", $aBlocksFoundData);
$smarty->assign("BLOCKLIMIT", $iLimit); $smarty->assign("BLOCKLIMIT", $iLimit);
$smarty->assign("USEBLOCKAVERAGE", $use_average);
$smarty->assign("POOLSTATS", $aPoolStatistics);
} else { } else {
$debug->append('Using cached page', 3); $debug->append('Using cached page', 3);
} }

View File

@ -22,7 +22,7 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
// Top hash contributors // Top hash contributors
$aContributorsHashes = $statistics->getTopContributors('hashes', 15); $aContributorsHashes = $statistics->getTopContributors('hashes', 15);
// Grab the last 10 blocks found // Grab the last 5 blocks found as a quick overview
$iLimit = 5; $iLimit = 5;
$aBlocksFoundData = $statistics->getBlocksFound($iLimit); $aBlocksFoundData = $statistics->getBlocksFound($iLimit);
count($aBlocksFoundData) > 0 ? $aBlockData = $aBlocksFoundData[0] : $aBlockData = array(); count($aBlocksFoundData) > 0 ? $aBlockData = $aBlocksFoundData[0] : $aBlockData = array();
@ -34,9 +34,8 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
$iCurrentPoolHashrate > 0 ? $iEstTime = $dDifficulty * pow(2,32) / ($iCurrentPoolHashrate * 1000) : $iEstTime = 0; $iCurrentPoolHashrate > 0 ? $iEstTime = $dDifficulty * pow(2,32) / ($iCurrentPoolHashrate * 1000) : $iEstTime = 0;
// Time since last block // Time since last block
$now = new DateTime( "now" );
if (!empty($aBlockData)) { if (!empty($aBlockData)) {
$dTimeSinceLast = ($now->getTimestamp() - $aBlockData['time']); $dTimeSinceLast = (time() - $aBlockData['time']);
if ($dTimeSinceLast < 0) $dTimeSinceLast = 0; if ($dTimeSinceLast < 0) $dTimeSinceLast = 0;
} else { } else {
$dTimeSinceLast = 0; $dTimeSinceLast = 0;

View File

@ -6,26 +6,40 @@ if (!defined('SECURITY')) die('Hacking attempt');
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
$debug->append('No cached version available, fetching from backend', 3); $debug->append('No cached version available, fetching from backend', 3);
if (@$_REQUEST['next'] && !empty($_REQUEST['height'])) { if (@$_REQUEST['search']) {
$iKey = $roundstats->getNextBlock($_REQUEST['height']); $_REQUEST['height'] = $roundstats->searchForBlockHeight($_REQUEST['search']);
} else if (@$_REQUEST['prev'] && !empty($_REQUEST['height'])) {
$iKey = $roundstats->getPreviousBlock($_REQUEST['height']);
} else {
if (empty($_REQUEST['height'])) {
$iBlock = $block->getLast();
$iKey = $iBlock['height'];
} else {
$iKey = $_REQUEST['height'];
}
} }
$aDetailsForBlockHeight = $roundstats->getDetailsForBlockHeight($iKey, $user->isAdmin(@$_SESSION['USERDATA']['id'])); if (@$_REQUEST['next'] && !empty($_REQUEST['height'])) {
$aRoundShareStats = $roundstats->getRoundStatsForAccounts($iKey, $user->isAdmin(@$_SESSION['USERDATA']['id'])); $iHeight = @$roundstats->getNextBlock($_REQUEST['height']);
if (!$iHeight) {
if ($user->isAdmin(@$_SESSION['USERDATA']['id']) || $setting->getValue('acl_round_transactions')) { $iBlock = $block->getLast();
$aUserRoundTransactions = $roundstats->getAllRoundTransactions($iKey, @$_SESSION['USERDATA']['id']); $iHeight = $iBlock['height'];
}
} else if (@$_REQUEST['prev'] && !empty($_REQUEST['height'])) {
$iHeight = $roundstats->getPreviousBlock($_REQUEST['height']);
} else if (empty($_REQUEST['height'])) {
$iBlock = $block->getLast();
$iHeight = $iBlock['height'];
} else { } else {
$aUserRoundTransactions = $roundstats->getUserRoundTransactions($iKey, @$_SESSION['USERDATA']['id']); $iHeight = $_REQUEST['height'];
}
$_REQUEST['height'] = $iHeight;
$iPPLNSShares = 0;
$aDetailsForBlockHeight = $roundstats->getDetailsForBlockHeight($iHeight);
$aRoundShareStats = $roundstats->getRoundStatsForAccounts($iHeight);
$aUserRoundTransactions = $roundstats->getAllRoundTransactions($iHeight);
if ($config['payout_system'] == 'pplns') {
$aPPLNSRoundShares = $roundstats->getPPLNSRoundStatsForAccounts($iHeight);
foreach($aPPLNSRoundShares as $aData) {
$iPPLNSShares += $aData['pplns_valid'];
}
$block_avg = $block->getAvgBlockShares($iHeight, $config['pplns']['blockavg']['blockcount']);
$smarty->assign('PPLNSROUNDSHARES', $aPPLNSRoundShares);
$smarty->assign("PPLNSSHARES", $iPPLNSShares);
$smarty->assign("BLOCKAVGCOUNT", $config['pplns']['blockavg']['blockcount']);
$smarty->assign("BLOCKAVERAGE", $block_avg );
} }
// Propagate content our template // Propagate content our template

View File

@ -0,0 +1,27 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
$debug->append('No cached version available, fetching from backend', 3);
if ($setting->getValue('monitoring_uptimerobot_api_keys') && $aStatus = $monitoring->getUptimeRobotStatus()) {
$smarty->assign("STATUS", $aStatus);
$smarty->assign("UPDATED", $setting->getValue('monitoring_uptimerobot_lastcheck'));
$smarty->assign("CODES", array(
0 => 'Paused',
1 => 'Unchecked',
2 => 'Up',
8 => 'Down',
9 => 'Down'
));
$smarty->assign("CONTENT", "default.tpl");
} else {
$_SESSION['POPUP'][] = array('CONTENT' => 'UptimeRobot API Key not configured.', 'TYPE' => 'info');
$smarty->assign("CONTENT", "");
}
} else {
$debug->append('Using cached page', 3);
}
?>

View File

@ -65,9 +65,12 @@ $aGlobal = array(
'price' => $setting->getValue('price'), 'price' => $setting->getValue('price'),
'disable_mp' => $setting->getValue('disable_mp'), 'disable_mp' => $setting->getValue('disable_mp'),
'config' => array( 'config' => array(
'algorithm' => $config['algorithm'],
'target_bits' => $config['target_bits'],
'accounts' => $config['accounts'], 'accounts' => $config['accounts'],
'disable_invitations' => $setting->getValue('disable_invitations'), 'disable_invitations' => $setting->getValue('disable_invitations'),
'disable_notifications' => $setting->getValue('disable_notifications'), 'disable_notifications' => $setting->getValue('disable_notifications'),
'monitoring_uptimerobot_api_keys' => $setting->getValue('monitoring_uptimerobot_api_keys'),
'statistics_ajax_refresh_interval' => $statistics_ajax_refresh_interval, 'statistics_ajax_refresh_interval' => $statistics_ajax_refresh_interval,
'price' => array( 'currency' => $config['price']['currency'] ), 'price' => array( 'currency' => $config['price']['currency'] ),
'targetdiff' => $config['difficulty'], 'targetdiff' => $config['difficulty'],
@ -89,31 +92,21 @@ $aGlobal['website']['email'] = $setting->getValue('website_email');
$aGlobal['website']['api']['disabled'] = $setting->getValue('disable_api'); $aGlobal['website']['api']['disabled'] = $setting->getValue('disable_api');
$aGlobal['website']['blockexplorer']['disabled'] = $setting->getValue('website_blockexplorer_disabled'); $aGlobal['website']['blockexplorer']['disabled'] = $setting->getValue('website_blockexplorer_disabled');
$aGlobal['website']['chaininfo']['disabled'] = $setting->getValue('website_chaininfo_disabled'); $aGlobal['website']['chaininfo']['disabled'] = $setting->getValue('website_chaininfo_disabled');
$aGlobal['website']['donors']['disabled'] = $setting->getValue('disable_donors');
$aGlobal['website']['about']['disabled'] = $setting->getValue('disable_about');
$setting->getValue('website_blockexplorer_url') ? $aGlobal['website']['blockexplorer']['url'] = $setting->getValue('website_blockexplorer_url') : $aGlobal['website']['blockexplorer']['url'] = 'http://explorer.litecoin.net/block/'; $setting->getValue('website_blockexplorer_url') ? $aGlobal['website']['blockexplorer']['url'] = $setting->getValue('website_blockexplorer_url') : $aGlobal['website']['blockexplorer']['url'] = 'http://explorer.litecoin.net/block/';
$setting->getValue('website_chaininfo_url') ? $aGlobal['website']['chaininfo']['url'] = $setting->getValue('website_chaininfo_url') : $aGlobal['website']['chaininfo']['url'] = 'http://allchains.info'; $setting->getValue('website_chaininfo_url') ? $aGlobal['website']['chaininfo']['url'] = $setting->getValue('website_chaininfo_url') : $aGlobal['website']['chaininfo']['url'] = 'http://allchains.info';
// Google Analytics
$aGlobal['statistics']['analytics']['enabled'] = $setting->getValue('statistics_analytics_enabled');
$aGlobal['statistics']['analytics']['code'] = $setting->getValue('statistics_analytics_code');
// ACLs // ACLs
$aGlobal['acl']['pool']['statistics'] = $setting->getValue('acl_pool_statistics'); $aGlobal['acl']['pool']['statistics'] = $setting->getValue('acl_pool_statistics');
$aGlobal['acl']['block']['statistics'] = $setting->getValue('acl_block_statistics'); $aGlobal['acl']['block']['statistics'] = $setting->getValue('acl_block_statistics');
$aGlobal['acl']['round']['statistics'] = $setting->getValue('acl_round_statistics'); $aGlobal['acl']['round']['statistics'] = $setting->getValue('acl_round_statistics');
$aGlobal['acl']['blockfinder']['statistics'] = $setting->getValue('acl_blockfinder_statistics');
// We support some dynamic reward targets but fall back to our fixed value $aGlobal['acl']['uptime']['statistics'] = $setting->getValue('acl_uptime_statistics');
// Special calculations for PPS Values based on reward_type setting and/or available blocks
if ($config['pps']['reward']['type'] == 'blockavg' && $block->getBlockCount() > 0) {
$pps_reward = round($block->getAvgBlockReward($config['pps']['blockavg']['blockcount']));
} else {
if ($config['pps']['reward']['type'] == 'block') {
if ($aLastBlock = $block->getLast()) {
$pps_reward = $aLastBlock['amount'];
} else {
$pps_reward = $config['pps']['reward']['default'];
}
} else {
$pps_reward = $config['pps']['reward']['default'];
}
}
$aGlobal['ppsvalue'] = number_format(round($pps_reward / (pow(2,32) * $dDifficulty) * pow(2, $config['pps_target']), 12) ,12);
// We don't want these session infos cached // We don't want these session infos cached
if (@$_SESSION['USERDATA']['id']) { if (@$_SESSION['USERDATA']['id']) {
@ -122,17 +115,16 @@ if (@$_SESSION['USERDATA']['id']) {
// Other userdata that we can cache savely // Other userdata that we can cache savely
$aGlobal['userdata']['shares'] = $statistics->getUserShares($_SESSION['USERDATA']['id']); $aGlobal['userdata']['shares'] = $statistics->getUserShares($_SESSION['USERDATA']['id']);
$aGlobal['userdata']['hashrate'] = $statistics->getUserHashrate($_SESSION['USERDATA']['id']) * $dPersonalHashrateModifier; $aGlobal['userdata']['rawhashrate'] = $statistics->getUserHashrate($_SESSION['USERDATA']['id']);
$aGlobal['userdata']['hashrate'] = $aGlobal['userdata']['rawhashrate'] * $dPersonalHashrateModifier;
$aGlobal['userdata']['sharerate'] = $statistics->getUserSharerate($_SESSION['USERDATA']['id']); $aGlobal['userdata']['sharerate'] = $statistics->getUserSharerate($_SESSION['USERDATA']['id']);
switch ($config['payout_system']) { switch ($config['payout_system']) {
case 'prop' || 'pplns': case 'prop':
// Some estimations // Some estimations
$aEstimates = $statistics->getUserEstimates($aRoundShares, $aGlobal['userdata']['shares'], $aGlobal['userdata']['donate_percent'], $aGlobal['userdata']['no_fees']); $aEstimates = $statistics->getUserEstimates($aRoundShares, $aGlobal['userdata']['shares'], $aGlobal['userdata']['donate_percent'], $aGlobal['userdata']['no_fees']);
$aGlobal['userdata']['est_block'] = $aEstimates['block']; $aGlobal['userdata']['estimates'] = $aEstimates;
$aGlobal['userdata']['est_fee'] = $aEstimates['fee']; break;
$aGlobal['userdata']['est_donation'] = $aEstimates['donation'];
$aGlobal['userdata']['est_payout'] = $aEstimates['payout'];
case 'pplns': case 'pplns':
$aGlobal['pplns']['target'] = $config['pplns']['shares']['default']; $aGlobal['pplns']['target'] = $config['pplns']['shares']['default'];
if ($aLastBlock = $block->getLast()) { if ($aLastBlock = $block->getLast()) {
@ -140,8 +132,31 @@ if (@$_SESSION['USERDATA']['id']) {
$aGlobal['pplns']['target'] = $iAvgBlockShares; $aGlobal['pplns']['target'] = $iAvgBlockShares;
} }
} }
$aEstimates = $statistics->getUserEstimates($aRoundShares, $aGlobal['userdata']['shares'], $aGlobal['userdata']['donate_percent'], $aGlobal['userdata']['no_fees']);
$aGlobal['userdata']['estimates'] = $aEstimates;
break; break;
case 'pps': case 'pps':
// We support some dynamic reward targets but fall back to our fixed value
// Special calculations for PPS Values based on reward_type setting and/or available blocks
if ($config['pps']['reward']['type'] == 'blockavg' && $block->getBlockCount() > 0) {
$pps_reward = round($block->getAvgBlockReward($config['pps']['blockavg']['blockcount']));
} else {
if ($config['pps']['reward']['type'] == 'block') {
if ($aLastBlock = $block->getLast()) {
$pps_reward = $aLastBlock['amount'];
} else {
$pps_reward = $config['pps']['reward']['default'];
}
} else {
$pps_reward = $config['pps']['reward']['default'];
}
}
$aGlobal['userdata']['pps']['unpaidshares'] = $statistics->getUserUnpaidPPSShares($_SESSION['USERDATA']['id'], $setting->getValue('pps_last_share_id'));
$aGlobal['ppsvalue'] = number_format(round($pps_reward / (pow(2, $config['target_bits']) * $dDifficulty), 12) ,12);
$aGlobal['poolppsvalue'] = $aGlobal['ppsvalue'] * pow(2, $config['difficulty'] - 16);
$aGlobal['userdata']['sharedifficulty'] = $statistics->getUserShareDifficulty($_SESSION['USERDATA']['id']);
$aGlobal['userdata']['estimates'] = $statistics->getUserEstimates($aGlobal['userdata']['sharerate'], $aGlobal['userdata']['sharedifficulty'], $aGlobal['userdata']['donate_percent'], $aGlobal['userdata']['no_fees'], $aGlobal['ppsvalue']);
break; break;
} }
@ -162,5 +177,6 @@ $smarty->assign('DEBUG', DEBUG);
// Make it available in Smarty // Make it available in Smarty
$smarty->assign('PATH', 'site_assets/' . THEME); $smarty->assign('PATH', 'site_assets/' . THEME);
$smarty->assign('GLOBALASSETS', 'site_assets/global');
$smarty->assign('GLOBAL', $aGlobal); $smarty->assign('GLOBAL', $aGlobal);
?> ?>

View File

@ -51,7 +51,13 @@ if (is_dir(INCLUDE_DIR . '/pages/')) {
} }
// Set a default action here if no page has been requested // Set a default action here if no page has been requested
$page = isset($_REQUEST['page']) && isset($arrPages[$_REQUEST['page']]) ? $_REQUEST['page'] : 'home'; if (isset($_REQUEST['page']) && isset($arrPages[$_REQUEST['page']])) {
$page = $_REQUEST['page'];
} else if (isset($_REQUEST['page']) && ! isset($arrPages[$_REQUEST['page']])) {
$page = 'error';
} else {
$page = 'home';
}
// Create our pages array from existing files // Create our pages array from existing files
if (is_dir(INCLUDE_DIR . '/pages/' . $page)) { if (is_dir(INCLUDE_DIR . '/pages/' . $page)) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 647 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 B

Some files were not shown because too many files have changed in this diff Show More