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.*.gz
/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://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.
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
with some hard earned coins feel free to donate:
* BTC address: `1HuYK6WPU8o3yWCrAaADDZPRpL5QiXitfv`
* LTC address: `Lge95QR2frp9y1wJufjUPCycVsg5gLJPW8`
* FTC address: `6jDgGaUzMVyac5uqBhJCMiFMKCtH1LagTA`
* NVC address: `4Guct6z7NVPVALHRAVn517TTmvqQve4WYr`
* FST address: `g17CfFHqNqR5JnUjtG8RNBYh2WrhEirV67`
Website Footer
==============
@ -50,8 +46,6 @@ Requirements
This setup has been tested on Ubuntu 12.04, Ubuntu 13.04 and CentOS.
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
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
* Mobile WebUI
* **NEW** VARDIFF Support
* Scrypt, **NEW** SHA256, VARDIFF Support
* Reward Systems
* Propotional
* PPS
* PPLNS
* Statistics are cached in Memcache by Cronjob for quick data access
* **NEW** New Theme
* **NEW** Live Dashboard
* **NEW** AJAX Support
* **NEW** Overhauled API
* New Theme
* Live Dashboard
* AJAX Support
* Overhauled API
* Web User accounts
* Re-Captcha protected registration form
* 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.
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
============

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 (!$share->purgeArchive()) {
$log->logError("Failed to delete archived shares, not critical but should be checked!");
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Failed to delete archived shares");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
$monitoring->endCronjob($cron_name, 'E0008', 1, true);
}
// Cron cleanup and monitoring

View File

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

View File

@ -27,10 +27,7 @@ require_once('shared.inc.php');
if ( $bitcoin->can_connect() !== true ) {
$log->logFatal("Failed to connect to RPC server\n");
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
$monitoring->endCronjob($cron_name, 'E0006', 1, true);
}
// Fetch all unconfirmed blocks
@ -54,7 +51,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
if ($aBlock['confirmations'] == $aBlockInfo['confirmations']) {
$log->logDebug(' No update needed');
} 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->setStatus($cron_name . "_message", "message", "OK");
$monitoring->setStatus($cron_name . "_status", "okerror", 0);
$monitoring->endCronjob($cron_name, 'OK', 0, false, false);
$monitoring->setStatus($cron_name . "_runtime", "time", microtime(true) - $cron_start[$cron_name]);
$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);
} else {
$log->logFatal('Unable to conenct to RPC server backend');
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
$monitoring->endCronjob($cron_name, 'E0006', 1, true);
}
// Nothing to do so bail out
@ -51,22 +48,22 @@ if (empty($aTransactions['transactions'])) {
// Let us add those blocks as unaccounted
foreach ($aTransactions['transactions'] as $iIndex => $aData) {
if ( $aData['category'] == 'generate' || $aData['category'] == 'immature' ) {
$aBlockInfo = $bitcoin->query('getblock', $aData['blockhash']);
$aBlockRPCInfo = $bitcoin->query('getblock', $aData['blockhash']);
$config['reward_type'] == 'block' ? $aData['amount'] = $aData['amount'] : $aData['amount'] = $config['reward'];
$aData['height'] = $aBlockInfo['height'];
$aData['difficulty'] = $aBlockInfo['difficulty'];
$aData['height'] = $aBlockRPCInfo['height'];
$aData['difficulty'] = $aBlockRPCInfo['difficulty'];
$log->logInfo(substr($aData['blockhash'], 0, 15) . "...\t" .
$aData['height'] . "\t" .
$aData['amount'] . "\t" .
$aData['confirmations'] . "\t\t" .
$aData['difficulty'] . "\t" .
strftime("%Y-%m-%d %H:%M:%S", $aData['time']));
if ( ! empty($aBlockInfo['flags']) && preg_match('/proof-of-stake/', $aBlockInfo['flags']) ) {
if ( ! empty($aBlockRPCInfo['flags']) && preg_match('/proof-of-stake/', $aBlockRPCInfo['flags']) ) {
$log->logInfo("Block above with height " . $aData['height'] . " not added to database, proof-of-stake block!");
continue;
}
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');
} else {
// Loop through our unaccounted blocks
$log->logInfo("Block ID\t\tHeight\tAmount\tShare ID\tShares\tFinder\tType");
$log->logInfo("Block ID\tHeight\t\tAmount\tShare ID\tShares\tFinder\tWorker\t\tType");
foreach ($aAllBlocks as $iIndex => $aBlock) {
if (empty($aBlock['share_id'])) {
// Fetch this blocks upstream ID
$aBlockInfo = $bitcoin->query('getblock', $aBlock['blockhash']);
if ($share->setUpstream($aBlockInfo, $block->getLastUpstreamId())) {
$iCurrentUpstreamId = $share->getUpstreamId();
$iAccountId = $user->getUserId($share->getUpstreamFinder());
} else {
$log->logFatal('Unable to fetch blocks upstream share, aborted:' . $share->getError());
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Unable to fetch blocks " . $aBlock['height'] . " upstream share: " . $share->getError());
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit;
}
// Fetch share information
if (!$iPreviousShareId = $block->getLastShareId()) {
if ( !$iPreviousShareId = $block->getLastShareId())
$iPreviousShareId = 0;
$log->logInfo('Unable to find highest share ID found so far, if this is your first block, this is normal.');
}
$iRoundShares = $share->getRoundShares($iPreviousShareId, $iCurrentUpstreamId);
// Store new information
if (!$block->setShareId($aBlock['id'], $iCurrentUpstreamId))
$log->logError('Failed to update share ID in database for block ' . $aBlock['height']);
if (!$block->setFinder($aBlock['id'], $iAccountId))
$log->logError('Failed to update finder account ID in database for block ' . $aBlock['height']);
if (!$block->setShares($aBlock['id'], $iRoundShares))
$log->logError('Failed to update share count in database for block ' . $aBlock['height']);
if ($config['block_bonus'] > 0 && !$transaction->addTransaction($iAccountId, $config['block_bonus'], 'Bonus', $aBlock['id'])) {
$log->logError('Failed to create Bonus transaction in database for user ' . $user->getUserName($iAccountId) . ' for block ' . $aBlock['height']);
// Fetch this blocks upstream ID
$aBlockRPCInfo = $bitcoin->query('getblock', $aBlock['blockhash']);
if ($share->findUpstreamShare($aBlockRPCInfo, $iPreviousShareId)) {
$iCurrentUpstreamId = $share->getUpstreamShareId();
// Rarely happens, but did happen once to me
if ($iCurrentUpstreamId == $iPreviousShareId) {
$log->logFatal($share->getErrorMsg('E0063'));
$monitoring->endCronjob($cron_name, 'E0063', 1, true);
}
// Out of order share detection
if ($iCurrentUpstreamId < $iPreviousShareId) {
// Fetch our offending block
$aBlockError = $block->getBlockByShareId($iPreviousShareId);
$log->logError('E0001: The block with height ' . $aBlock['height'] . ' found share ' . $iCurrentUpstreamId . ' which is < than ' . $iPreviousShareId . ' of block ' . $aBlockError['height'] . '.');
if ( !$aShareError = $share->getShareById($aBlockError['share_id']) || !$aShareCurrent = $share->getShareById($iCurrentUpstreamId)) {
// We were not able to fetch all shares that were causing this detection to trigger, bail out
$log->logFatal('E0002: Failed to fetch both offending shares ' . $iCurrentUpstreamId . ' and ' . $iPreviousShareId . '. Block height: ' . $aBlock['height']);
$monitoring->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(
@ -119,9 +130,23 @@ if (empty($aAllBlocks)) {
. $iCurrentUpstreamId . "\t\t"
. $iRoundShares . "\t"
. "[$iAccountId] " . $user->getUserName($iAccountId) . "\t"
. $iWorker . "\t"
. $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) {
// Notify users
$aAccounts = $notification->getNotificationAccountIdByType('new_block');

View File

@ -26,20 +26,13 @@ chdir(dirname(__FILE__));
require_once('shared.inc.php');
if ($setting->getValue('disable_mp') == 1) {
$log->logInfo(" auto payout disabled via admin panel");
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$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);
$log->logInfo(" manual payout disabled via admin panel");
$monitoring->endCronjob($cron_name, 'E0009', 0, true);
}
if ($bitcoin->can_connect() !== true) {
$log->logFatal(" unable to connect to RPC server, exiting");
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
$monitoring->endCronjob($cron_name, 'E0006', 1, true);
}
// Fetch outstanding payout requests
@ -53,6 +46,12 @@ if (count($aPayouts) > 0) {
$aData['coin_address'] = $user->getCoinAddress($aData['account_id']);
$aData['username'] = $user->getUserName($aData['account_id']);
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']);
try {
$aStatus = $bitcoin->validateaddress($aData['coin_address']);
@ -65,21 +64,13 @@ if (count($aPayouts) > 0) {
continue;
}
try {
$bitcoin->sendtoaddress($aData['coin_address'], $dBalance - $config['txfee']);
$txid = $bitcoin->sendtoaddress($aData['coin_address'], $dBalance - $config['txfee']);
} catch (BitcoinClientException $e) {
$log->logError('Failed to send requested balance to coin address, please check payout process');
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
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');
@ -91,7 +82,8 @@ if (count($aPayouts) > 0) {
if (!$notification->sendNotification($aData['account_id'], 'manual_payout', $aMailData))
$log->logError('Failed to send notification email to users address: ' . $aMailData['email']);
} 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');
if ($setting->getValue('disable_notifications') == 1) {
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Cron disabled by admin");
$monitoring->setStatus($cron_name . "_status", "okerror", 0);
exit(0);
$monitoring->endCronjob($cron_name, 'E0009', 0, true);
}
$log->logDebug(" IDLE Worker Notifications ...");
@ -45,9 +42,9 @@ if (empty($aWorkers)) {
$aData['subject'] = 'IDLE Worker : ' . $aWorker['username'];
$aData['worker'] = $aWorker['username'];
$aData['email'] = $user->getUserEmail($aData['username']);
$log->logInfo(" " . $aWorker['username'] . "...");
$log->logDebug(" " . $aWorker['username'] . "...");
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) {
$aData = json_decode($aNotification['data'], true);
$aWorker = $worker->getWorker($aData['id']);
$log->logInfo(" " . $aWorker['username'] . " ...");
$log->logDebug(" " . $aWorker['username'] . " ...");
if ($aWorker['hashrate'] > 0) {
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 {
$log->logInfo(" failed to update #" . $aNotification['id'] . " for " . $aWorker['username'] . "\n");
$log->logError(" failed to update #" . $aNotification['id'] . " for " . $aWorker['username'] . "\n");
}
} else {
$log->logInfo(" still inactive\n");
$log->logDebug(" still inactive\n");
}
}
} else {

View File

@ -35,37 +35,50 @@ if ($config['payout_system'] != 'pplns') {
$aAllBlocks = $block->getAllUnaccounted('ASC');
if (empty($aAllBlocks)) {
$log->logDebug("No new unaccounted blocks found");
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "No new unaccounted blocks");
$monitoring->setStatus($cron_name . "_status", "okerror", 0);
exit(0);
$monitoring->endCronjob($cron_name, 'E0011', 0, true, false);
}
$count = 0;
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
// Re-calculate after each run due to re-targets in this loop
if ($config['pplns']['shares']['type'] == 'blockavg' && $block->getBlockCount() > 0) {
$pplns_target = round($block->getAvgBlockShares($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 {
$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;
$iCurrentUpstreamId = $aBlock['share_id'];
if (!is_numeric($iCurrentUpstreamId)) {
$log->logFatal("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue");
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Block " . $aBlock['height'] . " has no share_id associated with it");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
$monitoring->endCronjob($cron_name, 'E0012', 1, true);
}
$iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']);
$iNewRoundShares = 0;
$config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward'];
$aRoundAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']);
$log->logInfo('Target: ' . $pplns_target . '; Shares: ' . $iRoundShares . '; Height: ' . $aBlock['height'] . '; Amount: ' . $aBlock['amount'] . '; Found by ID: ' . $aBlock['account_id']);
if ($iRoundShares >= $pplns_target) {
$log->logDebug("Matching or exceeding PPLNS target of $pplns_target with $iRoundShares");
$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']);
if (empty($aAccountShares)) {
$log->logFatal("No shares found for this block, aborted! Block Height : " . $aBlock['height']);
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "No shares found for this block: " . $aBlock['height']);
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
$monitoring->endCronjob($cron_name, 'E0013', 1, true);
}
foreach($aAccountShares as $key => $aData) {
$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;
} else {
$log->logDebug("Not able to match PPLNS target of $pplns_target with $iRoundShares");
@ -90,10 +100,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
$aAccountShares = $aRoundAccountShares;
if (empty($aAccountShares)) {
$log->logFatal("No shares found for this block, aborted! Block height: " . $aBlock['height']);
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "No shares found for this block: " . $aBlock['height']);
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
$monitoring->endCronjob($cron_name, 'E0013', 1, true);
}
// 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'];
}
}
// 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
foreach($aAccountShares as $key => $aData) {
@ -159,44 +184,55 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
foreach ($aRoundAccountShares as $key => $aRoundData) {
if ($aRoundData['username'] == $aData['username'])
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
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
if ($aData['fee'] > 0 && $config['fees'] > 0)
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
if ($aData['donation'] > 0)
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
$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
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
if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) {
$log->logFatal("Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting!");
exit(1);
$log->logFatal("Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting! Error: " . $share->getCronError());
$monitoring->endCronjob($cron_name, 'E0016', 1, true);
}
// Mark this block as accounted for
if (!$block->setAccounted($aBlock['id'])) {
$log->logFatal("Failed to mark block as accounted! Aborting!");
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Failed to mark block " . $aBlock['height'] . " as accounted");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
$log->logFatal("Failed to mark block as accounted! Aborting! Error: " . $block->getCronError());
$monitoring->endCronjob($cron_name, 'E0014', 1, true);
}
} else {
$log->logFatal('Potential double payout detected. Aborted.');
$aMailData = array(
'email' => $setting->getValue('website_email'),
'email' => $setting->getValue('system_error_email'),
'subject' => 'Payout processing aborted',
'Error' => 'Potential double payout detected. All payouts halted until fixed!',
'BlockID' => $aBlock['id'],
@ -204,12 +240,8 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
'Block Share ID' => $aBlock['share_id']
);
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->setStatus($cron_name . "_message", "message", "Block height for block too low! Potential double payout detected.");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
$log->logError(" Failed sending notifications: " . $notification->getCronError() . "\n");
$monitoring->endCronjob($cron_name, 'E0015', 1, true);
}
}

View File

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

View File

@ -35,17 +35,30 @@ if ($config['payout_system'] != 'prop') {
$aAllBlocks = $block->getAllUnaccounted('ASC');
if (empty($aAllBlocks)) {
$log->logDebug('No new unaccounted blocks found in database');
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "No new unaccounted blocks");
$monitoring->setStatus($cron_name . "_status", "okerror", 0);
exit(0);
$monitoring->endCronjob($cron_name, 'E0011', 0, true, false);
}
$count = 0;
// Table header for account shares
$log->logInfo("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee");
foreach ($aAllBlocks as $iIndex => $aBlock) {
if (!$aBlock['accounted'] && $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;
$iCurrentUpstreamId = $aBlock['share_id'];
$aAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']);
@ -54,10 +67,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
if (empty($aAccountShares)) {
$log->logFatal('No shares found for this block, aborted: ' . $aBlock['height']);
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "No shares found for this block, aborted: " . $aBlock['height']);
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
$monitoring->endCronjob($cron_name, 'E0013', 1, true);
}
// Loop through all accounts that have found shares for this round
@ -86,46 +96,40 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
// Update user share statistics
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
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
if ($aData['fee'] > 0 && $config['fees'] > 0)
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
if ($aData['donation'] > 0)
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
$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
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
if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) {
$log->logFatal('Failed to delete accounted shares from ' . $iPreviousShareId . ' to ' . $iCurrentUpstreamId . ', aborted');
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Failed to delete accounted shares from " . $iPreviousShareId . " to " . $iCurrentUpstreamId);
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
$log->logFatal('Failed to delete accounted shares from ' . $iPreviousShareId . ' to ' . $iCurrentUpstreamId . ', aborted! Error: ' . $share->getCronError());
$monitoring->endCronjob($cron_name, 'E0016', 1, true);
}
// Mark this block as accounted for
if (!$block->setAccounted($aBlock['id'])) {
$log->logFatal('Failed to mark block as accounted! Aborted.');
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Failed to mark block " . $aBlock['height'] . " as accounted");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
$log->logFatal('Failed to mark block as accounted! Aborted! Error: ' . $block->getCronError());
$monitoring->endCronjob($cron_name, 'E0014', 1, true);
}
} else {
$log->logFatal('Possible double payout detected. Aborted.');
$log->logFatal('Potential double payout detected. Aborted.');
$aMailData = array(
'email' => $setting->getValue('website_email'),
'email' => $setting->getValue('system_error_email'),
'subject' => 'Payout Failure: Double Payout',
'Error' => 'Possible double payout detected',
'BlockID' => $aBlock['id'],
@ -133,11 +137,8 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
'Block Share ID' => $aBlock['share_id']
);
if (!$mail->sendMail('notifications/error', $aMailData))
$log->logError(" Failed sending notifications: " . $notification->getError() . "\n");
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", 'Possible double payout detected. Aborted.');
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
$log->logFatal('Potential double payout detected. Aborted.');
$monitoring->endCronjob($cron_name, 'E0015', 1, true);
}
}

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
ME=$( basename $0 )
# Overwrite some settings via command line arguments
while getopts "hvp:d:" opt; do
while getopts "hfvp:d:" opt; do
case "$opt" in
h|\?)
echo "Usage: $0 [-v] [-p PHP_BINARY] [-d SUBFOLDER]";
exit 0
;;
v) VERBOSE=1 ;;
f) PHP_OPTS="$PHP_OPTS -f";;
p) PHP_BIN=$OPTARG ;;
d) SUBFOLDER=$OPTARG ;;
:)
@ -52,7 +67,7 @@ done
# Path to PID file, needs to be writable by user running this
PIDFILE="${BASEPATH}/${SUBFOLDER}/${ME}.pid"
# Clean PIDFILE path
PIDFILE=$(readlink -m "$PIDFILE")
PIDFILE=$($READLINK -m "$PIDFILE")
# Create folders recursively if necessary
if ! $(mkdir -p $( dirname $PIDFILE)); then
@ -62,7 +77,7 @@ fi
# Find scripts path
if [[ -L $0 ]]; then
CRONHOME=$( dirname $( readlink $0 ) )
CRONHOME=$( dirname $( $READLINK $0 ) )
else
CRONHOME=$( dirname $0 )
fi
@ -104,7 +119,7 @@ echo $PID > $PIDFILE
for cron in $CRONS; do
[[ $VERBOSE == 1 ]] && echo "Running $cron, check logfile for details"
$PHP_BIN $cron
$PHP_BIN $cron $PHP_OPTS
done
# 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
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
$log = new KLogger ( 'logs/' . $cron_name . '.txt' , KLogger::INFO );
$log->LogDebug('Starting ' . $cron_name);
@ -48,8 +58,13 @@ $log->LogDebug('Starting ' . $cron_name);
// Load the start time for later runtime calculations for monitoring
$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
$log->logDebug('Marking cronjob as running for monitoring');
$monitoring->setStatus($cron_name . '_active', 'yesno', 1);
$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
require_once(CLASS_DIR . '/tools.class.php');
// Fetch latest coin price via API call
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))
$log->logError("unable to update value in settings table");
} 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');

View File

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

View File

@ -1,10 +1,17 @@
<?php
// SHA/Scrypt check
if (empty($config['algorithm']) || $config['algorithm'] == 'scrypt') {
$config['target_bits'] = 16;
} else {
$config['target_bits'] = 32;
}
// Default classes
require_once(CLASS_DIR . '/debug.class.php');
require_once(INCLUDE_DIR . '/lib/KLogger.php');
require_once(INCLUDE_DIR . '/database.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
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 . '/worker.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 . '/roundstats.class.php');
require_once(CLASS_DIR . '/notification.class.php');
require_once(CLASS_DIR . '/news.class.php');
require_once(CLASS_DIR . '/api.class.php');

View File

@ -4,12 +4,21 @@
if (!defined('SECURITY'))
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 {
private $sError = '';
private $sCronError = '';
protected $table = '';
private $values = array(), $types = '';
public function getTableName() {
return $this->table;
}
public function setDebug($debug) {
$this->debug = $debug;
}
@ -19,6 +28,9 @@ class Base {
public function setMail($mail) {
$this->mail = $mail;
}
public function setSalt($salt) {
$this->salt = $salt;
}
public function setSmarty($smarty) {
$this->smarty = $smarty;
}
@ -28,39 +40,106 @@ class Base {
public function setConfig($config) {
$this->config = $config;
}
public function setErrorCodes(&$aErrorCodes) {
$this->aErrorCodes =& $aErrorCodes;
}
public function setToken($token) {
$this->token = $token;
}
public function setBlock($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) {
$this->setting = $setting;
}
public function setTools($tools) {
$this->tools = $tools;
}
public function setBitcoin($bitcoin) {
$this->bitcoin = $bitcoin;
}
public function setTokenType($tokentype) {
$this->tokentype = $tokentype;
}
public function setShare($share) {
$this->share = $share;
}
public function setErrorMessage($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() {
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
* @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
* @param lower bool try with LOWER comparision
* @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);
$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)) {
$stmt->bind_param($type, $value);
$stmt->execute();
@ -72,18 +151,38 @@ class Base {
return false;
}
/**
* Check if the prepared statement is valid
* @param $bState Statement return value
* @return bool true or false
**/
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;
}
if ($bState ===! true)
return $this->sqlError();
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
* Update a single row in a table
* @param field string Field to update
* @return bool
**/
@ -94,7 +193,7 @@ class Base {
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;
$this->sqlError();
}
/**

View File

@ -22,6 +22,12 @@ class BitcoinWrapper extends BitcoinClient {
/**
* 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() {
$this->oDebug->append("STA " . __METHOD__, 4);
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
@ -48,15 +54,21 @@ class BitcoinWrapper extends BitcoinClient {
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
try {
$dNetworkHashrate = $this->query('getmininginfo');
if (is_array($dNetworkHashrate) && array_key_exists('networkhashps', $dNetworkHashrate)) {
$dNetworkHashrate = $dNetworkHashrate['networkhashps'];
} else if (is_array($dNetworkHashrate) && array_key_exists('hashespersec', $dNetworkHashrate)) {
$dNetworkHashrate = $dNetworkHashrate['hashespersec'];
} else if (is_array($dNetworkHashrate) && array_key_exists('netmhashps', $dNetworkHashrate)) {
$dNetworkHashrate = $dNetworkHashrate['netmhashps'] * 1000 * 1000;
if (is_array($dNetworkHashrate)) {
if (array_key_exists('networkhashps', $dNetworkHashrate)) {
$dNetworkHashrate = $dNetworkHashrate['networkhashps'];
} else if (array_key_exists('hashespersec', $dNetworkHashrate)) {
$dNetworkHashrate = $dNetworkHashrate['hashespersec'];
} else if (array_key_exists('netmhashps', $dNetworkHashrate)) {
$dNetworkHashrate = $dNetworkHashrate['netmhashps'] * 1000 * 1000;
} else {
// Unsupported implementation
$dNetworkHashrate = 0;
}
}
} 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);
}

View File

@ -1,32 +1,10 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
class Block {
private $sError = '';
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;
}
class Block extends Base {
protected $table = 'blocks';
/**
* Specific method to fetch the latest block found
@ -35,13 +13,9 @@ class Block {
**/
public function getLast() {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table ORDER BY height DESC LIMIT 1");
if ($this->checkStmt($stmt)) {
$stmt->execute();
$result = $stmt->get_result();
$stmt->close();
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc();
}
return false;
return $this->sqlError();
}
/**
@ -53,7 +27,31 @@ class Block {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE height = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $height) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc();
return false;
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");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
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");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
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");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
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");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
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");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $height, $limit) && $stmt->execute() && $result = $stmt->get_result())
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");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $limit) && $stmt->execute() && $result = $stmt->get_result())
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");
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $confirmations) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
return false;
return $this->sqlError();
}
/**
@ -148,13 +146,9 @@ class Block {
**/
public function setConfirmations($block_id, $confirmations) {
$stmt = $this->mysqli->prepare("UPDATE $this->table SET confirmations = ? WHERE id = ?");
if ($this->checkStmt($stmt)) {
$stmt->bind_param("ii", $confirmations, $block_id) or die($stmt->error);
$stmt->execute() or die("Failed");
$stmt->close();
if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $confirmations, $block_id) && $stmt->execute())
return true;
}
return false;
return $this->sqlError();
}
/**
@ -164,13 +158,9 @@ class Block {
**/
public function getAll($order='DESC') {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table ORDER BY height $order");
if ($this->checkStmt($stmt)) {
$stmt->execute();
$result = $stmt->get_result();
$stmt->close();
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
}
return false;
return $this->sqlError();
}
/**
@ -180,50 +170,21 @@ class Block {
**/
public function addBlock($block) {
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (height, blockhash, confirmations, amount, difficulty, time) VALUES (?, ?, ?, ?, ?, ?)");
if ($this->checkStmt($stmt)) {
$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();
if ($this->checkStmt($stmt) && $stmt->bind_param('isiddi', $block['height'], $block['blockhash'], $block['confirmations'], $block['amount'], $block['difficulty'], $block['time']) && $stmt->execute())
return true;
}
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;
return $this->sqlError();
}
/**
* Update a single column within a single row
* @param block_id int Block ID to update
* @param field string Column name to update
* @param value string Value to insert
* @return bool
* Get our last inserted upstream ID from table
* @param none
* @return mixed upstream ID or 0, false on error
**/
private function updateSingle($block_id, $field, $value) {
$stmt = $this->mysqli->prepare("UPDATE $this->table SET $field = ? WHERE id = ?");
if ($this->checkStmt($stmt)) {
$stmt->bind_param('ii', $value, $block_id);
if (!$stmt->execute()) {
$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;
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;
return $this->sqlError();
}
/**
@ -233,7 +194,19 @@ class Block {
* @return bool
**/
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
**/
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
**/
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) {
if (empty($block_id)) return false;
return $this->updateSingle($block_id, 'accounted', 1);
}
/**
* 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;
$field = array( 'name' => 'accounted', 'value' => 1, 'type' => 'i');
return $this->updateSingle($block_id, $field);
}
}
// 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 = ?");
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
$this->setErrorMessage('Unable to fetch invitiations send from your account');
$this->debug->append('Failed to fetch invitations from database: ' . $this->mysqli->errro);
return false;
$this->sqlError('E0021');
}
/**
@ -31,9 +29,7 @@ class Invitation extends Base {
$stmt = $this->mysqli->prepare("SELECT count(id) AS total FROM $this->table WHERE account_id = ?");
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $stmt->bind_result($total) && $stmt->fetch())
return $total;
$this->setErrorMessage('Unable to fetch invitiations send from your account');
$this->debug->append('Failed to fetch invitations from database: ' . $this->mysqli->errro);
return false;
$this->sqlError('E0021');
}
/**
@ -65,7 +61,7 @@ class Invitation extends Base {
**/
public function setActivated($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;
}
$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 ( ?, ?, ?)");
if ($stmt && $stmt->bind_param('isi', $account_id, $email, $token_id) && $stmt->execute())
return true;
return false;
$this->sqlError('E0022');
}
/**
* Send an invitation out to a user
* Uses the mail class to send mails
@ -97,39 +94,37 @@ class Invitation extends Base {
$this->debug->append("STA " . __METHOD__, 4);
// Check data input
if (empty($aData['email']) || !filter_var($aData['email'], FILTER_VALIDATE_EMAIL)) {
$this->setErrorMessage( 'Invalid e-mail address' );
$this->setErrorMessage($this->getErrorMsg('E0023'));
return false;
}
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;
}
// Ensure this invitation does not exist yet nor do we have an account with that email
if ($this->user->getEmail($aData['email'])) {
$this->setErrorMessage('This email is already registered as an account');
$this->setErrorMessage($this->getErrorMsg('E0025'));
return false;
}
if ($this->getByEmail($aData['email'])) {
$this->setErrorMessage('A pending invitation for this address already exists');
$this->setErrorMessage($this->getErrorMsg('E0026'));
return false;
}
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;
}
$aData['username'] = $this->user->getUserName($account_id);
$aData['subject'] = 'Pending Invitation';
if ($this->mail->sendMail('invitations/body', $aData)) {
$aToken = $this->token->getToken($aData['token']);
if (!$this->createInvitation($account_id, $aData['email'], $aToken['id'])) {
$this->setErrorMessage('Unable to create invitation record');
if (!$this->createInvitation($account_id, $aData['email'], $aToken['id']))
return false;
}
return true;
} 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;
}
}
@ -142,5 +137,5 @@ $invitation->setMail($mail);
$invitation->setUser($user);
$invitation->setToken($oToken);
$invitation->setConfig($config);
$invitation->setErrorCodes($aErrorCodes);
?>

View File

@ -5,16 +5,6 @@ if (!defined('SECURITY'))
die('Hacking attempt');
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
* @param senderName string senderName
@ -28,19 +18,19 @@ class Mail extends Base {
public function contactform($senderName, $senderEmail, $senderSubject, $senderMessage) {
$this->debug->append("STA " . __METHOD__, 4);
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;
}
if (empty($senderEmail) || !filter_var($senderEmail, FILTER_VALIDATE_EMAIL)) {
$this->setErrorMessage( 'Invalid e-mail address' );
$this->setErrorMessage($this->getErrorMsg('E0023'));
return false;
}
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;
}
if (strlen(strip_tags($senderMessage)) < strlen($senderMessage)) {
$this->setErrorMessage('Your message may only contain alphanumeric characters');
$this->setErrorMessage($this->getErrorMsg('E0024'));
return false;
}
$aData['senderName'] = $senderName;
@ -58,16 +48,28 @@ class Mail extends Base {
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) {
// 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('SUBJECT', $aData['subject']);
$this->smarty->assign('DATA', $aData);
$headers = 'From: Website Administration <' . $this->setting->getValue('website_email') . ">\n";
$headers .= "MIME-Version: 1.0\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))
return true;
$this->setErrorMessage('Unable to send mail');
$this->setErrorMessage($this->sqlError('E0031'));
return false;
}
}
@ -79,4 +81,5 @@ $mail->setMysql($mysqli);
$mail->setSmarty($smarty);
$mail->setConfig($config);
$mail->setSetting($setting);
$mail->setErrorCodes($aErrorCodes);
?>

View File

@ -1,14 +1,65 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
class Monitoring {
public function __construct($debug, $mysqli) {
$this->debug = $debug;
$this->mysqli = $mysqli;
$this->table = 'monitoring';
class Monitoring extends Base {
protected $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()) {
return $result->fetch_assoc();
} else {
$this->debug->append("Failed to fetch variable $name from $this->table");
return false;
$this->sqlError();
}
return $value;
}
@ -44,6 +94,41 @@ class Monitoring {
$this->debug->append("Failed to set $name to $value");
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');
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) {
$this->debug->append("STA " . __METHOD__, 5);
return $this->getSingle($id, 'active', 'id');
}
/**
* Switch activation status
* @param id int News ID
* @return bool true or false
**/
public function toggleActive($id) {
$this->debug->append("STA " . __METHOD__, 5);
$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");
if ($stmt && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
// Catchall
return false;
return $this->sqlError('E0040');
}
/**
@ -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");
if ($stmt && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
// Catchall
return false;
return $this->sqlError('E0039');
}
/**
@ -50,8 +58,7 @@ class News extends Base {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE id = ?");
if ($stmt && $stmt->bind_param('i', $id) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc();
// Catchall
return false;
return $this->sqlError('E0038');
}
/**
@ -62,8 +69,7 @@ class News extends Base {
$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)
return true;
$this->setErrorMessage("Failed to update news entry $id");
return false;
return $this->sqlError('E0037');
}
public function deleteNews($id) {
@ -72,8 +78,7 @@ class News extends Base {
$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)
return true;
$this->setErrorMessage("Failed to delete news entry $id");
return false;
return $this->sqlError('E0036');
}
/**
@ -89,9 +94,7 @@ class News extends Base {
$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())
return true;
$this->debug->append("Failed to add news: " . $this->mysqli->error);
$this->setErrorMessage("Unable to add new news: " . $this->mysqli->error);
return false;
return $this->sqlError('E0035');
}
}

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

View File

@ -4,7 +4,7 @@
if (!defined('SECURITY')) die('Hacking attempt');
class Payout Extends Base {
var $table = 'payouts';
protected $table = 'payouts';
/**
* 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");
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute( )&& $stmt->store_result() && $stmt->num_rows > 0)
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");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
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()) {
return $stmt->insert_id;
}
$this->setErrorMessage('Unable to create new payout request');
$this->debug->append('Failed to create new payout request in database: ' . $this->mysqli->error);
return false;
return $this->sqlError('E0049');
}
/**
@ -54,10 +52,13 @@ class Payout Extends Base {
$stmt = $this->mysqli->prepare("UPDATE $this->table SET completed = 1 WHERE id = ?");
if ($stmt && $stmt->bind_param('i', $id) && $stmt->execute())
return true;
return false;
return $this->sqlError('E0051');
}
}
$oPayout = new Payout();
$oPayout->setDebug($debug);
$oPayout->setMysql($mysqli);
$oPayout->setErrorCodes($aErrorCodes);
?>

View File

@ -4,41 +4,25 @@
if (!defined('SECURITY'))
die('Hacking attempt');
class RoundStats {
private $sError = '';
class RoundStats extends Base {
private $tableTrans = 'transactions';
private $tableStats = 'statistics_shares';
private $tableBlocks = 'blocks';
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
**/
public function getNextBlock($iHeight=0) {
$stmt = $this->mysqli->prepare("
SELECT height
FROM $this->tableBlocks
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 false;
return $this->sqlError();
}
/**
@ -47,13 +31,44 @@ class RoundStats {
public function getPreviousBlock($iHeight=0) {
$stmt = $this->mysqli->prepare("
SELECT height
FROM $this->tableBlocks
FROM " . $this->block->getTableName() . "
WHERE height < ?
ORDER BY height DESC
LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result())
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
* @return data array Block information from DB
**/
public function getDetailsForBlockHeight($iHeight=0, $isAdmin=0) {
public function getDetailsForBlockHeight($iHeight=0) {
$stmt = $this->mysqli->prepare("
SELECT
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
FROM $this->tableBlocks as b
LEFT JOIN $this->tableUsers AS a ON b.account_id = a.id
WHERE b.height = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $isAdmin, $iHeight) && $stmt->execute() && $result = $stmt->get_result())
IF(a.is_anonymous, 'anonymous', a.username) AS finder,
ROUND((difficulty * 65535) / POW(2, (" . $this->config['difficulty'] . " -16)), 0) AS estshares,
(time - (SELECT time FROM $this->tableBlocks WHERE height < ? ORDER BY height DESC LIMIT 1)) AS round_time
FROM " . $this->block->getTableName() . " as b
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 false;
return $this->sqlError();
}
/**
@ -79,22 +96,68 @@ class RoundStats {
* @param height int Block Height
* @return data array Block information from DB
**/
public function getRoundStatsForAccounts($iHeight=0, $isAdmin=0) {
public function getRoundStatsForAccounts($iHeight=0) {
$stmt = $this->mysqli->prepare("
SELECT
IF(a.is_anonymous, IF( ? , a.username, 'anonymous'), a.username) AS username,
a.id,
a.username,
a.is_anonymous,
s.valid,
s.invalid
FROM $this->tableStats AS s
LEFT JOIN $this->tableBlocks AS b ON s.block_id = b.id
LEFT JOIN $this->tableUsers AS a ON a.id = s.account_id
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 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 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
* @return data array Block round transactions
**/
public function getAllRoundTransactions($iHeight=0, $admin) {
public function getAllRoundTransactions($iHeight=0) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("
SELECT
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.amount AS amount
FROM $this->tableTrans AS t
LEFT JOIN $this->tableBlocks AS b ON t.block_id = b.id
LEFT JOIN $this->tableUsers AS a ON t.account_id = a.id
WHERE b.height = ?
ORDER BY id ASC");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $admin, $iHeight) && $stmt->execute() && $result = $stmt->get_result())
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'
ORDER BY amount DESC");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
$this->debug->append('Unable to fetch transactions');
return false;
return $this->sqlError();
}
/**
@ -135,26 +200,93 @@ class RoundStats {
a.username AS username,
t.type AS type,
t.amount AS amount
FROM $this->tableTrans AS t
LEFT JOIN $this->tableBlocks AS b ON t.block_id = b.id
LEFT JOIN $this->tableUsers AS a ON t.account_id = a.id
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 a.id = ?
ORDER BY id ASC");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iHeight, $id) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
$this->debug->append('Unable to fetch transactions');
return false;
return $this->sqlError();
}
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;
/**
* Get ALL last blocks from height for admin panel
**/
public function getAllReportBlocksFoundHeight($iHeight=0, $limit=10) {
$stmt = $this->mysqli->prepare("
SELECT
height, shares
FROM " . $this->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
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
class Setting {
public function __construct($debug, $mysqli) {
$this->debug = $debug;
$this->mysqli = $mysqli;
$this->table = 'settings';
}
class Setting extends Base {
protected $table = 'settings';
/**
* Fetch a value from our table
@ -17,18 +12,13 @@ class Setting {
* @return value string Value
**/
public function getValue($name) {
$query = $this->mysqli->prepare("SELECT value FROM $this->table WHERE name=? LIMIT 1");
if ($query) {
$query->bind_param('s', $name);
$query->execute();
$query->bind_result($value);
$query->fetch();
$query->close();
} else {
$this->debug->append("Failed to fetch variable $name from $this->table");
return false;
}
return $value;
$stmt = $this->mysqli->prepare("SELECT value FROM $this->table WHERE name = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('s', $name) && $stmt->execute() && $result = $stmt->get_result())
if ($result->num_rows > 0)
return $result->fetch_object()->value;
// Log error but return empty string
$this->sqlError();
return "";
}
/**
@ -41,13 +31,14 @@ class Setting {
$stmt = $this->mysqli->prepare("
INSERT INTO $this->table (name, value)
VALUES (?, ?)
ON DUPLICATE KEY UPDATE value = ?
");
ON DUPLICATE KEY UPDATE value = ?");
if ($stmt && $stmt->bind_param('sss', $name, $value, $value) && $stmt->execute())
return true;
$this->debug->append("Failed to set $name to $value");
return false;
return $this->sqlError();
}
}
$setting = new Setting($debug, $mysqli);
$setting->setDebug($debug);
$setting->setMysql($mysqli);
$setting->setErrorCodes($aErrorCodes);

View File

@ -4,32 +4,14 @@
if (!defined('SECURITY'))
die('Hacking attempt');
class Share {
private $sError = '';
private $table = 'shares';
private $tableArchive = 'shares_archive';
class Share Extends Base {
protected $table = 'shares';
protected $tableArchive = 'shares_archive';
private $oUpstream;
private $iLastUpstreamId;
// This defines each share
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
* @param none
@ -38,13 +20,43 @@ class Share {
public function getArchiveTableName() {
return $this->tableArchive;
}
/**
* Fetch normal table name for this class
* @param none
* @return data string Table name
* Fetch a single share by ID
* @param id int Share ID
* @return array Share data
**/
public function getTableName() {
return $this->table;
public function getShareById($id) {
return $this->getAllAssoc($id);
}
/**
* Update an entire shares data
**/
public function updateShareById($id, $data) {
$this->debug->append("STA " . __METHOD__, 4);
$sql = "UPDATE $this->table SET";
$start = true;
// Remove ID column
unset($data['id']);
foreach ($data as $column => $value) {
$start == true ? $sql .= " $column = ? " : $sql .= ", $column = ?";
$start = false;
switch($column) {
case 'difficulty':
$this->addParam('d', $value);
break;
default:
$this->addParam('s', $value);
break;
}
}
$sql .= " WHERE id = ? LIMIT 1";
$this->addParam('i', $id);
$stmt = $this->mysqli->prepare($sql);
if ($this->checkStmt($stmt) && call_user_func_array( array($stmt, 'bind_param'), $this->getParam()) && $stmt->execute())
return true;
return $this->sqlError();
}
/**
@ -52,14 +64,10 @@ class Share {
* Used for PPS calculations without moving to archive
**/
public function getLastInsertedShareId() {
$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())
return $result->fetch_object()->id;
// Catchall
$this->setErrorMessage('Failed to fetch last inserted share ID');
return false;
return $this->sqlError();
}
/**
@ -75,14 +83,9 @@ class Share {
WHERE our_result = 'Y'
AND id > ? AND id <= ?
");
if ($this->checkStmt($stmt)) {
$stmt->bind_param('ii', $previous_upstream, $current_upstream);
$stmt->execute();
$result = $stmt->get_result();
$stmt->close();
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->total;
}
return false;
return $this->sqlError();
}
/**
@ -108,7 +111,7 @@ class Share {
");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
return false;
return $this->sqlError();
}
/**
@ -118,19 +121,17 @@ class Share {
$stmt = $this->mysqli->prepare("SELECT MAX(id) AS id FROM $this->table");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->id;
return false;
return $this->sqlError();
}
/**
* Fetch the highest available share ID from archive
**/
function getMaxArchiveShareId() {
$stmt = $this->mysqli->prepare("
SELECT MAX(share_id) AS share_id FROM $this->tableArchive
");
$stmt = $this->mysqli->prepare("SELECT MAX(share_id) AS share_id FROM $this->tableArchive");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->share_id;
return false;
return $this->sqlError();
}
/**
@ -161,7 +162,7 @@ class Share {
}
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())
return true;
}
// Catchall
return false;
return $this->sqlError();
}
/**
@ -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
FROM $this->table
WHERE id > ? AND id <= ?");
if ($this->checkStmt($archive_stmt) && $archive_stmt->bind_param('iii', $block_id, $previous_upstream, $current_upstream) && $archive_stmt->execute()) {
$archive_stmt->close();
if ($this->checkStmt($archive_stmt) && $archive_stmt->bind_param('iii', $block_id, $previous_upstream, $current_upstream) && $archive_stmt->execute())
return true;
}
// Catchall
return false;
return $this->sqlError();
}
/**
* 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) {
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id > ? AND id <= ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute())
return true;
// Catchall
return false;
return $this->sqlError();
}
/**
* Set/get last found share accepted by upstream: id and accounts
@ -229,7 +231,10 @@ class Share {
public function getUpstreamFinder() {
return @$this->oUpstream->account;
}
public function getUpstreamId() {
public function getUpstreamWorker() {
return @$this->oUpstream->worker;
}
public function getUpstreamShareId() {
return @$this->oUpstream->id;
}
/**
@ -240,7 +245,7 @@ class Share {
* @param last int Skips all shares up to last to find new share
* @return bool
**/
public function setUpstream($aBlock, $last=0) {
public function findUpstreamShare($aBlock, $last=0) {
// Many use stratum, so we create our stratum check first
$version = pack("I*", sprintf('%08d', $aBlock['version']));
$previousblockhash = pack("H*", swapEndian($aBlock['previousblockhash']));
@ -252,39 +257,39 @@ class Share {
$header_hex = implode(unpack("H*", $header_bin));
// Stratum supported blockhash solution entry
$stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution = ? LIMIT 1");
$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()) {
$this->oUpstream = $result->fetch_object();
$this->share_type = 'startum_blockhash';
if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id))
$this->share_type = 'stratum_blockhash';
if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id))
return true;
}
// Stratum scrypt hash check
$scrypt_hash = swapEndian(bin2hex(Scrypt::calc($header_bin, $header_bin, 1024, 1, 1, 32)));
$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()) {
$this->oUpstream = $result->fetch_object();
$this->share_type = 'startum_solution';
if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id))
$this->share_type = 'stratum_solution';
if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id))
return true;
}
// Failed to fetch via startum solution, try pushpoold
// Fallback to pushpoold solution type
$ppheader = sprintf('%08d', $aBlock['version']) . word_reverse($aBlock['previousblockhash']) . word_reverse($aBlock['merkleroot']) . dechex($aBlock['time']) . $aBlock['bits'] . dechex($aBlock['nonce']);
$stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution LIKE CONCAT(?, '%') LIMIT 1");
$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()) {
$this->oUpstream = $result->fetch_object();
$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;
}
// Still no match, try upstream result with timerange
$stmt = $this->mysqli->prepare("
SELECT
SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id
SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id
FROM $this->table
WHERE upstream_result = 'Y'
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()) {
$this->oUpstream = $result->fetch_object();
$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;
}
// We failed again, now we take ANY result matching the timestamp
$stmt = $this->mysqli->prepare("
SELECT
SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id
SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id
FROM $this->table
WHERE our_result = 'Y'
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()) {
$this->oUpstream = $result->fetch_object();
$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;
}
// Catchall
$this->setErrorMessage($this->getErrorMsg('E0052', $aBlock['height']));
return false;
}
@ -332,21 +337,22 @@ class Share {
AND id <= ? AND @total < ?
ORDER BY id DESC
) AS b
WHERE total <= ?
");
WHERE total <= ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $current_upstream, $iCount, $iCount) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->id;
return false;
return $this->sqlError();
}
/**
* Fetch the lowest needed share ID from archive
**/
function getMinArchiveShareId($iCount) {
// We don't use baseline here to be more accurate
$iCount = $iCount * pow(2, ($this->config['difficulty'] - 16));
$stmt = $this->mysqli->prepare("
SELECT MIN(b.share_id) AS share_id FROM
(
SELECT share_id, @total := @total + (IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty) / POW(2, (" . $this->config['difficulty'] . " - 16))) AS total
SELECT share_id, @total := @total + IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty) AS total
FROM $this->tableArchive, (SELECT @total := 0) AS a
WHERE our_result = 'Y'
AND @total < ?
@ -356,20 +362,14 @@ class Share {
");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iCount, $iCount) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->share_id;
return false;
}
/**
* Helper function
**/
private function checkStmt($bState) {
if ($bState ===! true) {
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error);
$this->setErrorMessage('Internal application Error');
return false;
}
return true;
return $this->sqlError();
}
}
$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
* rows in our database to ensure data integrity for the backend
**/
class Statistics {
private $sError = '';
private $table = 'statistics_shares';
class Statistics extends Base {
protected $table = 'statistics_shares';
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
public function setGetCache($set=false) {
$this->getcache = $set;
@ -43,13 +22,74 @@ class Statistics {
return $this->getcache;
}
private function checkStmt($bState) {
if ($bState ===! true) {
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error);
$this->setErrorMessage('Failed to prepare statement');
return false;
}
return true;
/**
* Get our first block found
*
**/
public function getFirstBlockFound() {
$this->debug->append("STA " . __METHOD__, 4);
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.*,
a.username AS finder,
a.is_anonymous AS is_anonymous,
ROUND((difficulty * 65535) / POW(2, (" . $this->config['difficulty'] . " -16)), 0) AS estshares
ROUND((difficulty * POW(2, 32 - " . $this->config['target_bits'] . ")) / POW(2, (" . $this->config['difficulty'] . " -16)), 0) AS estshares
FROM " . $this->block->getTableName() . " AS b
LEFT JOIN " . $this->user->getTableName() . " AS a
ON b.account_id = a.id
ORDER BY height 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);
// Catchall
$this->debug->append("Failed to find blocks:" . $this->mysqli->error);
return false;
return $this->sqlError();
}
/**
* 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
* Stored per block user statistics of valid and invalid shares
@ -88,9 +198,41 @@ class Statistics {
$this->debug->append("STA " . __METHOD__, 4);
$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;
// Catchall
$this->debug->append("Failed to update share stats: " . $this->mysqli->error);
return false;
return $this->sqlError();
}
/**
* 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 IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0) AS hashrate
FROM " . $this->share->getTableName() . "
WHERE time > DATE_SUB(now(), INTERVAL ? SECOND)
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() . "
WHERE time > DATE_SUB(now(), INTERVAL ? SECOND)
AND our_result = 'Y'
)
) AS hashrate
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);
$this->debug->append("Failed to get hashrate: " . $this->mysqli->error);
return false;
return $this->sqlError();
}
/**
@ -137,17 +279,17 @@ class Statistics {
SELECT ROUND(COUNT(id) / ?, 2) AS sharerate
FROM " . $this->share->getTableName() . "
WHERE time > DATE_SUB(now(), INTERVAL ? SECOND)
AND our_result = 'Y'
) + (
SELECT ROUND(COUNT(id) / ?, 2) AS sharerate
FROM " . $this->share->getArchiveTableName() . "
WHERE time > DATE_SUB(now(), INTERVAL ? SECOND)
AND our_result = 'Y'
)
) AS sharerate
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);
// Catchall
$this->debug->append("Failed to fetch share rate: " . $this->mysqli->error);
return false;
return $this->sqlError();
}
/**
@ -179,9 +321,7 @@ class Statistics {
WHERE UNIX_TIMESTAMP(time) > IFNULL((SELECT MAX(time) FROM " . $this->block->getTableName() . "), 0)");
if ( $this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() )
return $this->memcache->setCache(STATISTICS_ROUND_SHARES, $result->fetch_assoc());
// Catchall
$this->debug->append("Failed to fetch round shares: " . $this->mysqli->error);
return false;
return $this->sqlError();
}
/**
@ -229,9 +369,7 @@ class Statistics {
$data['share_id'] = $this->share->getMaxShareId();
return $this->memcache->setCache(STATISTICS_ALL_USER_SHARES, $data);
}
// Catchall
$this->debug->append("Unable to fetch all users round shares: " . $this->mysqli->error);
return false;
return $this->sqlError();
}
/**
@ -261,18 +399,15 @@ class Statistics {
AND u.id = ?");
if ($stmt && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result())
return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_assoc());
// Catchall
$this->debug->append("Unable to fetch user round shares: " . $this->mysqli->error);
return false;
return $this->sqlError();
}
/**
* 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='%') {
$this->debug->append("STA " . __METHOD__, 4);
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $filter)) return $data;
$stmt = $this->mysqli->prepare("
SELECT
a.id AS id,
@ -281,22 +416,25 @@ class Statistics {
a.no_fees as no_fees,
a.username AS username,
a.donate_percent AS donate_percent,
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
a.email AS email
FROM " . $this->user->getTableName() . " AS a
LEFT JOIN " . $this->share->getTableName() . " AS s
ON a.username = SUBSTRING_INDEX( s.username, '.', 1 )
WHERE
a.username LIKE ?
GROUP BY username
ORDER BY username");
if ($this->checkStmt($stmt) && $stmt->bind_param('s', $filter) && $stmt->execute() && $result = $stmt->get_result()) {
return $this->memcache->setCache(__FUNCTION__ . $filter, $result->fetch_all(MYSQLI_ASSOC));
// 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
* @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;
$stmt = $this->mysqli->prepare("
SELECT
(
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate
FROM " . $this->share->getTableName() . " AS s,
" . $this->user->getTableName() . " AS u
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND s.time > DATE_SUB(now(), INTERVAL ? SECOND)
AND u.id = ?
) + (
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate
FROM " . $this->share->getArchiveTableName() . " AS s,
" . $this->user->getTableName() . " AS u
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND s.time > DATE_SUB(now(), INTERVAL ? SECOND)
AND u.id = ?
) AS hashrate
FROM DUAL");
if ($this->checkStmt($stmt) && $stmt->bind_param("iiiiii", $interval, $interval, $account_id, $interval, $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result() )
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 (
SELECT
s.id, s.our_result, IF(s.difficulty = 0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty) AS difficulty
FROM
shares AS s,
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 = ?
UNION
SELECT
s.share_id, s.our_result, IF(s.difficulty = 0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty) AS difficulty
FROM
shares_archive AS s,
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);
// Catchall
$this->debug->append("Failed to fetch hashrate: " . $this->mysqli->error);
return false;
return $this->sqlError();
}
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;
$stmt = $this->mysqli->prepare("
SELECT
(
(
SELECT COUNT(s.id) / ? AS sharerate
FROM " . $this->share->getTableName() . " AS s,
" . $this->user->getTableName() . " AS u
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND s.time > DATE_SUB(now(), INTERVAL ? SECOND)
AND u.id = ?
) + (
SELECT COUNT(s.id) / ? AS sharerate
FROM " . $this->share->getArchiveTableName() . " AS s,
" . $this->user->getTableName() . " AS u
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND s.time > DATE_SUB(now(), INTERVAL ? SECOND)
AND u.id = ?
)
) AS sharerate
FROM DUAL");
if ($this->checkStmt($stmt) && $stmt->bind_param("iiiiii", $interval, $interval, $account_id, $interval, $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result() )
IFNULL(COUNT(*) / ?, 0) AS sharerate
FROM (
SELECT
s.id
FROM
shares AS s,
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 = ?
UNION
SELECT
s.share_id
FROM
shares_archive AS s,
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()->sharerate);
// Catchall
$this->debug->append("Failed to fetch sharerate: " . $this->mysqli->error);
return false;
return $this->sqlError();
}
/**
@ -372,17 +556,16 @@ class Statistics {
$this->debug->append("STA " . __METHOD__, 4);
if ($data = $this->memcache->get(__FUNCTION__ . $worker_id)) return $data;
$stmt = $this->mysqli->prepare("
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / 600 / 1000), 0) AS hashrate
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / 600 / 1000), 0) AS hashrate
FROM " . $this->share->getTableName() . " AS s,
" . $this->user->getTableName() . " AS u
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND our_result = 'Y'
AND s.time > DATE_SUB(now(), INTERVAL 600 SECOND)
AND u.id = ?");
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);
// Catchall
$this->debug->append("Failed to fetch hashrate: " . $this->mysqli->error);
return false;
return $this->sqlError();
}
/**
@ -397,28 +580,32 @@ class Statistics {
switch ($type) {
case 'shares':
if ($data = $this->memcache->get(STATISTICS_ALL_USER_SHARES)) {
// Use global cache to build data
$max = 0;
foreach($data['data'] as $key => $aUser) {
$shares[$key] = $aUser['valid'];
$username[$key] = $aUser['username'];
// Use global cache to build data, if we have any data there
if (!empty($data['data']) && is_array($data['data'])) {
foreach($data['data'] as $key => $aUser) {
$shares[$key] = $aUser['valid'];
$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
$stmt = $this->mysqli->prepare("
SELECT
a.username AS account,
a.donate_percent AS donate_percent,
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,
SUBSTRING_INDEX( s.username, '.', 1 ) AS account
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->share->getTableName() . " AS s
LEFT JOIN " . $this->user->getTableName() . " AS a
ON SUBSTRING_INDEX( s.username, '.', 1 ) = a.username
@ -428,22 +615,21 @@ class Statistics {
LIMIT ?");
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));
$this->debug->append("Fetching shares failed: ");
return false;
return $this->sqlError();
break;
case 'hashes':
$stmt = $this->mysqli->prepare("
SELECT
a.username AS account,
a.donate_percent AS donate_percent,
a.is_anonymous AS is_anonymous,
IFNULL(ROUND(SUM(t1.difficulty) * 65536/600/1000, 2), 0) AS hashrate,
SUBSTRING_INDEX( t1.username, '.', 1 ) AS account
IFNULL(ROUND(SUM(t1.difficulty) * POW(2, " . $this->config['target_bits'] . ") / 600 / 1000, 2), 0) AS hashrate
FROM
(
SELECT IFNULL(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0) AS difficulty, username FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y'
UNION ALL
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 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
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
LEFT JOIN " . $this->user->getTableName() . " AS a
ON SUBSTRING_INDEX( t1.username, '.', 1 ) = a.username
@ -451,8 +637,7 @@ class Statistics {
ORDER BY hashrate DESC LIMIT ?");
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));
$this->debug->append("Fetching shares failed: " . $this->mysqli->error);
return false;
return $this->sqlError();
break;
}
}
@ -467,20 +652,24 @@ class Statistics {
if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
$stmt = $this->mysqli->prepare("
SELECT
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate,
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
FROM " . $this->share->getTableName() . " AS s, accounts AS a
WHERE time < NOW() - INTERVAL 1 HOUR
AND our_result = 'Y'
AND time > NOW() - INTERVAL 25 HOUR
AND a.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND a.id = ?
GROUP BY HOUR(time)
UNION ALL
UNION
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
FROM " . $this->share->getArchiveTableName() . " AS s, accounts AS a
WHERE time < NOW() - INTERVAL 1 HOUR
AND our_result = 'Y'
AND time > NOW() - INTERVAL 25 HOUR
AND a.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND a.id = ?
@ -493,9 +682,7 @@ class Statistics {
while ($row = $result->fetch_assoc()) $aData[$row['hour']] = $row['hashrate'];
return $this->memcache->setCache(__FUNCTION__ . $account_id, $aData);
}
// Catchall
$this->debug->append("Failed to fetch hourly hashrate: " . $this->mysqli->error);
return false;
return $this->sqlError();
}
/**
@ -508,19 +695,23 @@ class Statistics {
if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__)) return $data;
$stmt = $this->mysqli->prepare("
SELECT
IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate,
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
FROM " . $this->share->getTableName() . " AS s
WHERE time < NOW() - INTERVAL 1 HOUR
AND time > NOW() - INTERVAL 25 HOUR
AND our_result = 'Y'
GROUP BY HOUR(time)
UNION ALL
UNION
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
FROM " . $this->share->getArchiveTableName() . " AS s
WHERE time < NOW() - INTERVAL 1 HOUR
AND time > NOW() - INTERVAL 25 HOUR
AND our_result = 'Y'
GROUP BY HOUR(time)");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) {
$iStartHour = date('G');
@ -530,35 +721,95 @@ class Statistics {
while ($row = $result->fetch_assoc()) $aData[$row['hour']] = (int) $row['hashrate'];
return $this->memcache->setCache(__FUNCTION__, $aData);
}
// Catchall
$this->debug->append("Failed to fetch hourly hashrate: " . $this->mysqli->error);
return false;
return $this->sqlError();
}
/**
* get user estimated payouts based on share counts
* @param aRoundShares array Round shares
* @param aUserShares array User shares
* @param value1 mixed Round shares OR share rate
* @param value2 mixed User shares OR share difficulty
* @param dDonate double User donation setting
* @param bNoFees bool User no-fees option setting
* @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);
// Fetch some user information that we need
if (@$aRoundShares['valid'] > 0 && @$aUserShares['valid'] > 0) {
$aEstimates['block'] = round(( (int)$aUserShares['valid'] / (int)$aRoundShares['valid'] ) * (float)$this->config['reward'], 8);
$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['payout'] = round((float)$aEstimates['block'] - (float)$aEstimates['donation'] - (float)$aEstimates['fee'], 8);
if ($this->config['payout_system'] != 'pps') {
if (@$value1['valid'] > 0 && @$value2['valid'] > 0) {
$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;
$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);
} else {
$aEstimates['block'] = 0;
$aEstimates['fee'] = 0;
$aEstimates['donation'] = 0;
$aEstimates['payout'] = 0;
}
} else {
$aEstimates['block'] = 0;
$aEstimates['fee'] = 0;
$aEstimates['donation'] = 0;
$aEstimates['payout'] = 0;
// Hack so we can use this method for PPS estimates too
// value1 = shares/s
// value2 = avg share difficulty
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;
}
/**
* 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');
class Token Extends Base {
var $table = 'tokens';
protected $table = 'tokens';
/**
* 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");
if ($stmt && $stmt->bind_param('s', $strToken) && $stmt->execute() && $result = $stmt->get_result())
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())
return $strToken;
$this->setErrorMessage('Unable to create new token');
$this->debug->append('Failed to create new token in database: ' . $this->mysqli->error);
return false;
return $this->sqlError();
}
/**
@ -50,7 +48,7 @@ class Token Extends Base {
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE token = ? LIMIT 1");
if ($stmt && $stmt->bind_param('s', $token) && $stmt->execute())
return true;
return false;
return $this->sqlError();
}
}
@ -58,3 +56,4 @@ $oToken = new Token();
$oToken->setDebug($debug);
$oToken->setMysql($mysqli);
$oToken->setTokenType($tokentype);
$oToken->setErrorCodes($aErrorCodes);

View File

@ -5,7 +5,8 @@ if (!defined('SECURITY'))
die('Hacking attempt');
class Token_Type Extends Base {
var $table = 'token_types';
protected $table = 'token_types';
/**
* Return ID for specific token
* @param strName string Token Name
@ -19,3 +20,4 @@ class Token_Type Extends Base {
$tokentype = new Token_Type();
$tokentype->setDebug($debug);
$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
* @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;
if (is_null($ch)) {
@ -32,9 +32,15 @@ class Tools extends Base {
// run the query
$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);
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;
}

View File

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

View File

@ -4,39 +4,12 @@
if (!defined('SECURITY'))
die('Hacking attempt');
class User {
private $sError = '';
class User extends Base {
protected $table = 'accounts';
private $userID = false;
private $table = 'accounts';
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
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) {
return hash('sha256', $string.$this->salt);
}
@ -174,31 +147,6 @@ class User {
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
* @param none
@ -243,31 +191,6 @@ class User {
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
* @param userID int User ID
@ -438,12 +361,34 @@ class User {
// Enforce generation of a new Session ID and delete the old
session_regenerate_id(true);
// 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 (!headers_sent()) header('Location: ' . $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
* @return table string This classes table name
@ -493,7 +438,7 @@ class User {
**/
public function register($username, $password1, $password2, $pin, $email1='', $email2='', $strToken='') {
$this->debug->append("STA " . __METHOD__, 4);
if (strlen($username > 40)) {
if (strlen($username) > 40) {
$this->setErrorMessage('Username exceeding character limit');
return false;
}
@ -687,7 +632,11 @@ class User {
}
// 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->setToken($oToken);
$user->setBitcoin($bitcoin);

View File

@ -1,38 +1,10 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
class Worker {
private $sError = '';
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;
}
class Worker extends Base {
protected $table = 'pool_worker';
/**
* Update worker list for a user
@ -61,9 +33,7 @@ class Worker {
}
if ($iFailed == 0)
return true;
// Catchall
$this->setErrorMessage('Failed to update ' . $iFailed . ' worker.');
return false;
return $this->sqlError('E0053', $iFailed);
}
/**
@ -71,20 +41,21 @@ class Worker {
* @param none
* @return data array Workers in IDLE state and monitoring enabled
**/
public function getAllIdleWorkers() {
public function getAllIdleWorkers($interval=600) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("
SELECT account_id, id, username
FROM " . $this->table . " AS w
WHERE monitor = 1
AND (
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)
) = 0");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
SELECT w.account_id AS account_id, w.id AS id, w.username AS username
FROM " . $this->share->getTableName() . " AS s
RIGHT JOIN " . $this->getTableName() . " AS w
ON w.username = s.username
AND s.time > DATE_SUB(now(), INTERVAL ? SECOND)
AND our_result = 'Y'
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);
// Catchall
$this->setErrorMessage("Unable to fetch IDLE, monitored workers");
return false;
return $this->sqlError('E0054');
}
/**
@ -92,7 +63,7 @@ class Worker {
* @param id int Worker ID
* @return mixed array Worker details
**/
public function getWorker($id) {
public function getWorker($id, $interval=600) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("
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
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() . "
WHERE
username = w.username
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
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)) * 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() . "
WHERE
username = w.username
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
AND time > DATE_SUB(now(), INTERVAL ? SECOND)
) AS hashrate,
(
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all, 2), 0)
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)
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
FROM $this->table AS w
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();
// Catchall
return false;
return $this->sqlError('E0055');
}
/**
@ -136,44 +106,90 @@ class Worker {
* @param account_id int User ID
* @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);
$stmt = $this->mysqli->prepare("
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->getArchiveTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS count_all_archive,
( 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 ? SECOND)) AS count_all_archive,
(
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() . "
WHERE
username = w.username
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
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)) * 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() . "
WHERE
username = w.username
AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)
AND time > DATE_SUB(now(), INTERVAL ? SECOND)
) AS hashrate,
(
SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all, 2), 0)
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)
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
FROM $this->table AS w
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);
// Catchall
$this->setErrorMessage('Failed to fetch workers for your account');
$this->debug->append('Fetching workers failed: ' . $this->mysqli->error);
return false;
return $this->sqlError('E0056');
}
/**
* 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() {
$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())
return $result->fetch_object()->total;
return false;
return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->total);
return $this->sqlError();
}
/**
@ -201,22 +222,20 @@ class Worker {
public function addWorker($account_id, $workerName, $workerPassword) {
$this->debug->append("STA " . __METHOD__, 4);
if ('' === $workerName || '' === $workerPassword) {
$this->setErrorMessage('Worker name and/or password may not be empty');
$this->setErrorMessage($this->getErrorMsg('E0058'));
return false;
}
$username = $this->user->getUserName($account_id);
$workerName = "$username.$workerName";
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, username, password) VALUES(?, ?, ?)");
if ($this->checkStmt($stmt)) {
$stmt->bind_param('iss', $account_id, $workerName, $workerPassword);
if ($this->checkStmt($stmt) && $stmt->bind_param('iss', $account_id, $workerName, $workerPassword)) {
if (!$stmt->execute()) {
$this->setErrorMessage( 'Failed to add worker' );
if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Worker already exists' );
return false;
if ($stmt->sqlstate == '23000') return $this->sqlError('E0059');
} else {
return true;
}
return true;
}
return false;
return $this->sqlError('E0060');
}
/**
@ -228,16 +247,19 @@ class Worker {
public function deleteWorker($account_id, $id) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE account_id = ? AND id = ?");
if ($this->checkStmt($stmt)) {
$stmt->bind_param('ii', $account_id, $id);
if ($stmt->execute() && $stmt->affected_rows == 1) {
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $account_id, $id) && $stmt->execute() && $stmt->affected_rows == 1)
return true;
} else {
$this->setErrorMessage( 'Unable to delete worker' );
}
}
return false;
return $this->sqlError('E0061');
}
}
$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'),
'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(
'display' => 'Pool Hashrate Modifier', 'type' => 'select',
'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'),
'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(
'display' => 'Pool Statistics', 'type' => 'select',
'options' => array( 0 => 'Private', 1 => 'Public'),
@ -166,11 +195,25 @@ $aSettings['acl'][] = array(
'tooltip' => 'Make the round statistics page private (users only) or public.'
);
$aSettings['acl'][] = array(
'display' => 'Round Transactions', 'type' => 'select',
'options' => array( 0 => 'Admins', 1 => 'Public'),
'default' => 0,
'name' => 'acl_round_transactions', 'value' => $setting->getValue('acl_round_transactions'),
'tooltip' => 'Display all transactions regardless of admin status.'
'display' => 'Block Finder Statistics', 'type' => 'select',
'options' => array( 0 => 'Private', 1 => 'Public'),
'default' => 1,
'name' => 'acl_blockfinder_statistics', 'value' => $setting->getValue('acl_blockfinder_statistics'),
'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(
'display' => 'Disable e-mail confirmations', 'type' => 'select',
@ -228,6 +271,34 @@ $aSettings['system'][] = array(
'name' => 'disable_contactform', 'value' => $setting->getValue('disable_contactform'),
'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(
'display' => 'Enable re-Captcha', 'type' => 'select',
'options' => array( 0 => 'No', 1 => 'Yes' ),
@ -249,5 +320,12 @@ $aSettings['recaptcha'][] = array(
'name' => 'recaptcha_public_key', 'value' => $setting->getValue('recaptcha_public_key'),
'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
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
*
@ -58,6 +69,18 @@ $config['wallet']['host'] = 'localhost:19334';
$config['wallet']['username'] = '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
*
@ -86,7 +109,6 @@ $config['price']['url'] = 'https://btc-e.com';
$config['price']['target'] = '/api/2/ltc_usd/ticker';
$config['price']['currency'] = 'USD';
/**
* Automatic payout thresholds
*
@ -123,6 +145,20 @@ $config['accounts']['invitations']['count'] = 5;
// Currency system used in this pool, default: `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
*
@ -231,9 +267,20 @@ $config['fees'] = 0;
* type = `blockavg`
* 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']['type'] = 'blockavg';
$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
// 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']['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
*
@ -413,4 +456,19 @@ $config['cookie']['secure'] = false;
**/
$config['smarty']['cache'] = 0;
$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
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'))
die('Hacking attempt');
// Tempalte specifics
$smarty->assign("CONTENT", "default.tpl");
if ($setting->getValue('disable_about')) {
$_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'])) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Updated notification settings');
} 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
foreach ($aCrons as $strCron) {
$aCronStatus[$strCron] = array(
'disabled' => $monitoring->getStatus($strCron . '_disabled'),
'exit' => $monitoring->getStatus($strCron . '_status'),
'active' => $monitoring->getStatus($strCron . '_active'),
'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']);
// 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) {
$aBalance = $transaction->getBalance($aUser['id']);
$aUser['balance'] = $aBalance['confirmed'];
$aUser['hashrate'] = $statistics->getUserHashrate($aUser['id']);
if ($aUser['shares'] > 0) {
$aUser['payout']['est_block'] = round(( (int)$aUser['shares'] / (int)$aRoundShares['valid'] ) * (int)$config['reward'], 3);
$aUser['payout']['est_fee'] = round(($config['fees'] / 100) * $aUser['payout']['est_block'], 3);
$aUser['payout']['est_donation'] = round((( $aUser['donate_percent'] / 100) * ($aUser['payout']['est_block'] - $aUser['payout']['est_fee'])), 3);
$aUser['payout']['est_payout'] = round($aUser['payout']['est_block'] - $aUser['payout']['est_donation'] - $aUser['payout']['est_fee'], 3);
} else {
$aUser['payout']['est_block'] = 0;
$aUser['payout']['est_fee'] = 0;
$aUser['payout']['est_donation'] = 0;
$aUser['payout']['est_payout'] = 0;
}
$aUser['estimates'] = $statistics->getUserEstimates($aRoundShares, $aUser['shares'], $aUser['donate_percent'], $aUser['no_fees']);
$aUsers[$iKey] = $aUser;
}
// 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);
if ($bitcoin->can_connect() === true){
$dBalance = $bitcoin->query('getbalance');
$dGetInfo = $bitcoin->query('getinfo');
if (is_array($dGetInfo) && array_key_exists('newmint', $dGetInfo)) {
$dNewmint = $dGetInfo['newmint'];
$aGetInfo = $bitcoin->query('getinfo');
if (is_array($aGetInfo) && array_key_exists('newmint', $aGetInfo)) {
$dNewmint = $aGetInfo['newmint'];
} else {
$dNewmint = -1;
}
} else {
$aGetInfo = array('errors' => 'Unable to connect');
$dBalance = 0;
$dNewmint = -1;
$_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("LOCKED", $dLockedBalance);
$smarty->assign("NEWMINT", $dNewmint);
$smarty->assign("COININFO", $aGetInfo);
// Tempalte specifics
$smarty->assign("CONTENT", "default.tpl");

View File

@ -3,8 +3,22 @@
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
// Check if the API is activated
$api->isActive();
// Check if the system is enabled
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
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
@ -32,27 +46,85 @@ $dPoolHashrate = $statistics->getCurrentHashrate($interval);
if ($dPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $dPoolHashrate;
$dPersonalHashrate = $statistics->getUserHashrate($user_id, $interval);
$dPersonalSharerate = $statistics->getUserSharerate($user_id, $interval);
$dPersonalShareDifficulty = $statistics->getUserShareDifficulty($user_id, $interval);
$statistics->setGetCache(true);
// Use caches for this one
$aUserRoundShares = $statistics->getUserShares($user_id);
$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
$dPersonalHashrateAdjusted = $dPersonalHashrate * $dPersonalHashrateModifier;
$dPoolHashrateAdjusted = $dPoolHashrate * $dPoolHashrateModifier;
$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
$data = array(
'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),
'pool' => array( 'hashrate' => $dPoolHashrateAdjusted, 'shares' => $aRoundShares ),
'personal' => array (
'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 ),
);
echo $api->get_json($data);
// Supress master template
$supress_master = 1;
echo $api->get_json($data);
?>

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
$data = array(
'pool_name' => $setting->getValue('website_name'),
'hashrate' => $iCurrentPoolHashrate,
'efficiency' => $dEfficiency,
'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
$user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']);
// Fetch transaction summary
$aTransactionSummary = $transaction->getTransactionSummary($user_id);
// Output JSON format
$data = array(
'username' => $user->getUsername($user_id),
'shares' => $statistics->getUserShares($user_id),
'hashrate' => $statistics->getUserHashrate($user_id),
'sharerate' => $statistics->getUserSharerate($user_id)
'sharerate' => $statistics->getUserSharerate($user_id),
'transactions' => $aTransactionSummary
);
echo $api->get_json($data);

View File

@ -6,35 +6,28 @@ if (!defined('SECURITY')) die('Hacking attempt');
if ($user->isAuthenticated()) {
if (! $interval = $setting->getValue('statistics_ajax_data_interval')) $interval = 300;
// Defaults to get rid of PHP Notice warnings
$dNetworkHashrate = 0;
$dDifficulty = 1;
$aRoundShares = 1;
// Only run these if the user is logged in
$aRoundShares = $statistics->getRoundShares();
$dDifficulty = 1;
$dNetworkHashrate = 1;
$iBlock = 0;
if ($bitcoin->can_connect() === true) {
$dDifficulty = $bitcoin->query('getdifficulty');
if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty))
$dDifficulty = $dDifficulty['proof-of-work'];
}
// Always fetch this since we need for ministats header
$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;
$dDifficulty = $bitcoin->getdifficulty();
$dNetworkHashrate = $bitcoin->getnetworkhashps();
$iBlock = $bitcoin->getblockcount();
}
// 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;
$iCurrentPoolHashrate = $statistics->getCurrentHashrate();
$iCurrentPoolShareRate = $statistics->getCurrentShareRate();
@ -43,6 +36,10 @@ if ($user->isAuthenticated()) {
if ($iCurrentPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $iCurrentPoolHashrate;
// 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('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'))
die('Hacking attempt');
$smarty->assign("SITESTRATUMPORT", $config['gettingstarted']['stratumport']);
$smarty->assign("SITECOINNAME", $config['gettingstarted']['coinname']);
$smarty->assign("SITECOINURL", $config['gettingstarted']['coinurl']);
// Tempalte specifics
$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']);
}
}
$smarty->assign("HIDEAUTHOR", $setting->getValue('acl_hide_news_author'));
$smarty->assign("NEWS", $aNews);
} else {
$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');
} else if ($user->checkLogin(@$_POST['username'], @$_POST['password']) ) {
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);
exit('<meta http-equiv="refresh" content="0; url=' . $location . '"/>');
} else if (@$_POST['username'] && @$_POST['password']) {

View File

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

View File

@ -23,7 +23,7 @@ if ($setting->getValue('disable_invitations') && $setting->getValue('lock_regist
if ($rsp->is_valid) {
$smarty->assign("RECAPTCHA", recaptcha_get_html($setting->getValue('recaptcha_public_key')));
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');
} else {
$_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
} else {
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');
} else {
$_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);
// Grab the last blocks found
$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
$smarty->assign("FIRSTBLOCKFOUND", $iTimeSinceFirstBlockFound);
$smarty->assign("LASTBLOCKSBYTIME", $iFoundBlocksByTime);
$smarty->assign("BLOCKSFOUND", $aBlocksFoundData);
$smarty->assign("BLOCKLIMIT", $iLimit);
$smarty->assign("USEBLOCKAVERAGE", $use_average);
$smarty->assign("POOLSTATS", $aPoolStatistics);
} else {
$debug->append('Using cached page', 3);
}

View File

@ -22,7 +22,7 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
// Top hash contributors
$aContributorsHashes = $statistics->getTopContributors('hashes', 15);
// Grab the last 10 blocks found
// Grab the last 5 blocks found as a quick overview
$iLimit = 5;
$aBlocksFoundData = $statistics->getBlocksFound($iLimit);
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;
// Time since last block
$now = new DateTime( "now" );
if (!empty($aBlockData)) {
$dTimeSinceLast = ($now->getTimestamp() - $aBlockData['time']);
$dTimeSinceLast = (time() - $aBlockData['time']);
if ($dTimeSinceLast < 0) $dTimeSinceLast = 0;
} else {
$dTimeSinceLast = 0;

View File

@ -6,26 +6,40 @@ 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 (@$_REQUEST['next'] && !empty($_REQUEST['height'])) {
$iKey = $roundstats->getNextBlock($_REQUEST['height']);
} 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'];
}
if (@$_REQUEST['search']) {
$_REQUEST['height'] = $roundstats->searchForBlockHeight($_REQUEST['search']);
}
$aDetailsForBlockHeight = $roundstats->getDetailsForBlockHeight($iKey, $user->isAdmin(@$_SESSION['USERDATA']['id']));
$aRoundShareStats = $roundstats->getRoundStatsForAccounts($iKey, $user->isAdmin(@$_SESSION['USERDATA']['id']));
if ($user->isAdmin(@$_SESSION['USERDATA']['id']) || $setting->getValue('acl_round_transactions')) {
$aUserRoundTransactions = $roundstats->getAllRoundTransactions($iKey, @$_SESSION['USERDATA']['id']);
if (@$_REQUEST['next'] && !empty($_REQUEST['height'])) {
$iHeight = @$roundstats->getNextBlock($_REQUEST['height']);
if (!$iHeight) {
$iBlock = $block->getLast();
$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 {
$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

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'),
'disable_mp' => $setting->getValue('disable_mp'),
'config' => array(
'algorithm' => $config['algorithm'],
'target_bits' => $config['target_bits'],
'accounts' => $config['accounts'],
'disable_invitations' => $setting->getValue('disable_invitations'),
'disable_notifications' => $setting->getValue('disable_notifications'),
'monitoring_uptimerobot_api_keys' => $setting->getValue('monitoring_uptimerobot_api_keys'),
'statistics_ajax_refresh_interval' => $statistics_ajax_refresh_interval,
'price' => array( 'currency' => $config['price']['currency'] ),
'targetdiff' => $config['difficulty'],
@ -89,31 +92,21 @@ $aGlobal['website']['email'] = $setting->getValue('website_email');
$aGlobal['website']['api']['disabled'] = $setting->getValue('disable_api');
$aGlobal['website']['blockexplorer']['disabled'] = $setting->getValue('website_blockexplorer_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_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
$aGlobal['acl']['pool']['statistics'] = $setting->getValue('acl_pool_statistics');
$aGlobal['acl']['block']['statistics'] = $setting->getValue('acl_block_statistics');
$aGlobal['acl']['round']['statistics'] = $setting->getValue('acl_round_statistics');
// 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['ppsvalue'] = number_format(round($pps_reward / (pow(2,32) * $dDifficulty) * pow(2, $config['pps_target']), 12) ,12);
$aGlobal['acl']['blockfinder']['statistics'] = $setting->getValue('acl_blockfinder_statistics');
$aGlobal['acl']['uptime']['statistics'] = $setting->getValue('acl_uptime_statistics');
// We don't want these session infos cached
if (@$_SESSION['USERDATA']['id']) {
@ -122,17 +115,16 @@ if (@$_SESSION['USERDATA']['id']) {
// Other userdata that we can cache savely
$aGlobal['userdata']['shares'] = $statistics->getUserShares($_SESSION['USERDATA']['id']);
$aGlobal['userdata']['hashrate'] = $statistics->getUserHashrate($_SESSION['USERDATA']['id']) * $dPersonalHashrateModifier;
$aGlobal['userdata']['rawhashrate'] = $statistics->getUserHashrate($_SESSION['USERDATA']['id']);
$aGlobal['userdata']['hashrate'] = $aGlobal['userdata']['rawhashrate'] * $dPersonalHashrateModifier;
$aGlobal['userdata']['sharerate'] = $statistics->getUserSharerate($_SESSION['USERDATA']['id']);
switch ($config['payout_system']) {
case 'prop' || 'pplns':
case 'prop':
// Some estimations
$aEstimates = $statistics->getUserEstimates($aRoundShares, $aGlobal['userdata']['shares'], $aGlobal['userdata']['donate_percent'], $aGlobal['userdata']['no_fees']);
$aGlobal['userdata']['est_block'] = $aEstimates['block'];
$aGlobal['userdata']['est_fee'] = $aEstimates['fee'];
$aGlobal['userdata']['est_donation'] = $aEstimates['donation'];
$aGlobal['userdata']['est_payout'] = $aEstimates['payout'];
$aGlobal['userdata']['estimates'] = $aEstimates;
break;
case 'pplns':
$aGlobal['pplns']['target'] = $config['pplns']['shares']['default'];
if ($aLastBlock = $block->getLast()) {
@ -140,8 +132,31 @@ if (@$_SESSION['USERDATA']['id']) {
$aGlobal['pplns']['target'] = $iAvgBlockShares;
}
}
$aEstimates = $statistics->getUserEstimates($aRoundShares, $aGlobal['userdata']['shares'], $aGlobal['userdata']['donate_percent'], $aGlobal['userdata']['no_fees']);
$aGlobal['userdata']['estimates'] = $aEstimates;
break;
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;
}
@ -162,5 +177,6 @@ $smarty->assign('DEBUG', DEBUG);
// Make it available in Smarty
$smarty->assign('PATH', 'site_assets/' . THEME);
$smarty->assign('GLOBALASSETS', 'site_assets/global');
$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
$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
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