Merge pull request #11 from TheSerapher/next

next
This commit is contained in:
obigal 2013-07-21 10:37:57 -07:00
commit 9f02359c4c
111 changed files with 2966 additions and 647 deletions

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
/public/include/config/global.inc.php
/public/templates/compile/*.php
/cronjobs/logs/*.txt
/public/templates/cache/*.php

View File

@ -27,7 +27,7 @@ They have succesfully mined blocks on each of those pools listed.
All pools are running on Stratum only.
| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes |
| -------- | ---- | ------------- | ----------------- | ----- |
| -------- | ---- | ------------- | -----------------: | ----- |
| http://wdc.nordicminers.eu | Worldcoin | n/a | n/a | |
| http://lky.nordicminers.eu | Luckycoin | n/a | n/a | |
| http://fst.nordicminers.eu | Fastcoin | n/a | n/a | |
@ -48,7 +48,7 @@ running more or less without any issues (related to `mmcfe-ng` that is ;-)). He
the most powerful pool!
| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes |
| -------- | ---- | ------------- | ----------------- | ----- |
| -------- | ---- | ------------- | ------------------: | ----- |
| http://www.ejpool.info | Litecoin | 155 MHash | 120 | |
### Obigal
@ -65,6 +65,12 @@ Small Time Miners are running various stratum only pools for different coins.
### Feeleep75
| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes |
| -------- | ---- | ------------- | ------------------- | ----- |
| -------- | ---- | ------------- | ------------------: | ----- |
| http://bot.coinmine.pl | Bottlecaps | 3 - 50 MHash | n/a | PoS/PoW type coin |
| http://yacp.coinmine.pl | YaCoin | 19 MHash | n/a | |
### LiteSaber
| Pool URL | Coin | Avg. Hashrate | Avg. Active Workers | Notes |
| -------- | ---- | ------------- | ------------------: | ----- |
| http://coinhuntr.com | Litecoin | 200 MHash | 250 | Custom Frontend template |

View File

@ -32,6 +32,7 @@ These people have supported this project with a donation:
* [vias](https://github.com/vias79)
* [WKNiGHT](https://github.com/WKNiGHT-)
* [ZC](https://github.com/zccopwrx)
* Nutnut
Pools running mmcfe-ng
======================
@ -68,12 +69,13 @@ Features
The following feature have been implemented so far:
* Mobile WebUI **NEW**
* Fully re-written GUI with [Smarty][2] templates
* Mobile WebUI
* Reward Systems
* Propotional
* PPS
* (Planned) PPLNS
* Use of memcache for statistics instead of a cronjob
* PPLNS **NEW**
* Statistics are cached in Memcache by Cronjob for quick data access
* Web User accounts
* Re-Captcha protected registration form
* Worker accounts
@ -87,9 +89,11 @@ The following feature have been implemented so far:
* Auto payout
* Transaction list (confirmed and unconfirmed)
* Admin Panel
* Cron Monitoring Overview **NEW**
* User Listing including statistics
* Wallet information
* News Posts **NEW**
* User Transactions **NEW**
* News Posts
* Notification system
* IDLE Workers
* New blocks found in pool
@ -139,3 +143,4 @@ limitations under the License.
[1]: https://github.com/TheSerapher/php-mmcfe-ng/issues "Issue"
[2]: http://www.smarty.net/docs/en/ "Smarty"

36
cronjobs/archive_cleanup.php Executable file
View File

@ -0,0 +1,36 @@
#!/usr/bin/php
<?php
/*
Copyright:: 2013, Sebastian Grewe
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Include all settings and classes
require_once('shared.inc.php');
// If we don't keep archives, delete some now to release disk space
if (!$share->purgeArchive()) {
$log->logError("Failed to delete archived shares, not critical but should be checked!");
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Failed to delete archived shares");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
}
// Cron cleanup and monitoring
require_once('cron_end.inc.php');
?>

View File

@ -22,30 +22,28 @@ limitations under the License.
// Include all settings and classes
require_once('shared.inc.php');
verbose("Running auto-payouts ...");
if ($bitcoin->can_connect() !== true) {
verbose(" unable to connect to RPC server, exiting\n");
$log->logFatal(" unable to connect to RPC server, exiting");
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
}
// Mark this job as active
$setting->setValue('auto_payout_active', 1);
// Fetch all users with setup AP
$users = $user->getAllAutoPayout();
// Quick summary
verbose(" found " . count($users) . " queued payout(s)\n");
$log->logInfo(" found " . count($users) . " queued payout(s)");
// Go through users and run transactions
if (! empty($users)) {
verbose("\tUserID\tUsername\tBalance\tThreshold\tAddress\t\t\t\t\tStatus\n\n");
$log->logInfo("\tUserID\tUsername\tBalance\tThreshold\tAddress");
foreach ($users as $aUserData) {
$aBalance = $transaction->getBalance($aUserData['id']);
$dBalance = $aBalance['confirmed'];
verbose("\t" . $aUserData['id'] . "\t" . $aUserData['username'] . "\t" . $dBalance . "\t" . $aUserData['ap_threshold'] . "\t\t" . $aUserData['coin_address'] . "\t");
$log->logInfo("\t" . $aUserData['id'] . "\t" . $aUserData['username'] . "\t" . $dBalance . "\t" . $aUserData['ap_threshold'] . "\t\t" . $aUserData['coin_address']);
// Only run if balance meets threshold and can pay the potential transaction fee
if ($dBalance > $aUserData['ap_threshold'] && $dBalance > $config['txfee']) {
@ -53,7 +51,7 @@ if (! empty($users)) {
try {
$bitcoin->validateaddress($aUserData['coin_address']);
} catch (BitcoinClientException $e) {
verbose("VERIFY FAILED\n");
$log->logError('Failed to verifu this users coin address, skipping payout');
continue;
}
@ -61,7 +59,7 @@ if (! empty($users)) {
try {
$bitcoin->sendtoaddress($aUserData['coin_address'], $dBalance);
} catch (BitcoinClientException $e) {
verbose("SEND FAILED\n");
$log->logError('Failed to send requested balance to coin address, please check payout process');
continue;
}
@ -71,24 +69,17 @@ if (! empty($users)) {
$aMailData['email'] = $user->getUserEmail($user->getUserName($aUserData['id']));
$aMailData['subject'] = 'Auto Payout Completed';
$aMailData['amount'] = $dBalance;
if (!$notification->sendNotification($aUserData['id'], 'auto_payout', $aMailData)) {
verbose("NOTIFY FAILED\n");
} else {
verbose("OK\n");
}
if (!$notification->sendNotification($aUserData['id'], 'auto_payout', $aMailData))
$log->logError('Failed to send notification email to users address: ' . $aMailData['email']);
} else {
verbose("FAILED\n");
$log->logError('Failed to add new Debit_AP transaction in database for user ' . $user->getUserName($aUserData['id']));
}
} else {
verbose("SKIPPED\n");
}
}
} else {
verbose(" no user has configured their AP > 0\n");
$log->logDebug(" no user has configured their AP > 0");
}
// Mark this job as inactive
$setting->setValue('auto_payout_active', 0);
// Cron cleanup and monitoring
require_once('cron_end.inc.php');
?>

View File

@ -23,33 +23,37 @@ limitations under the License.
require_once('shared.inc.php');
if ( $bitcoin->can_connect() !== true ) {
verbose("Failed to connect to RPC server\n");
$log->logFatal("Failed to connect to RPC server\n");
$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);
}
// Fetch all unconfirmed blocks
$aAllBlocks = $block->getAllUnconfirmed($config['confirmations']);
verbose("ID\tBlockhash\tConfirmations\t\n");
$log->logInfo("ID\tHeight\tBlockhash\tConfirmations");
foreach ($aAllBlocks as $iIndex => $aBlock) {
$aBlockInfo = $bitcoin->query('getblock', $aBlock['blockhash']);
// Fetch this blocks transaction details to find orphan blocks
$aTxDetails = $bitcoin->query('gettransaction', $aBlockInfo['tx'][0]);
verbose($aBlock['id'] . "\t" . $aBlock['blockhash'] . "\t" . $aBlock['confirmations'] . " -> " . $aBlockInfo['confirmations'] . "\t");
$log->logInfo($aBlock['id'] . "\t" . $aBlock['height'] . "\t" . $aBlock['blockhash'] . "\t" . $aBlock['confirmations'] . " -> " . $aBlockInfo['confirmations']);
if ($aTxDetails['details'][0]['category'] == 'orphan') {
// We have an orphaned block, we need to invalidate all transactions for this one
if ($transaction->setOrphan($aBlock['id']) && $block->setConfirmations($aBlock['id'], -1)) {
verbose("ORPHAN\n");
if ($block->setConfirmations($aBlock['id'], -1)) {
$log->logInfo(" Block marked as orphan");
} else {
verbose("ORPHAN_ERR");
$log->logError(" Block became orphaned but unable to update database entries");
}
continue;
}
if ($aBlock['confirmations'] == $aBlockInfo['confirmations']) {
verbose("SKIPPED\n");
} else if ($block->setConfirmations($aBlock['id'], $aBlockInfo['confirmations'])) {
verbose("UPDATED\n");
} else {
verbose("ERROR\n");
$log->logDebug(' No update needed');
} else if (!$block->setConfirmations($aBlock['id'], $aBlockInfo['confirmations'])) {
$log->logError(' Failed to update block confirmations');
}
}
require_once('cron_end.inc.php');
?>

28
cronjobs/cron_end.inc.php Normal file
View File

@ -0,0 +1,28 @@
<?php
/*
Copyright:: 2013, Sebastian Grewe
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Monitoring cleanup and status update
$monitoring->setStatus($cron_name . "_message", "message", "OK");
$monitoring->setStatus($cron_name . "_status", "okerror", 0);
$monitoring->setStatus($cron_name . "_runtime", "time", microtime(true) - $cron_start[$cron_name]);
$monitoring->setStatus($cron_name . "_endtime", "date", time());
// Mark cron as running for monitoring
$monitoring->setStatus($cron_name . '_active', "yesno", 0);
?>

View File

@ -25,24 +25,25 @@ require_once('shared.inc.php');
// Fetch our last block found from the DB as a starting point
$aLastBlock = @$block->getLast();
$strLastBlockHash = $aLastBlock['blockhash'];
if (!$strLastBlockHash) {
$strLastBlockHash = '';
}
if (!$strLastBlockHash) $strLastBlockHash = '';
// Fetch all transactions since our last block
if ( $bitcoin->can_connect() === true ){
$aTransactions = $bitcoin->query('listsinceblock', $strLastBlockHash);
} else {
verbose("Aborted: " . $bitcoin->can_connect() . "\n");
$log->logFatal('Unable to conenct to RPC server backend');
$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);
}
// Nothing to do so bail out
if (empty($aTransactions['transactions'])) {
verbose("No new RPC transactions since last block\n");
$log->logDebug('No new RPC transactions since last block');
} else {
// Table header
verbose("Blockhash\t\tHeight\tAmount\tConfirmations\tDiff\t\tTime\t\t\tStatus\n");
$log->logInfo("Blockhash\t\tHeight\tAmount\tConfirmations\tDiff\t\tTime");
// Let us add those blocks as unaccounted
foreach ($aTransactions['transactions'] as $iIndex => $aData) {
@ -51,70 +52,67 @@ if (empty($aTransactions['transactions'])) {
$config['reward_type'] == 'block' ? $aData['amount'] = $aData['amount'] : $aData['amount'] = $config['reward'];
$aData['height'] = $aBlockInfo['height'];
$aData['difficulty'] = $aBlockInfo['difficulty'];
verbose(substr($aData['blockhash'], 0, 15) . "...\t" .
$log->logInfo(substr($aData['blockhash'], 0, 15) . "...\t" .
$aData['height'] . "\t" .
$aData['amount'] . "\t" .
$aData['confirmations'] . "\t\t" .
$aData['difficulty'] . "\t" .
strftime("%Y-%m-%d %H:%M:%S", $aData['time']) . "\t");
if ( $block->addBlock($aData) ) {
verbose("Added\n");
} else {
verbose("Failed" . "\n");
strftime("%Y-%m-%d %H:%M:%S", $aData['time']));
if (!$block->addBlock($aData) ) {
$log->logFatal('Unable to add this block to database: ' . $aData['height']);
}
}
}
}
verbose("\n");
// Now with our blocks added we can scan for their upstream shares
$aAllBlocks = $block->getAllUnaccounted('ASC');
$aAllBlocks = $block->getAllUnsetShareId('ASC');
if (empty($aAllBlocks)) {
verbose("No new unaccounted blocks found\n");
$log->logDebug('No new blocks without share_id found in database');
} else {
// Loop through our unaccounted blocks
verbose("\nBlock ID\tBlock Height\tAmount\tShare ID\tShares\tFinder\t\t\tStatus\n");
$log->logInfo("Block ID\t\tHeight\tAmount\tShare ID\tShares\tFinder\tType");
foreach ($aAllBlocks as $iIndex => $aBlock) {
if (empty($aBlock['share_id'])) {
// Fetch this blocks upstream ID
if ($share->setUpstream($block->getLastUpstreamId(), $aBlock['time'])) {
$aBlockInfo = $bitcoin->query('getblock', $aBlock['blockhash']);
if ($share->setUpstream($aBlockInfo, $block->getLastUpstreamId())) {
$iCurrentUpstreamId = $share->getUpstreamId();
$iAccountId = $user->getUserId($share->getUpstreamFinder());
} else {
verbose("\nUnable to fetch blocks upstream share. Aborting!\n");
verbose($share->getError() . "\n");
$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()) {
$iPreviousShareId = 0;
verbose("\nUnable to find highest share ID found so far\n");
verbose("If this is your first block, this is normal\n\n");
$log->logInfo('Unable to find highest share ID found so far, if this is your first block, this is normal.');
}
$iRoundShares = $share->getRoundShares($iPreviousShareId, $iCurrentUpstreamId);
// Store new information
$strStatus = "OK";
if (!$block->setShareId($aBlock['id'], $iCurrentUpstreamId))
$strStatus = "Share ID Failed";
$log->logError('Failed to update share ID in database for block ' . $aBlock['height']);
if (!$block->setFinder($aBlock['id'], $iAccountId))
$strStatus = "Finder Failed";
$log->logError('Failed to update finder account ID in database for block ' . $aBlock['height']);
if (!$block->setShares($aBlock['id'], $iRoundShares))
$strStatus = "Shares Failed";
$log->logError('Failed to update share count in database for block ' . $aBlock['height']);
if ($config['block_bonus'] > 0 && !$transaction->addTransaction($iAccountId, $config['block_bonus'], 'Bonus', $aBlock['id'])) {
$strStatus = "Bonus Failed";
$log->logError('Failed to create Bonus transaction in database for user ' . $user->getUserName($iAccountId) . ' for block ' . $aBlock['height']);
}
verbose(
$log->logInfo(
$aBlock['id'] . "\t\t"
. $aBlock['height'] . "\t\t"
. $aBlock['amount'] . "\t"
. $iCurrentUpstreamId . "\t\t"
. $iRoundShares . "\t"
. "[$iAccountId] " . $user->getUserName($iAccountId) . "\t\t"
. $strStatus
. "\n"
. "[$iAccountId] " . $user->getUserName($iAccountId) . "\t"
. $share->share_type
);
// Notify users
@ -125,11 +123,13 @@ if (empty($aAllBlocks)) {
$aMailData['subject'] = 'New Block';
$aMailData['email'] = $user->getUserEmail($user->getUserName($aData['account_id']));
$aMailData['shares'] = $iRoundShares;
$notification->sendNotification($aData['account_id'], 'new_block', $aMailData);
if (!$notification->sendNotification($aData['account_id'], 'new_block', $aMailData))
$log->logError('Failed to notify user of new found block: ' . $user->getUserName($aData['account_id']));
}
}
}
}
}
?>
require_once('cron_end.inc.php');
?>

1
cronjobs/logs/README.md Normal file
View File

@ -0,0 +1 @@
Logging directory for cronjobs.

View File

@ -22,51 +22,49 @@ limitations under the License.
// Include all settings and classes
require_once('shared.inc.php');
verbose("Running system notifications\n");
verbose(" IDLE Worker Notifications ...");
$log->logDebug(" IDLE Worker Notifications ...");
// Find all IDLE workers
$aWorkers = $worker->getAllIdleWorkers();
if (empty($aWorkers)) {
verbose(" no idle workers found\n");
$log->logDebug(" no idle workers found\n");
} else {
verbose(" found " . count($aWorkers) . " IDLE workers\n");
$log->logInfo(" found " . count($aWorkers) . " IDLE workers\n");
foreach ($aWorkers as $aWorker) {
$aData = $aWorker;
$aData['username'] = $user->getUserName($aWorker['account_id']);
$aData['subject'] = 'IDLE Worker : ' . $aWorker['username'];
$aData['worker'] = $aWorker['username'];
$aData['email'] = $user->getUserEmail($aData['username']);
verbose(" " . $aWorker['username'] . "...");
if (!$notification->sendNotification($aWorker['account_id'], 'idle_worker', $aData)) {
verbose(" " . $notification->getError() . "\n");
} else {
verbose(" sent\n");
}
$log->logInfo(" " . $aWorker['username'] . "...");
if (!$notification->sendNotification($aWorker['account_id'], 'idle_worker', $aData))
$log->logError(" Failed sending notifications: " . $notification->getError() . "\n");
}
}
verbose(" Reset IDLE Worker Notifications ...");
$log->logDebug(" Reset IDLE Worker Notifications ...");
// We notified, lets check which recovered
$aNotifications = $notification->getAllActive('idle_worker');
if (!empty($aNotifications)) {
verbose(" found " . count($aNotifications) . " active notification(s)\n");
$log->logInfo(" found " . count($aNotifications) . " active notification(s)\n");
foreach ($aNotifications as $aNotification) {
$aData = json_decode($aNotification['data'], true);
$aWorker = $worker->getWorker($aData['id']);
verbose(" " . $aWorker['username'] . " ...");
$log->logInfo(" " . $aWorker['username'] . " ...");
if ($aWorker['active'] == 1) {
if ($notification->setInactive($aNotification['id'])) {
verbose(" updated #" . $aNotification['id'] . " for " . $aWorker['username'] . " as inactive\n");
$log->logInfo(" updated #" . $aNotification['id'] . " for " . $aWorker['username'] . " as inactive\n");
} else {
verbose(" failed to update #" . $aNotification['id'] . " for " . $aWorker['username'] . "\n");
$log->logInfo(" failed to update #" . $aNotification['id'] . " for " . $aWorker['username'] . "\n");
}
} else {
verbose(" still inactive\n");
$log->logInfo(" still inactive\n");
}
}
} else {
verbose(" no active IDLE worker notifications\n");
$log->logDebug(" no active IDLE worker notifications\n");
}
require_once('cron_end.inc.php');
?>

187
cronjobs/pplns_payout.php Executable file
View File

@ -0,0 +1,187 @@
#!/usr/bin/php
<?php
/*
Copyright:: 2013, Sebastian Grewe
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Include all settings and classes
require_once('shared.inc.php');
// Check if we are set as the payout system
if ($config['payout_system'] != 'pplns') {
$log->logInfo("Please activate this cron in configuration via payout_system = pplns");
exit(0);
}
// Fetch all unaccounted blocks
$aAllBlocks = $block->getAllUnaccounted('ASC');
if (empty($aAllBlocks)) {
$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);
}
$count = 0;
foreach ($aAllBlocks as $iIndex => $aBlock) {
// We support some dynamic share targets but fall back to our fixed value
// Re-calculate after each run due to re-targets in this loop
if ($config['pplns']['shares']['type'] == 'blockavg' && $block->getBlockCount() > 0) {
$pplns_target = round($block->getAvgBlockShares($config['pplns']['blockavg']['blockcount']));
} else {
$pplns_target = $config['pplns']['shares']['default'] ;
}
if (!$aBlock['accounted']) {
$iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0;
$iCurrentUpstreamId = $aBlock['share_id'];
if (!is_numeric($iCurrentUpstreamId)) {
$log->logFatal("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue");
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Block " . $aBlock['height'] . " has no share_id associated with it");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
}
$iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']);
$iNewRoundShares = 0;
$config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward'];
$aRoundAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']);
if ($iRoundShares >= $pplns_target) {
$log->logDebug("Matching or exceeding PPLNS target of $pplns_target with $iRoundShares");
$aAccountShares = $share->getSharesForAccounts($aBlock['share_id'] - $pplns_target, $aBlock['share_id']);
if (empty($aAccountShares)) {
$log->logFatal("No shares found for this block, aborted! Block Height : " . $aBlock['height']);
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "No shares found for this block: " . $aBlock['height']);
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
}
} else {
$log->logDebug("Not able to match PPLNS target of $pplns_target with $iRoundShares");
// We need to fill up with archived shares
// Grab the full current round shares since we didn't match target
$aAccountShares = $aRoundAccountShares;
if (empty($aAccountShares)) {
$log->logFatal("No shares found for this block, aborted! 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);
}
// Grab only the most recent shares from Archive that fill the missing shares
$log->logInfo('Fetching ' . ($pplns_target - $iRoundShares) . ' additional shares from archive');
if (!$aArchiveShares = $share->getArchiveShares($pplns_target - $iRoundShares)) {
$log->logError('Failed to fetch shares from archive, setting target to round total');
$pplns_target = $iRoundShares;
} else {
// Add archived shares to users current shares, if we have any in archive
if (is_array($aArchiveShares)) {
$log->logDebug('Found shares in archive to match PPLNS target, calculating per-user shares');
foreach($aAccountShares as $key => $aData) {
if (array_key_exists($aData['username'], $aArchiveShares)) {
$log->logDebug('Found user ' . $aData['username'] . ' in archived shares');
$log->logDebug(' valid : ' . $aAccountShares[$key]['valid'] . ' + ' . $aArchiveShares[$aData['username']]['valid'] . ' = ' . ($aAccountShares[$key]['valid'] + $aArchiveShares[$aData['username']]['valid']) );
$log->logDebug(' invalid : ' . $aAccountShares[$key]['invalid'] . ' + ' . $aArchiveShares[$aData['username']]['invalid'] . ' = ' . ($aAccountShares[$key]['invalid'] + $aArchiveShares[$aData['username']]['invalid']) );
$aAccountShares[$key]['valid'] += $aArchiveShares[$aData['username']]['valid'];
$aAccountShares[$key]['invalid'] += $aArchiveShares[$aData['username']]['invalid'];
}
}
}
// We tried to fill up to PPLNS target, now we need to check the actual shares to properly payout users
foreach($aAccountShares as $key => $aData) {
$iNewRoundShares += $aData['valid'];
}
}
}
// We filled from archive but still are not able to match PPLNS target, re-adjust
if ($iRoundShares < $iNewRoundShares) {
$log->logInfo('Adjusting round target to ' . $iNewRoundShares);
$iRoundShares = $iNewRoundShares;
}
// Table header for account shares
$log->logInfo("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee");
// Loop through all accounts that have found shares for this round
foreach ($aAccountShares as $key => $aData) {
// Payout based on PPLNS target shares, proportional payout for all users
$aData['percentage'] = number_format(round(( 100 / $pplns_target) * $aData['valid'], 8), 8);
$aData['payout'] = number_format(round(( $aData['percentage'] / 100 ) * $dReward, 8), 8);
// Defaults
$aData['fee' ] = 0;
$aData['donation'] = 0;
if ($config['fees'] > 0)
$aData['fee'] = number_format(round($config['fees'] / 100 * $aData['payout'], 8), 8);
// Calculate donation amount, fees not included
$aData['donation'] = number_format(round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 8), 8);
// Verbose output of this users calculations
$log->logInfo($aData['id'] . "\t" .
$aData['username'] . "\t" .
$aData['valid'] . "\t" .
$aData['invalid'] . "\t" .
$aData['percentage'] . "\t" .
$aData['payout'] . "\t" .
$aData['donation'] . "\t" .
$aData['fee']);
// Add full round share statistics, not just PPLNS
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']);
}
// 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']);
// 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']);
// 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']);
}
// 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');
// Delete all accounted shares
if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) {
$log->logFatal("Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting!");
exit(1);
}
// Mark this block as accounted for
if (!$block->setAccounted($aBlock['id'])) {
$log->logFatal("Failed to mark block as accounted! Aborting!");
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Failed to mark block " . $aBlock['height'] . " as accounted");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
}
}
}
require_once('cron_end.inc.php');
?>

View File

@ -25,7 +25,7 @@ require_once('shared.inc.php');
// Check if we are set as the payout system
if ($config['payout_system'] != 'pps') {
verbose("Please activate this cron in configuration via payout_system = pps\n");
$log->logInfo("Please activate this cron in configuration via payout_system = pps\n");
exit(0);
}
@ -35,13 +35,16 @@ if ( $bitcoin->can_connect() === true ){
if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty))
$dDifficulty = $dDifficulty['proof-of-work'];
} else {
verbose("Aborted: " . $bitcoin->can_connect() . "\n");
$log->logFatal("Aborted: " . $bitcoin->can_connect() . "\n");
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
}
// Value per share calculation
if ($config['reward_type'] != 'block') {
$pps_value = number_format(round($config['reward'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12);
$pps_value = number_format(round($config['reward'] / (pow(2,32) * $dDifficulty) * pow(2, $config['difficulty']), 12) ,12);
} else {
// Try to find the last block value and use that for future payouts, revert to fixed reward if none found
if ($aLastBlock = $block->getLast()) {
@ -58,7 +61,7 @@ $iLastShareId = $share->getLastInsertedShareId();
// Check for all new shares, we start one higher as our last accounted share to avoid duplicates
$aAccountShares = $share->getSharesForAccounts($iPreviousShareId + 1, $iLastShareId);
verbose("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\tPayout\t\tDonation\tFee\t\tStatus\n");
$log->logInfo("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\tPayout\t\tDonation\tFee");
foreach ($aAccountShares as $aData) {
// Take our valid shares and multiply by per share value
@ -74,65 +77,76 @@ foreach ($aAccountShares as $aData) {
// Calculate donation amount
$aData['donation'] = number_format(round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 8), 8);
verbose($aData['id'] . "\t" .
$log->logInfo($aData['id'] . "\t" .
$aData['username'] . "\t" .
$aData['invalid'] . "\t" .
$aData['valid'] . "\t*\t" .
$pps_value . "\t=\t" .
$aData['payout'] . "\t" .
$aData['donation'] . "\t" .
$aData['fee'] . "\t");
$aData['fee']);
$strStatus = "OK";
// Add new credit transaction
if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit_PPS'))
$strStatus = "Transaction Failed";
$log->logError('Failed to add Credit_PPS transaction in database');
// Add new fee debit for this block
if ($aData['fee'] > 0 && $config['fees'] > 0)
if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee_PPS'))
$strStatus = "Fee Failed";
$log->logError('Failed to add Fee_PPS transaction in database');
// Add new donation debit
if ($aData['donation'] > 0)
if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation_PPS'))
$strStatus = "Donation Failed";
verbose($strStatus . "\n");
$log->logError('Failed to add Donation_PPS transaction in database');
}
// Store our last inserted ID for the next run
$setting->setValue('pps_last_share_id', $iLastShareId);
verbose("\n\n------------------------------------------------------------------------------------\n\n");
// Fetch all unaccounted blocks
$aAllBlocks = $block->getAllUnaccounted('ASC');
if (empty($aAllBlocks)) {
verbose("No new unaccounted blocks found\n");
$log->logDebug("No new unaccounted blocks found");
}
// Go through blocks and archive/delete shares that have been accounted for
foreach ($aAllBlocks as $iIndex => $aBlock) {
// If we are running through more than one block, check for previous share ID
$iLastBlockShare = @$aAllBlocks[$iIndex - 1]['share_id'] ? @$aAllBlocks[$iIndex - 1]['share_id'] : 0;
if (!is_numeric($aBlock['share_id'])) {
$log->logFatal("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue");
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Block " . $aBlock['height'] . " has no share_id associated with it");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
}
// Per account statistics
$aAccountShares = $share->getSharesForAccounts(@$iLastBlockShare, $aBlock['share_id']);
foreach ($aAccountShares as $key => $aData) {
if (!$statistics->updateShareStatistics($aData, $aBlock['id']))
verbose("Failed to update stats for this block on : " . $aData['username'] . "\n");
$log->logError("Failed to update stats for this block on : " . $aData['username']);
}
// Move shares to archive
if ($config['archive_shares'] && $aBlock['share_id'] < $iLastShareId) {
if ($aBlock['share_id'] < $iLastShareId) {
if (!$share->moveArchive($aBlock['share_id'], $aBlock['id'], @$iLastBlockShare))
verbose("Archving failed\n");
$log->logError("Archving failed");
}
// Delete shares
if ($aBlock['share_id'] < $iLastShareId && !$share->deleteAccountedShares($aBlock['share_id'], $iLastBlockShare)) {
verbose("\nERROR : Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . $iLastBlockShare . ", aborting!\n");
$log->logFatal("Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . $iLastBlockShare . ", aborting!");
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . $iLastBlockShare);
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
}
// Mark this block as accounted for
if (!$block->setAccounted($aBlock['id'])) {
verbose("\nERROR : Failed to mark block as accounted! Aborting!\n");
$log->logFatal("Failed to mark block as accounted! Aborting!");
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Failed to mark block " . $aBlock['height'] . " as accounted");
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
}
}
require_once('cron_end.inc.php');
?>

View File

@ -24,18 +24,23 @@ require_once('shared.inc.php');
// Check if we are set as the payout system
if ($config['payout_system'] != 'prop') {
verbose("Please activate this cron in configuration via payout_system = prop\n");
$log->logInfo("Please activate this cron in configuration via payout_system = prop");
exit(0);
}
// Fetch all unaccounted blocks
$aAllBlocks = $block->getAllUnaccounted('ASC');
if (empty($aAllBlocks)) {
verbose("No new unaccounted blocks found\n");
$log->logDebug('No new unaccounted blocks found in database');
$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);
}
$count = 0;
// Table header for account shares
$log->logInfo("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee");
foreach ($aAllBlocks as $iIndex => $aBlock) {
if (!$aBlock['accounted']) {
$iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0;
@ -45,14 +50,13 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
$config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward'];
if (empty($aAccountShares)) {
verbose("\nNo shares found for this block\n\n");
sleep(2);
continue;
$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);
}
// Table header for account shares
verbose("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee\t\tStatus\n");
// Loop through all accounts that have found shares for this round
foreach ($aAccountShares as $key => $aData) {
// Payout based on shares, PPS system
@ -68,45 +72,52 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
$aData['donation'] = number_format(round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 8), 8);
// Verbose output of this users calculations
verbose($aData['id'] . "\t" .
$aData['username'] . "\t" .
$aData['valid'] . "\t" .
$aData['invalid'] . "\t" .
$aData['percentage'] . "\t" .
$aData['payout'] . "\t" .
$aData['donation'] . "\t" .
$aData['fee'] . "\t");
$log->logInfo($aData['id'] . "\t" .
$aData['username'] . "\t" .
$aData['valid'] . "\t" .
$aData['invalid'] . "\t" .
$aData['percentage'] . "\t" .
$aData['payout'] . "\t" .
$aData['donation'] . "\t" .
$aData['fee']);
$strStatus = "OK";
// Update user share statistics
if (!$statistics->updateShareStatistics($aData, $aBlock['id']))
$strStatus = "Stats Failed";
$log->logFatal('Failed to update share statistics for ' . $aData['username']);
// Add new credit transaction
if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit', $aBlock['id']))
$strStatus = "Transaction Failed";
$log->logFatal('Failed to insert new Credit transaction to database for ' . $aData['username']);
// Add new fee debit for this block
if ($aData['fee'] > 0 && $config['fees'] > 0)
if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee', $aBlock['id']))
$strStatus = "Fee Failed";
$log->logFatal('Failed to insert new Fee transaction to database for ' . $aData['username']);
// Add new donation debit
if ($aData['donation'] > 0)
if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation', $aBlock['id']))
$strStatus = "Donation Failed";
verbose("\t$strStatus\n");
$log->logFatal('Failed to insert new Donation transaction to database for ' . $aData['username']);
}
// Move counted shares to archive before this blockhash upstream share
if ($config['archive_shares']) $share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId);
if (!$share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId))
$log->logError('Failed to copy shares to archive');
// Delete all accounted shares
if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) {
verbose("\nERROR : Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting!\n");
$log->logFatal('Failed to delete accounted shares from ' . $iPreviousShareId . ' to ' . $iCurrentUpstreamId . ', aborted');
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
$monitoring->setStatus($cron_name . "_message", "message", "Failed to delete accounted shares from " . $iPreviousShareId . " to " . $iCurrentUpstreamId);
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
exit(1);
}
// Mark this block as accounted for
if (!$block->setAccounted($aBlock['id'])) {
verbose("\nERROR : Failed to mark block as accounted! Aborting!\n");
$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);
}
verbose("------------------------------------------------------------------------\n\n");
}
}
require_once('cron_end.inc.php');
?>

View File

@ -12,14 +12,8 @@ PHP_BIN=$( which php )
# Path to PID file, needs to be writable by user running this
PIDFILE='/tmp/mmcfe-ng-cron.pid'
# Location of our cronjobs, assume current directory
CRONHOME='.'
# List of cruns to execute
CRONS="findblock.php proportional_payout.php pps_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php statistics.php"
# Additional arguments to pass to cronjobs
CRONARGS="-v"
CRONS="findblock.php proportional_payout.php pplns_payout.php pps_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php statistics.php archive_cleanup.php"
# Output additional runtime information
VERBOSE="0"
@ -30,6 +24,29 @@ VERBOSE="0"
# #
################################################################
# Overwrite some settings via command line arguments
while getopts "hvp:" opt; do
case "$opt" in
h|\?)
echo "Usage: $0 [-v] [-p PHP_BINARY]";
exit 0
;;
v) VERBOSE=1 ;;
p) PHP_BIN=$OPTARG ;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
# Find scripts path
if [[ -L $0 ]]; then
CRONHOME=$( dirname $( readlink $0 ) )
else
CRONHOME=$( dirname $0 )
fi
# Change working director to CRONHOME
if ! cd $CRONHOME 2>/dev/null; then
echo "Unable to change to working directory \$CRONHOME: $CRONHOME"
@ -66,8 +83,8 @@ fi
echo $PID > $PIDFILE
for cron in $CRONS; do
[[ $VERBOSE == 1 ]] && echo "Running $cron, see output below for details"
$PHP_BIN $cron $CRONARGS
[[ $VERBOSE == 1 ]] && echo "Running $cron, check logfile for details"
$PHP_BIN $cron
done
# Remove pidfile

View File

@ -22,6 +22,13 @@ limitations under the License.
// We need to find our include files so set this properly
define("BASEPATH", "../public/");
/*****************************************************
* No need to change beyond this point *
*****************************************************/
// Our cron name
$cron_name = basename($_SERVER['PHP_SELF'], '.php');
// Our security check
define("SECURITY", 1);
@ -31,16 +38,15 @@ require_once(BASEPATH . 'include/config/global.inc.php');
// We include all needed files here, even though our templates could load them themself
require_once(INCLUDE_DIR . '/autoloader.inc.php');
// Parse command line
$options = getopt("v");
if (array_key_exists('v', $options)) {
define("VERBOSE", true);
} else {
define("VERBOSE", false);
}
// Load 3rd party logging library for running crons
$log = new KLogger ( 'logs/' . $cron_name . '.txt' , KLogger::INFO );
$log->LogDebug('Starting ' . $cron_name);
// Command line cron functions only
function verbose($msg) {
if (VERBOSE) echo $msg;
}
// Load the start time for later runtime calculations for monitoring
$cron_start[$cron_name] = microtime(true);
// Mark cron as running for monitoring
$log->logDebug('Marking cronjob as running for monitoring');
$monitoring->setStatus($cron_name . '_active', 'yesno', 1);
$monitoring->setStatus($cron_name . '_starttime', 'date', time());
?>

View File

@ -25,44 +25,38 @@ require_once('shared.inc.php');
// Fetch all cachable values but disable fetching from cache
$statistics->setGetCache(false);
// Verbose output
verbose("Running statistical cache updates\n");
// Since fetching from cache is disabled, overwrite our stats
verbose(" getRoundShares ...");
$start = microtime(true);
if (!$statistics->getRoundShares())
verbose(" update failed");
verbose(" " . number_format(microtime(true) - $start, 2) . " seconds\n");
verbose(" getTopContributors shares ...");
$log->logError("getRoundShares update failed");
$log->logInfo("getRoundShares update " . number_format(microtime(true) - $start, 2) . " seconds");
$start = microtime(true);
if (!$statistics->getTopContributors('shares'))
verbose(" update failed");
verbose(" " . number_format(microtime(true) - $start, 2) . " seconds\n");
verbose(" getTopContributors hashes ...");
$log->logError("getTopContributors shares update failed");
$log->logInfo("getTopContributors shares " . number_format(microtime(true) - $start, 2) . " seconds");
$start = microtime(true);
if (!$statistics->getTopContributors('hashes'))
verbose(" update failed");
verbose(" " . number_format(microtime(true) - $start, 2) . " seconds\n");
verbose(" getCurrentHashrate ...");
$log->logError("getTopContributors hashes update failed");
$log->logInfo("getTopContributors hashes " . number_format(microtime(true) - $start, 2) . " seconds");
$start = microtime(true);
if (!$statistics->getCurrentHashrate())
verbose(" update failed");
verbose(" " . number_format(microtime(true) - $start, 2) . " seconds\n");
$log->logError("getCurrentHashrate update failed");
$log->logInfo("getCurrentHashrate " . number_format(microtime(true) - $start, 2) . " seconds");
/*
// Admin specific statistics, we cache the global query due to slowness
verbose(" getAllUserStats ...");
$start = microtime(true);
if (!$statistics->getAllUserStats('%'))
verbose(" update failed");
verbose(" " . number_format(microtime(true) - $start, 2) . " seconds\n");
$log->logError("getAllUserStats update failed");
$log->logInfo("getAllUserStats " . number_format(microtime(true) - $start, 2) . " seconds");
*/
// Per user share statistics based on all shares submitted
verbose(" getAllUserShares ...");
$start = microtime(true);
$aUserShares = $statistics->getAllUserShares();
verbose(" " . number_format(microtime(true) - $start, 2) . " seconds");
$log->logInfo("getAllUserShares " . number_format(microtime(true) - $start, 2) . " seconds");
foreach ($aUserShares as $aShares) {
$memcache->setCache('getUserShares'. $aShares['id'], $aShares);
}
verbose("\n");
require_once('cron_end.inc.php');
?>

View File

@ -25,14 +25,13 @@ require_once('shared.inc.php');
// Include additional file not set in autoloader
require_once(CLASS_DIR . '/tools.class.php');
verbose("Running scheduled updates\n");
verbose(" Price API Call ... ");
if ($price = $tools->getPrice()) {
verbose("found $price as price\n");
$log->logInfo("Price update: found $price as price");
if (!$setting->setValue('price', $price))
verbose("unable to update value in settings table\n");
$log->logError("unable to update value in settings table");
} else {
verbose("failed to fetch API data: " . $tools->getError() . "\n");
$log->logFatal("failed to fetch API data: " . $tools->getError());
}
require_once('cron_end.inc.php');
?>

View File

@ -17,18 +17,25 @@ require_once(CLASS_DIR . '/debug.class.php');
require_once(CLASS_DIR . '/bitcoin.class.php');
require_once(CLASS_DIR . '/statscache.class.php');
require_once(CLASS_DIR . '/bitcoinwrapper.class.php');
require_once(INCLUDE_DIR . '/lib/KLogger.php');
require_once(INCLUDE_DIR . '/database.inc.php');
require_once(INCLUDE_DIR . '/smarty.inc.php');
// Load classes that need the above as dependencies
require_once(CLASS_DIR . '/base.class.php');
require_once(CLASS_DIR . '/api.class.php');
require_once(CLASS_DIR . '/mail.class.php');
require_once(CLASS_DIR . '/tokentype.class.php');
require_once(CLASS_DIR . '/token.class.php');
require_once(CLASS_DIR . '/block.class.php');
require_once(CLASS_DIR . '/setting.class.php');
require_once(CLASS_DIR . '/monitoring.class.php');
require_once(CLASS_DIR . '/user.class.php');
require_once(CLASS_DIR . '/invitation.class.php');
require_once(CLASS_DIR . '/share.class.php');
require_once(CLASS_DIR . '/worker.class.php');
require_once(CLASS_DIR . '/statistics.class.php');
require_once(CLASS_DIR . '/transaction.class.php');
require_once(CLASS_DIR . '/mail.class.php');
require_once(CLASS_DIR . '/notification.class.php');
require_once(CLASS_DIR . '/news.class.php');
require_once(INCLUDE_DIR . '/lib/Michelf/Markdown.php');
require_once(INCLUDE_DIR . '/lib/scrypt.php');

View File

@ -0,0 +1,23 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
/**
* Helper class for our API
**/
class Api extends Base {
function isActive($error=true) {
if (!$this->config['website']['api']['disabled']) {
return true;
} else {
if ($error == true) {
header('HTTP/1.1 501 Not implemented');
die('501 Not implemented');
}
}
}
}
$api = new Api();
$api->setConfig($config);

View File

@ -15,6 +15,9 @@ class Base {
public function setMysql($mysqli) {
$this->mysqli = $mysqli;
}
public function setMail($mail) {
$this->mail = $mail;
}
public function setSmarty($smarty) {
$this->smarty = $smarty;
}
@ -24,6 +27,12 @@ class Base {
public function setConfig($config) {
$this->config = $config;
}
public function setToken($token) {
$this->token = $token;
}
public function setTokenType($tokentype) {
$this->tokentype = $tokentype;
}
public function setErrorMessage($msg) {
$this->sError = $msg;
}

View File

@ -43,6 +43,18 @@ class Block {
return false;
}
/**
* Get a specific block, by block height
* @param height int Block Height
* @return data array Block information from DB
**/
public function getBlock($height) {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE height = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $height) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc();
return false;
}
/**
* Get our last, highest share ID inserted for a block
* @param none
@ -55,6 +67,18 @@ class Block {
return false;
}
/**
* Fetch all blocks without a share ID
* @param order string Sort order, default ASC
* @return data array Array with database fields as keys
**/
public function getAllUnsetShareId($order='ASC') {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE ISNULL(share_id) ORDER BY height $order");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
return false;
}
/**
* Fetch all unaccounted blocks
* @param order string Sort order, default ASC
@ -62,12 +86,32 @@ class Block {
**/
public function getAllUnaccounted($order='ASC') {
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE accounted = 0 ORDER BY height $order");
if ($this->checkStmt($stmt)) {
$stmt->execute();
$result = $stmt->get_result();
$stmt->close();
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
}
return false;
}
/**
* Get total amount of blocks in our table
* @param noone
* @return data int Count of rows
**/
public function getBlockCount() {
$stmt = $this->mysqli->prepare("SELECT COUNT(id) AS blocks FROM $this->table");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return (int)$result->fetch_object()->blocks;
return false;
}
/**
* Fetch our average share count for the past N blocks
* @param limit int Maximum blocks to check
* @return data float Float value of average shares
**/
public function getAvgBlockShares($limit=1) {
$stmt = $this->mysqli->prepare("SELECT AVG(x.shares) AS average FROM (SELECT shares FROM $this->table ORDER BY height DESC LIMIT ?) AS x");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $limit) && $stmt->execute() && $result = $stmt->get_result())
return (float)$result->fetch_object()->average;
return false;
}
@ -77,14 +121,9 @@ class Block {
* @return data array Array with database fields as keys
**/
public function getAllUnconfirmed($confirmations='120') {
$stmt = $this->mysqli->prepare("SELECT id, blockhash, confirmations FROM $this->table WHERE confirmations < ? AND confirmations > -1");
if ($this->checkStmt($stmt)) {
$stmt->bind_param("i", $confirmations);
$stmt->execute();
$result = $stmt->get_result();
$stmt->close();
$stmt = $this->mysqli->prepare("SELECT id, height, blockhash, confirmations FROM $this->table WHERE confirmations < ? AND confirmations > -1");
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $confirmations) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
}
return false;
}

View File

@ -0,0 +1,146 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
class Invitation extends Base {
var $table = 'invitations';
/**
* Fetch invitations for one account
* @param account_id int Account ID
* @return mixed Array on success, bool on failure
**/
public function getInvitations($account_id) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE account_id = ?");
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
$this->setErrorMessage('Unable to fetch invitiations send from your account');
$this->debug->append('Failed to fetch invitations from database: ' . $this->mysqli->errro);
return false;
}
/**
* Count invitations sent by an account_id
* @param account_id integer Account ID
* @return mixes Integer on success, boolean on failure
**/
public function getCountInvitations($account_id) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("SELECT count(id) AS total FROM $this->table WHERE account_id = ?");
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;
}
/**
* Get a specific invitation by email address
* Used to ensure no invitation was already sent
* @param strEmail string Email address to check for
* @return bool boolean true of ralse
**/
public function getByEmail($strEmail) {
$this->debug->append("STA " . __METHOD__, 4);
return $this->getSingle($strEmail, 'id', 'email', 's');
}
/**
* Get a specific token by token ID
* Used to match an invitation against a token
* @param token_id integer Token ID stored in invitation
* @return data mixed Invitation ID on success, false on error
**/
public function getByTokenId($token_id) {
$this->debug->append("STA " . __METHOD__, 4);
return $this->getSingle($token_id, 'id', 'token_id');
}
/**
* Set an invitation as activated by the invitee
* @param token_id integer Token to activate
* @return bool boolean true or false
**/
public function setActivated($token_id) {
if (!$iInvitationId = $this->getByTokenId($token_id)) {
$this->setErrorMessage('Unable to convert token ID to invitation ID');
return false;
}
$field = array('name' => 'is_activated', 'type' => 'i', 'value' => 1);
return $this->updateSingle($iInvitationId, $field);
}
/**
* Insert a new invitation to the database
* @param account_id integer Account ID to bind the invitation to
* @param email string Email address the invite was sent to
* @param token_id integer Token ID used during invitation
* @return bool boolean True of false
**/
public function createInvitation($account_id, $email, $token_id) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("INSERT INTO $this->table ( account_id, email, token_id ) VALUES ( ?, ?, ?)");
if ($stmt && $stmt->bind_param('isi', $account_id, $email, $token_id) && $stmt->execute())
return true;
return false;
}
/**
* Send an invitation out to a user
* Uses the mail class to send mails
* @param account_id integer Sending account ID
* @param aData array Data array including mail information
* @return bool boolean True or false
**/
public function sendInvitation($account_id, $aData) {
$this->debug->append("STA " . __METHOD__, 4);
// Check data input
if (empty($aData['email']) || !filter_var($aData['email'], FILTER_VALIDATE_EMAIL)) {
$this->setErrorMessage( 'Invalid e-mail address' );
return false;
}
if (preg_match('/[^a-z_\.\!\?\-0-9 ]/i', $aData['message'])) {
$this->setErrorMessage('Message may only contain alphanumeric characters');
return false;
}
// Ensure this invitation does not exist yet nor do we have an account with that email
if ($this->user->getEmail($aData['email'])) {
$this->setErrorMessage('This email is already registered as an account');
return false;
}
if ($this->getByEmail($aData['email'])) {
$this->setErrorMessage('A pending invitation for this address already exists');
return false;
}
if (!$aData['token'] = $this->token->createToken('invitation', $account_id)) {
$this->setErrorMessage('Unable to generate invitation token: ' . $this->token->getError());
return false;
}
$aData['username'] = $this->user->getUserName($account_id);
$aData['subject'] = 'Pending Invitation';
if ($this->mail->sendMail('invitations/body', $aData)) {
$aToken = $this->token->getToken($aData['token']);
if (!$this->createInvitation($account_id, $aData['email'], $aToken['id'])) {
$this->setErrorMessage('Unable to create invitation record');
return false;
}
return true;
} else {
$this->setErrorMessage('Unable to send email to recipient');
}
$this->setErrorMessage('Unable to send invitation');
return false;
}
}
// Instantiate class
$invitation = new invitation();
$invitation->setDebug($debug);
$invitation->setMysql($mysqli);
$invitation->setMail($mail);
$invitation->setUser($user);
$invitation->setToken($oToken);
$invitation->setConfig($config);
?>

View File

@ -0,0 +1,49 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
class Monitoring {
public function __construct($debug, $mysqli) {
$this->debug = $debug;
$this->mysqli = $mysqli;
$this->table = 'monitoring';
}
/**
* Fetch a value from our table
* @param name string Setting name
* @return value string Value
**/
public function getStatus($name) {
$query = $this->mysqli->prepare("SELECT * FROM $this->table WHERE name = ? LIMIT 1");
if ($query && $query->bind_param('s', $name) && $query->execute() && $result = $query->get_result()) {
return $result->fetch_assoc();
} else {
$this->debug->append("Failed to fetch variable $name from $this->table");
return false;
}
return $value;
}
/**
* Insert or update a setting
* @param name string Name of the variable
* @param value string Variable value
* @return bool
**/
public function setStatus($name, $type, $value) {
$stmt = $this->mysqli->prepare("
INSERT INTO $this->table (name, type, value)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE value = ?
");
if ($stmt && $stmt->bind_param('ssss', $name, $type, $value, $value) && $stmt->execute())
return true;
$this->debug->append("Failed to set $name to $value");
return false;
}
}
$monitoring = new Monitoring($debug, $mysqli);

View File

@ -13,9 +13,12 @@ class Share {
// This defines each share
public $rem_host, $username, $our_result, $upstream_result, $reason, $solution, $time;
public function __construct($debug, $mysqli, $salt) {
public function __construct($debug, $mysqli, $user, $block, $config) {
$this->debug = $debug;
$this->mysqli = $mysqli;
$this->user = $user;
$this->config = $config;
$this->block = $block;
$this->debug->append("Instantiated Share class", 2);
}
@ -70,7 +73,7 @@ class Share {
count(id) as total
FROM $this->table
WHERE our_result = 'Y'
AND id BETWEEN ? AND ?
AND id > ? AND id <= ?
");
if ($this->checkStmt($stmt)) {
$stmt->bind_param('ii', $previous_upstream, $current_upstream);
@ -86,47 +89,94 @@ class Share {
* Fetch all shares grouped by accounts to count share per account
* @param previous_upstream int Previous found share accepted by upstream to limit results
* @param current_upstream int Current upstream accepted share
* @param limit int Limit to this amount of shares for PPLNS
* @return data array username, valid and invalid shares from account
**/
public function getSharesForAccounts($previous_upstream=0, $current_upstream) {
$stmt = $this->mysqli->prepare("SELECT
a.id,
validT.account AS username,
sum(validT.valid) as valid,
IFNULL(sum(invalidT.invalid),0) as invalid
FROM
(
SELECT DISTINCT
SUBSTRING_INDEX( `username` , '.', 1 ) as account,
COUNT(id) AS valid
FROM $this->table
WHERE id BETWEEN ? AND ?
AND our_result = 'Y'
GROUP BY account
) validT
LEFT JOIN
(
SELECT DISTINCT
SUBSTRING_INDEX( `username` , '.', 1 ) as account,
COUNT(id) AS invalid
FROM $this->table
WHERE id BETWEEN ? AND ?
AND our_result = 'N'
GROUP BY account
) invalidT
ON validT.account = invalidT.account
INNER JOIN accounts a ON a.username = validT.account
GROUP BY a.username DESC");
if ($this->checkStmt($stmt)) {
$stmt->bind_param('iiii', $previous_upstream, $current_upstream, $previous_upstream, $current_upstream);
$stmt->execute();
$result = $stmt->get_result();
$stmt->close();
$stmt = $this->mysqli->prepare("
SELECT
a.id,
SUBSTRING_INDEX( s.username , '.', 1 ) as username,
IFNULL(SUM(IF(our_result='Y', 1, 0)), 0) AS valid,
IFNULL(SUM(IF(our_result='N', 1, 0)), 0) AS invalid
FROM $this->table AS s
LEFT JOIN " . $this->user->getTableName() . " AS a
ON a.username = SUBSTRING_INDEX( s.username , '.', 1 )
WHERE s.id > ? AND s.id <= ?
GROUP BY username DESC
");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
return false;
}
/**
* Fetch the highest available share ID from archive
**/
function getMaxArchiveShareId() {
$stmt = $this->mysqli->prepare("
SELECT MAX(share_id) AS share_id FROM $this->tableArchive
");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->share_id;
return false;
}
/**
* We need a certain amount of valid archived shares
* param left int Left/lowest share ID
* param right int Right/highest share ID
* return array data Returns an array with usernames as keys for easy access
**/
function getArchiveShares($iCount) {
$iMinId = $this->getMaxArchiveShareId() - $iCount;
$iMaxId = $this->getMaxArchiveShareId();
$stmt = $this->mysqli->prepare("
SELECT
a.id,
SUBSTRING_INDEX( s.username , '.', 1 ) as account,
IFNULL(SUM(IF(our_result='Y', 1, 0)), 0) AS valid,
IFNULL(SUM(IF(our_result='N', 1, 0)), 0) AS invalid
FROM $this->tableArchive AS s
LEFT JOIN " . $this->user->getTableName() . " AS a
ON a.username = SUBSTRING_INDEX( s.username , '.', 1 )
WHERE s.share_id > ? AND s.share_id <= ?
GROUP BY account DESC");
if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $iMinId, $iMaxId) && $stmt->execute() && $result = $stmt->get_result()) {
$aData = NULL;
while ($row = $result->fetch_assoc()) {
$aData[$row['account']] = $row;
}
if (is_array($aData)) return $aData;
}
return false;
}
/**
* We keep shares only up to a certain point
* This can be configured by the user.
* @return return bool true or false
**/
public function purgeArchive() {
if ($this->config['payout_system'] == 'pplns') {
// Fetch our last block so we can go back configured rounds
$aLastBlock = $this->block->getLast();
// Fetch the block we need to find the share_id
$aBlock = $this->block->getBlock($aLastBlock['height'] - $this->config['archive']['maxrounds']);
// Now that we know our block, remove those shares
$stmt = $this->mysqli->prepare("DELETE FROM $this->tableArchive WHERE block_id < ? AND time < DATE_SUB(now(), INTERVAL ? MINUTE)");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $aBlock['id'], $this->config['archive']['maxage']) && $stmt->execute())
return true;
} else {
// We are not running pplns, so we just need to keep shares of the past <interval> minutes
$stmt = $this->mysqli->prepare("DELETE FROM $this->tableArchive WHERE time < DATE_SUB(now(), INTERVAL ? MINUTE)");
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $this->config['archive']['maxage']) && $stmt->execute())
return true;
}
// Catchall
return false;
}
/**
* Move accounted shares to archive table, this step is optional
* @param previous_upstream int Previous found share accepted by upstream to limit results
@ -135,10 +185,11 @@ class Share {
* @return bool
**/
public function moveArchive($current_upstream, $block_id, $previous_upstream=0) {
$archive_stmt = $this->mysqli->prepare("INSERT INTO $this->tableArchive (share_id, username, our_result, upstream_result, block_id, time)
SELECT id, username, our_result, upstream_result, ?, time
FROM $this->table
WHERE id BETWEEN ? AND ?");
$archive_stmt = $this->mysqli->prepare("
INSERT INTO $this->tableArchive (share_id, username, our_result, upstream_result, block_id, time)
SELECT id, username, our_result, upstream_result, ?, time
FROM $this->table
WHERE id > ? AND id <= ?");
if ($this->checkStmt($archive_stmt) && $archive_stmt->bind_param('iii', $block_id, $previous_upstream, $current_upstream) && $archive_stmt->execute()) {
$archive_stmt->close();
return true;
@ -148,7 +199,7 @@ class Share {
}
public function deleteAccountedShares($current_upstream, $previous_upstream=0) {
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id BETWEEN ? AND ?");
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id > ? AND id <= ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute())
return true;
// Catchall
@ -177,7 +228,48 @@ class Share {
* @param last int Skips all shares up to last to find new share
* @return bool
**/
public function setUpstream($last=0, $time=0) {
public function setUpstream($aBlock, $last=0) {
// Many use stratum, so we create our stratum check first
$version = pack("I*", sprintf('%08d', $aBlock['version']));
$previousblockhash = pack("H*", swapEndian($aBlock['previousblockhash']));
$merkleroot = pack("H*", swapEndian($aBlock['merkleroot']) );
$time = pack("I*", $aBlock['time']);
$bits = pack("H*", swapEndian($aBlock['bits']));
$nonce = pack("I*", $aBlock['nonce']);
$header_bin = $version . $previousblockhash . $merkleroot . $time . $bits . $nonce;
$header_hex = implode(unpack("H*", $header_bin));
// Stratum supported blockhash solution entry
$stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('s', $aBlock['hash']) && $stmt->execute() && $result = $stmt->get_result()) {
$this->oUpstream = $result->fetch_object();
$this->share_type = 'startum_blockhash';
if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id))
return true;
}
// Stratum scrypt hash check
$scrypt_hash = swapEndian(bin2hex(Scrypt::calc($header_bin, $header_bin, 1024, 1, 1, 32)));
$stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('s', $scrypt_hash) && $stmt->execute() && $result = $stmt->get_result()) {
$this->oUpstream = $result->fetch_object();
$this->share_type = 'startum_solution';
if (!empty($this->oUpstream->account) && 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");
if ($this->checkStmt($stmt) && $stmt->bind_param('s', $ppheader) && $stmt->execute() && $result = $stmt->get_result()) {
$this->oUpstream = $result->fetch_object();
$this->share_type = 'pp_solution';
if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id))
return true;
}
// Still no match, try upstream result with timerange
$stmt = $this->mysqli->prepare("
SELECT
SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id
@ -185,9 +277,27 @@ class Share {
WHERE upstream_result = 'Y'
AND id > ?
AND UNIX_TIMESTAMP(time) >= ?
AND UNIX_TIMESTAMP(time) <= ( ? + 60 )
ORDER BY id ASC LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $last, $time) && $stmt->execute() && $result = $stmt->get_result()) {
if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $last, $aBlock['time'], $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))
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
FROM $this->table
WHERE our_result = 'Y'
AND id > ?
AND UNIX_TIMESTAMP(time) >= ?
ORDER BY id ASC LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $last, $aBlock['time']) && $stmt->execute() && $result = $stmt->get_result()) {
$this->oUpstream = $result->fetch_object();
$this->share_type = 'any_share';
if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id))
return true;
}
@ -208,4 +318,4 @@ class Share {
}
}
$share = new Share($debug, $mysqli, SALT);
$share = new Share($debug, $mysqli, $user, $block, $config);

View File

@ -61,7 +61,10 @@ class Statistics {
$this->debug->append("STA " . __METHOD__, 4);
if ($data = $this->memcache->get(__FUNCTION__ . $limit)) return $data;
$stmt = $this->mysqli->prepare("
SELECT b.*, a.username as finder
SELECT
b.*,
a.username AS finder,
a.is_anonymous AS is_anonymous
FROM " . $this->block->getTableName() . " AS b
LEFT JOIN " . $this->user->getTableName() . " AS a
ON b.account_id = a.id
@ -327,9 +330,13 @@ class Statistics {
case 'shares':
$stmt = $this->mysqli->prepare("
SELECT
COUNT(id) AS shares,
SUBSTRING_INDEX( username, '.', 1 ) AS account
FROM " . $this->share->getTableName() . "
a.donate_percent AS donate_percent,
a.is_anonymous AS is_anonymous,
COUNT(s.id) AS shares,
SUBSTRING_INDEX( s.username, '.', 1 ) AS account
FROM " . $this->share->getTableName() . " AS s
LEFT JOIN " . $this->user->getTableName() . " AS a
ON SUBSTRING_INDEX( s.username, '.', 1 ) = a.username
WHERE our_result = 'Y'
GROUP BY account
ORDER BY shares DESC
@ -343,14 +350,18 @@ class Statistics {
case 'hashes':
$stmt = $this->mysqli->prepare("
SELECT
IFNULL(ROUND(COUNT(id) * POW(2," . $this->config['difficulty'] . ")/600/1000, 2), 0) AS hashrate,
SUBSTRING_INDEX( username, '.', 1 ) AS account
a.donate_percent AS donate_percent,
a.is_anonymous AS is_anonymous,
IFNULL(ROUND(COUNT(t1.id) * POW(2," . $this->config['difficulty'] . ")/600/1000, 2), 0) AS hashrate,
SUBSTRING_INDEX( t1.username, '.', 1 ) AS account
FROM
(
SELECT id, username FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y'
UNION
SELECT id, username FROM " . $this->share->getArchiveTableName() ." WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y'
) AS t1
LEFT JOIN " . $this->user->getTableName() . " AS a
ON SUBSTRING_INDEX( t1.username, '.', 1 ) = a.username
GROUP BY account
ORDER BY hashrate DESC LIMIT ?");
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result())
@ -374,12 +385,22 @@ class Statistics {
ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000) AS hashrate,
HOUR(s.time) AS hour
FROM " . $this->share->getTableName() . " AS s, accounts AS a
WHERE time < NOW() - INTERVAL 1 HOUR
AND time > NOW() - INTERVAL 25 HOUR
AND a.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND a.id = ?
GROUP BY HOUR(time)
UNION ALL
SELECT
ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000) AS hashrate,
HOUR(s.time) AS hour
FROM " . $this->share->getArchiveTableName() . " AS s, accounts AS a
WHERE time < NOW() - INTERVAL 1 HOUR
AND time > NOW() - INTERVAL 25 HOUR
AND a.username = SUBSTRING_INDEX( s.username, '.', 1 )
AND a.id = ?
GROUP BY HOUR(time)");
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result()) {
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $account_id, $account_id) && $stmt->execute() && $result = $stmt->get_result()) {
$aData = array();
while ($row = $result->fetch_assoc()) {
$aData[$row['hour']] = $row['hashrate'];
@ -404,6 +425,14 @@ class Statistics {
IFNULL(ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000), 0) AS hashrate,
HOUR(s.time) AS hour
FROM " . $this->share->getTableName() . " AS s
WHERE time < NOW() - INTERVAL 1 HOUR
AND time > NOW() - INTERVAL 25 HOUR
GROUP BY HOUR(time)
UNION ALL
SELECT
IFNULL(ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ") / 3600 / 1000), 0) AS hashrate,
HOUR(s.time) AS hour
FROM " . $this->share->getArchiveTableName() . " AS s
WHERE time < NOW() - INTERVAL 1 HOUR
AND time > NOW() - INTERVAL 25 HOUR
GROUP BY HOUR(time)");

View File

@ -9,12 +9,17 @@ if (!defined('SECURITY'))
* Can be enabled or disabled through site configuration
* Also sets a default time if no time is passed to it to enforce caching
**/
class StatsCache extends Memcached {
class StatsCache {
private $cache;
public function __construct($config, $debug) {
$this->config = $config;
$this->debug = $debug;
if (! $config['memcache']['enabled'] ) $this->debug->append("Not storing any values in memcache");
return parent::__construct();
if (! $config['memcache']['enabled'] ) {
$this->debug->append("Not storing any values in memcache");
} else {
$this->cache = new Memcached();
}
}
/**
@ -26,7 +31,7 @@ class StatsCache extends Memcached {
if (empty($expiration))
$expiration = $this->config['memcache']['expiration'] + rand( -$this->config['memcache']['splay'], $this->config['memcache']['splay']);
$this->debug->append("Storing " . $this->config['memcache']['keyprefix'] . "$key with expiration $expiration", 3);
return parent::set($this->config['memcache']['keyprefix'] . $key, $value, $expiration);
return $this->cache->set($this->config['memcache']['keyprefix'] . $key, $value, $expiration);
}
/**
@ -36,7 +41,7 @@ class StatsCache extends Memcached {
public function get($key, $cache_cb = NULL, &$cas_token = NULL) {
if (! $this->config['memcache']['enabled']) return false;
$this->debug->append("Trying to fetch key " . $this->config['memcache']['keyprefix'] . "$key from cache", 3);
if ($data = parent::get($this->config['memcache']['keyprefix'].$key)) {
if ($data = $this->cache->get($this->config['memcache']['keyprefix'].$key)) {
$this->debug->append("Found key in cache", 3);
return $data;
} else {
@ -55,7 +60,15 @@ class StatsCache extends Memcached {
if ($this->config['memcache']['enabled']) $this->set($key, $data, $expiration);
return $data;
}
/**
* This method is invoked if the called method was not realised in this class
**/
public function __call($name, $arguments) {
if (! $this->config['memcache']['enabled']) return false;
//Invoke method $name of $this->cache class with array of $arguments
return call_user_func_array(array($this->cache, $name), $arguments);
}
}
$memcache = new StatsCache($config, $debug);

View File

@ -0,0 +1,60 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
class Token Extends Base {
var $table = 'tokens';
/**
* Fetch a token from our table
* @param name string Setting name
* @return value string Value
**/
public function getToken($strToken) {
$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;
}
/**
* Insert a new token
* @param name string Name of the variable
* @param value string Variable value
* @return mixed Token string on success, false on failure
**/
public function createToken($strType, $account_id=NULL) {
$strToken = hash('sha256', $account_id.$strType.microtime());
if (!$iToken_id = $this->tokentype->getTypeId($strType)) {
$this->setErrorMessage('Invalid token type: ' . $strType);
return false;
}
$stmt = $this->mysqli->prepare("
INSERT INTO $this->table (token, type, account_id)
VALUES (?, ?, ?)
");
if ($stmt && $stmt->bind_param('sii', $strToken, $iToken_id, $account_id) && $stmt->execute())
return $strToken;
$this->setErrorMessage('Unable to create new token');
$this->debug->append('Failed to create new token in database: ' . $this->mysqli->error);
return false;
}
/**
* Delete a used token
* @param token string Token name
* @return bool
**/
public function deleteToken($token) {
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE token = ? LIMIT 1");
if ($stmt && $stmt->bind_param('s', $token) && $stmt->execute())
return true;
return false;
}
}
$oToken = new Token();
$oToken->setDebug($debug);
$oToken->setMysql($mysqli);
$oToken->setTokenType($tokentype);

View File

@ -0,0 +1,21 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
class Token_Type Extends Base {
var $table = 'token_types';
/**
* Return ID for specific token
* @param strName string Token Name
* @return mixed ID on success, false on failure
**/
public function getTypeId($strName) {
return $this->getSingle($strName, 'id', 'name', 's');
}
}
$tokentype = new Token_Type();
$tokentype->setDebug($debug);
$tokentype->setMysql($mysqli);

View File

@ -48,35 +48,6 @@ class Transaction {
return false;
}
/**
* Sometimes transactions become orphans when a block associated to them is orphaned
* Updates the transaction types to Orphan_<type>
* @param block_id int Orphaned block ID
* @return bool
**/
public function setOrphan($block_id) {
$this->debug->append("STA " . __METHOD__, 4);
$aOrphans = array(
'Credit' => 'Orphan_Credit',
'Fee' => 'Orphan_Fee',
'Donation' => 'Orphan_Donation',
'Bonus' => 'Orphan_Bonus'
);
foreach ($aOrphans as $from => $to) {
$stmt = $this->mysqli->prepare("
UPDATE $this->table
SET type = '$to'
WHERE type = '$from'
AND block_id = ?
");
if (!($this->checkStmt($stmt) && $stmt->bind_param('i', $block_id) && $stmt->execute())) {
$this->debug->append("Failed to set orphan $from => $to transactions for $block_id");
return false;
}
}
return true;
}
/**
* Get all transactions from start for account_id
* @param account_id int Account ID
@ -156,6 +127,7 @@ class Transaction {
SELECT
SUM(t.amount) AS donation,
a.username AS username,
a.is_anonymous AS is_anonymous,
a.donate_percent AS donate_percent
FROM $this->table AS t
LEFT JOIN " . $this->user->getTableName() . " AS a
@ -227,7 +199,8 @@ class Transaction {
$stmt = $this->mysqli->prepare("
SELECT
ROUND(IFNULL(t1.credit, 0) - IFNULL(t2.debit, 0) - IFNULL(t3.other, 0), 8) AS confirmed,
ROUND(IFNULL(t4.credit, 0) - IFNULL(t5.other, 0), 8) AS unconfirmed
ROUND(IFNULL(t4.credit, 0) - IFNULL(t5.other, 0), 8) AS unconfirmed,
ROUND(IFNULL(t6.credit, 0) - IFNULL(t7.other, 0), 8) AS orphaned
FROM
(
SELECT sum(t.amount) AS credit
@ -262,7 +235,7 @@ class Transaction {
FROM $this->table AS t
LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
WHERE
t.type IN ('Credit','Bonus') AND b.confirmations < ?
t.type IN ('Credit','Bonus') AND b.confirmations < ? AND b.confirmations >= 0
AND t.account_id = ?
) AS t4,
(
@ -271,13 +244,31 @@ class Transaction {
LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
WHERE
(
t.type IN ('Donation','Fee') AND b.confirmations < ?
t.type IN ('Donation','Fee') AND b.confirmations < ? AND b.confirmations >= 0
)
AND t.account_id = ?
) AS t5
) AS t5,
(
SELECT sum(t.amount) AS credit
FROM $this->table AS t
LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
WHERE
t.type IN ('Credit','Bonus') AND b.confirmations = -1
AND t.account_id = ?
) AS t6,
(
SELECT sum(t.amount) AS other
FROM $this->table AS t
LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
WHERE
(
t.type IN ('Donation','Fee') AND b.confirmations = -1
)
AND t.account_id = ?
) AS t7
");
if ($this->checkStmt($stmt)) {
$stmt->bind_param("iiiiiiiii", $this->config['confirmations'], $account_id, $account_id, $this->config['confirmations'], $account_id, $this->config['confirmations'], $account_id, $this->config['confirmations'], $account_id);
$stmt->bind_param("iiiiiiiiiii", $this->config['confirmations'], $account_id, $account_id, $this->config['confirmations'], $account_id, $this->config['confirmations'], $account_id, $this->config['confirmations'], $account_id, $account_id, $account_id);
if (!$stmt->execute()) {
$this->debug->append("Unable to execute statement: " . $stmt->error);
$this->setErrorMessage("Fetching balance failed");

View File

@ -9,7 +9,6 @@ class User {
private $userID = false;
private $table = 'accounts';
private $user = array();
private $tableAccountBalance = 'accountBalance';
public function __construct($debug, $mysqli, $salt, $config) {
$this->debug = $debug;
@ -20,6 +19,12 @@ class User {
}
// get and set methods
public function setMail($mail) {
$this->mail = $mail;
}
public function setToken($token) {
$this->token= $token;
}
private function setErrorMessage($msg) {
$this->sError = $msg;
}
@ -44,18 +49,15 @@ class User {
public function getUserLocked($id) {
return $this->getSingle($id, 'is_locked', 'id');
}
public function getUserToken($id) {
return $this->getSingle($id, 'token', 'id');
}
public function getUserIp($id) {
return $this->getSingle($id, 'loggedIp', 'id');
}
public function getEmail($email) {
return $this->getSingle($email, 'email', 'email', 's');
}
public function getUserFailed($id) {
return $this->getSingle($id, 'failed_logins', 'id');
}
public function getIdFromToken($token) {
return $this->getSingle($token, 'id', 'token', 's');
}
public function isLocked($id) {
return $this->getUserLocked($id);
}
@ -70,10 +72,6 @@ class User {
$field = array('name' => 'is_admin', 'type' => 'i', 'value' => !$this->isAdmin($id));
return $this->updateSingle($id, $field);
}
public function setUserToken($id) {
$field = array('name' => 'token', 'type' => 's', 'value' => setHash($id.time()));
return $this->updateSingle($id, $field);
}
public function setUserFailed($id, $value) {
$field = array( 'name' => 'failed_logins', 'type' => 'i', 'value' => $value);
return $this->updateSingle($id, $field);
@ -280,7 +278,7 @@ class User {
* @param donat float donation % of income
* @return bool
**/
public function updateAccount($userID, $address, $threshold, $donate, $email) {
public function updateAccount($userID, $address, $threshold, $donate, $email, $is_anonymous) {
$this->debug->append("STA " . __METHOD__, 4);
$bUser = false;
@ -314,8 +312,8 @@ class User {
$donate = min(100, max(0, floatval($donate)));
// We passed all validation checks so update the account
$stmt = $this->mysqli->prepare("UPDATE $this->table SET coin_address = ?, ap_threshold = ?, donate_percent = ?, email = ? WHERE id = ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('sddsi', $address, $threshold, $donate, $email, $userID) && $stmt->execute())
$stmt = $this->mysqli->prepare("UPDATE $this->table SET coin_address = ?, ap_threshold = ?, donate_percent = ?, email = ?, is_anonymous = ? WHERE id = ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('sddsii', $address, $threshold, $donate, $email, $is_anonymous, $userID) && $stmt->execute())
return true;
// Catchall
$this->setErrorMessage('Failed to update your account');
@ -384,7 +382,16 @@ class User {
**/
public function logoutUser($redirect="index.php") {
$this->debug->append("STA " . __METHOD__, 4);
// Unset all of the session variables
$_SESSION = array();
// As we're killing the sesison, also kill the cookie!
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000, $params["path"], $params["domain"], $params["secure"], $params["httponly"]);
}
// Destroy the session.
session_destroy();
// Enforce generation of a new Session ID and delete the old
session_regenerate_id(true);
// Enforce a page reload
header("Location: $redirect");
@ -409,7 +416,7 @@ class User {
$this->debug->append("Fetching user information for user id: $userID");
$stmt = $this->mysqli->prepare("
SELECT
id, username, pin, api_key, is_admin, email,
id, username, pin, api_key, is_admin, is_anonymous, email,
IFNULL(donate_percent, '0') as donate_percent, coin_address, ap_threshold
FROM $this->table
WHERE id = ? LIMIT 0,1");
@ -437,8 +444,20 @@ class User {
* @param email2 string Email confirmation
* @return bool
**/
public function register($username, $password1, $password2, $pin, $email1='', $email2='') {
public function register($username, $password1, $password2, $pin, $email1='', $email2='', $strToken='') {
$this->debug->append("STA " . __METHOD__, 4);
if (strlen($username > 40)) {
$this->setErrorMessage('Username exceeding character limit');
return false;
}
if (preg_match('/[^a-z_\-0-9]/i', $username)) {
$this->setErrorMessage('Username may only contain alphanumeric characters');
return false;
}
if ($this->getEmail($email1)) {
$this->setErrorMessage( 'This e-mail address is already taken' );
return false;
}
if (strlen($password1) < 8) {
$this->setErrorMessage( 'Password is too short, minimum of 8 characters required' );
return false;
@ -459,15 +478,36 @@ class User {
$this->setErrorMessage( 'Invalid PIN' );
return false;
}
if (isset($strToken) && !empty($strToken)) {
$aToken = $this->token->getToken($strToken);
// Circle dependency, so we create our own object here
$invitation = new Invitation();
$invitation->setMysql($this->mysqli);
$invitation->setDebug($this->debug);
$invitation->setUser($this);
$invitation->setConfig($this->config);
if (!$invitation->setActivated($aToken['id'])) {
$this->setErrorMessage('Unable to activate your invitation');
return false;
}
if (!$this->token->deleteToken($strToken)) {
$this->setErrorMessage('Unable to remove used token');
return false;
}
}
if ($this->mysqli->query("SELECT id FROM $this->table LIMIT 1")->num_rows > 0) {
$this->config['accounts']['confirm_email']['enabled'] ? $is_locked = 1 : $is_locked = 0;
$is_admin = 0;
$stmt = $this->mysqli->prepare("
INSERT INTO $this->table (username, pass, email, pin, api_key)
VALUES (?, ?, ?, ?, ?)
INSERT INTO $this->table (username, pass, email, pin, api_key, is_locked)
VALUES (?, ?, ?, ?, ?, ?)
");
} else {
$is_locked = 0;
$is_admin = 1;
$stmt = $this->mysqli->prepare("
INSERT INTO $this->table (username, pass, email, pin, api_key, is_admin)
VALUES (?, ?, ?, ?, ?, 1)
INSERT INTO $this->table (username, pass, email, pin, api_key, is_admin, is_locked)
VALUES (?, ?, ?, ?, ?, 1, ?)
");
}
@ -475,15 +515,33 @@ class User {
$password_hash = $this->getHash($password1);
$pin_hash = $this->getHash($pin);
$apikey_hash = $this->getHash($username);
$username_clean = strip_tags($username);
if ($this->checkStmt($stmt) && $stmt->bind_param('sssss', $username, $password_hash, $email1, $pin_hash, $apikey_hash)) {
if (!$stmt->execute()) {
$this->setErrorMessage( 'Unable to register' );
if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username already exists' );
return false;
if ($this->checkStmt($stmt) && $stmt->bind_param('sssssi', $username_clean, $password_hash, $email1, $pin_hash, $apikey_hash, $is_locked) && $stmt->execute()) {
if ($this->config['accounts']['confirm_email']['enabled'] && $is_admin != 1) {
if ($token = $this->token->createToken('confirm_email', $stmt->insert_id)) {
$aData['username'] = $username_clean;
$aData['token'] = $token;
$aData['email'] = $email1;
$aData['subject'] = 'E-Mail verification';
if (!$this->mail->sendMail('register/confirm_email', $aData)) {
$this->setErrorMessage('Unable to request email confirmation');
return false;
}
return true;
} else {
$this->setErrorMessage('Failed to create confirmation token');
$this->debug->append('Unable to create confirm_email token: ' . $this->token->getError());
return false;
}
} else {
return true;
}
$stmt->close();
return true;
} else {
$this->setErrorMessage( 'Unable to register' );
$this->debug->append('Failed to insert user into DB: ' . $this->mysqli->error);
if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username or email already registered' );
return false;
}
return false;
}
@ -495,9 +553,9 @@ class User {
* @param new2 string New password verification
* @return bool
**/
public function useToken($token, $new1, $new2) {
public function resetPassword($token, $new1, $new2) {
$this->debug->append("STA " . __METHOD__, 4);
if ($id = $this->getIdFromToken($token)) {
if ($aToken = $this->token->getToken($token)) {
if ($new1 !== $new2) {
$this->setErrorMessage( 'New passwords do not match' );
return false;
@ -507,54 +565,46 @@ class User {
return false;
}
$new_hash = $this->getHash($new1);
$stmt = $this->mysqli->prepare("UPDATE $this->table SET pass = ?, token = NULL WHERE id = ? AND token = ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('sis', $new_hash, $id, $token) && $stmt->execute() && $stmt->affected_rows === 1) {
return true;
$stmt = $this->mysqli->prepare("UPDATE $this->table SET pass = ? WHERE id = ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('si', $new_hash, $aToken['account_id']) && $stmt->execute() && $stmt->affected_rows === 1) {
if ($this->token->deleteToken($aToken['token'])) {
return true;
} else {
$this->setErrorMessage('Unable to invalidate used token');
}
} else {
$this->setErrorMessage('Unable to set new password');
}
} else {
$this->setErrorMessage("Unable find user for your token");
return false;
$this->setErrorMessage('Invalid token');
}
$this->debug->append('Failed to update password:' . $this->mysqli->error);
return false;
}
/**
* Reset a password by sending a password reset mail
* @param username string Username to reset password for
* @param smarty object Smarty object for mail templating
* @return bool
**/
public function resetPassword($username, $smarty) {
public function initResetPassword($username) {
$this->debug->append("STA " . __METHOD__, 4);
// Fetch the users mail address
if (empty($username)) {
$this->serErrorMessage("Username must not be empty");
return false;
}
if (!$email = $this->getUserEmail($username)) {
if (!$aData['email'] = $this->getUserEmail($username)) {
$this->setErrorMessage("Unable to find a mail address for user $username");
return false;
}
if (!$this->setUserToken($this->getUserId($username))) {
$this->setErrorMessage("Unable to setup token for password reset");
if (!$aData['token'] = $this->token->createToken('password_reset', $this->getUserId($username))) {
$this->setErrorMessage('Unable to setup token for password reset');
return false;
}
// Send password reset link
if (!$token = $this->getUserToken($this->getUserId($username))) {
$this->setErrorMessage("Unable fetch token for password reset");
return false;
}
$smarty->assign('TOKEN', $token);
$smarty->assign('USERNAME', $username);
$smarty->assign('SUBJECT', 'Password Reset Request');
$smarty->assign('WEBSITENAME', $this->config['website']['name']);
$headers = 'From: Website Administration <' . $this->config['website']['email'] . ">\n";
$headers .= "MIME-Version: 1.0\n";
$headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n";
if (mail($email,
$smarty->fetch('templates/mail/subject.tpl'),
$smarty->fetch('templates/mail/body.tpl'),
$headers)) {
$aData['username'] = $username;
$aData['subject'] = 'Password Reset Request';
if ($this->mail->sendMail('password/reset', $aData)) {
return true;
} else {
$this->setErrorMessage("Unable to send mail to your address");
@ -584,3 +634,5 @@ class User {
// Make our class available automatically
$user = new User($debug, $mysqli, SALT, $config);
$user->setMail($mail);
$user->setToken($oToken);

View File

@ -135,15 +135,9 @@ class Worker {
**/
public function getCountAllActiveWorkers() {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("SELECT COUNT(DISTINCT username) AS total FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)");
if ($this->checkStmt($stmt)) {
if (!$stmt->execute()) {
return false;
}
$result = $stmt->get_result();
$stmt->close();
$stmt = $this->mysqli->prepare("SELECT IFNULL(COUNT(DISTINCT username), 0) AS total FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)");
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_object()->total;
}
return false;
}

View File

@ -96,22 +96,24 @@ $config['ap_threshold']['max'] = 250;
* Website specific configuration settings
*
* Explanation:
* title : Website title used in master template
* name : The pool name, displayed in the header and mails
* slogan : A special slogan, also displayed in the header below name
* email : `From` addresses used in notifications
* theme : Theme used for desktop browsers
* mobile : Enable/Disable mobile theme support
* mobile_theme : Theme used for mobile browsers
* title : Website title used in master template
* name : The pool name, displayed in the header and mails
* slogan : A special slogan, also displayed in the header below name
* email : `From` addresses used in notifications
* theme : Theme used for desktop browsers
* mobile : Enable/Disable mobile theme support
* mobile_theme : Theme used for mobile browsers
* api disabled : Disable the sites API functions
*
* Defaults:
* title = `The Pool - Mining Evolved`
* name = `The Pool`
* slogan = `Resistance is futile`
* email = `test@example.com`
* theme = `mmcFE`
* mobile = true
* mobile_theme = `mobile`
* title = `The Pool - Mining Evolved`
* name = `The Pool`
* slogan = `Resistance is futile`
* email = `test@example.com`
* theme = `mmcFE`
* mobile = true
* mobile_theme = `mobile`
* api disbabled = false
**/
$config['website']['title'] = 'The Pool - Mining Evolved';
$config['website']['name'] = 'The Pool';
@ -120,7 +122,51 @@ $config['website']['email'] = 'test@example.com';
$config['website']['theme'] = 'mmcFE';
$config['website']['mobile'] = true;
$config['website']['mobile_theme'] = 'mobile';
$config['website']['api']['disabled'] = false;
/**
* Account specific settings
*
* Explanation
* You can change some defaults on how accounts are created or registered
* By default, all newly created accounts will require an email verificaiton.
* Only after acitivating an account the user will be able to login
*
* Invitations will allow your users to invite new members to join the pool.
* After sending a mail to the invited user, they can register using the token
* created. Invitations can be enabled and disabled through the admin panel.
* Sent invitations are listed on the account invitations page.
*
* You can limit the number of registrations send per account via configuration
* variable.
*
* Options:
* confirm_email : Send confirmation mail to user after registration
* count : Maximum invitations a user is able to send
*
* Defaults:
* confirm_email : true
* count : 5
**/
$config['accounts']['confirm_email']['enabled'] = true;
$config['accounts']['invitations']['count'] = 5;
/**
* Some basic access restrictions on some pages
*
* Explanation:
* Some pools would like to run a few pages for public access instead
* of enforcing a login. You can change visibility of some pages here.
*
* Options:
* 'public' : Allow guest access and authenticated user to view page
* 'private' : Only allow logged in users access to view page
*
* Defaults:
* 'private' for every page
**/
$config['website']['acl']['statistics']['pool'] = 'private';
$config['website']['acl']['statistics']['blocks'] = 'private';
/**
* Re-Captcha settings
@ -133,7 +179,19 @@ $config['recaptcha']['private_key'] = 'YOUR_PRIVATE_RECAPTCHA_KEY';
// Currency system used in this pool, default: `LTC`
$config['currency'] = 'LTC';
// Default transaction fee, added by RPC server, default: 0.1
/**
* Default transaction fee to apply to user transactions
*
* Explanation
* The coin daemon applies transcation fees to young coins.
* Since we are unable to find out what the exact fee was we set
* a default value here which is applied to both manual and auto payouts.
* If this is not set, no fee is applied in the transactions history but
* the user might still see them when the coins arrive.
*
* Default:
* txfee = 0.1
**/
$config['txfee'] = 0.1;
// Payout a block bonus to block finders, default: 0 (disabled)
@ -151,18 +209,47 @@ $config['block_bonus'] = 0;
* Available options:
* prop: Proportional payout system
* pps : Pay Per Share payout system
* pplns : Pay Per Last N Shares payout system
*
* Default:
* prop
**/
$config['payout_system'] = 'prop';
// For debugging purposes you can archive shares in the archive_shares table, default: true
$config['archive_shares'] = true;
/**
* Archiving configuration for debugging
*
* Explanation:
* By default, we don't need to archive for a long time. PPLNS and Hashrate
* calculations rely on this archive, but all shares past a certain point can
* safely be deleted.
*
* To ensure we have enough shares on stack for PPLNS, this
* is set to the past 10 rounds. Even with lucky ones in between those should
* fit the PPLNS target. On top of that, even if we have more than 10 rounds,
* we still keep the last maxage shares to ensure we can calculate hashrates.
* Both conditions need to be met in order for shares to be purged from archive.
*
* Proportional mode will only keep the past 24 hours. These are required for
* hashrate calculations to work past a round, hence 24 hours was selected as
* the default. You may want to increase the time for debugging, then add any
* integer reflecting minutes of shares to keep.
*
* Availabe Options:
* maxrounds : PPLNS, keep shares for maxrounds
* maxage : PROP and PPLNS, delete shares older than maxage minutes
*
* Default:
* maxrounds = 10
* maxage = 60 * 24 (24h)
**/
$config['archive']['maxrounds'] = 10;
$config['archive']['maxage'] = 60 * 24;
// URL prefix for block searches, used for block links, default: `http://explorer.litecoin.net/search?q=`
// If empty, the block link to the block information page will be removed
$config['blockexplorer'] = 'http://explorer.litecoin.net/search?q=';
// URL prefix for block searches, used for block links, default: `http://explorer.litecoin.net/block/`
// The Blockhash is appended on the templates to this URL
// If this config is empty, the block link to the block information page will be removed
$config['blockexplorer'] = 'http://explorer.litecoin.net/block/';
// Link to blockchain information, used for difficulty link, default: `http://allchains.info`
// If empty, the difficulty link to the chain information will be removed
@ -171,6 +258,48 @@ $config['chaininfo'] = 'http://allchains.info';
// Pool fees applied to users in percent, default: 0 (disabled)
$config['fees'] = 0;
/**
* PPLNS requires some settings to run properly. First we need to define
* a default shares count that is applied if we don't have a proper type set.
* Different dynamic types can be applied, or you can run a fixed scheme.
*
* Explanation
*
* PPLNS can run on two different payouts: fixed and blockavg. Each one
* defines a different PPLNS target.
*
* Fixed means we will be looking at the shares setup in the default
* setting. There is no automatic adjustments to the PPLNS target,
* all users will be paid out proportionally to that target.
*
* Blockavg will look at the last blockcount blocks shares and take
* the average as the PPLNS target. This will be automatically adjusted
* when difficulty changes and more blocks are available. This keeps the
* target dynamic but still traceable.
*
* If you use the fixed type it will use $config['pplns']['shares']['default']
* for target calculations, if you use blockavg type it will use
* $config['pplns']['blockavg']['blockcount'] blocks average for target
* calculations.
*
* default : Default target shares for PPLNS
* type : Payout type used in PPLNS
* blockcount : Amount of blocks to check for avg shares
*
* Available Options:
* default : amount of shares, integeger
* type : blockavg or fixed
* blockcount : amount of blocks, any integer
*
* Defaults:
* default = 4000000
* type = `blockavg`
* blockcount = 10
**/
$config['pplns']['shares']['default'] = 4000000;
$config['pplns']['shares']['type'] = 'blockavg';
$config['pplns']['blockavg']['blockcount'] = 10;
// Pool target difficulty as set in pushpoold configuration file
// Please also read this for stratum: https://github.com/TheSerapher/php-mmcfe-ng/wiki/FAQ
$config['difficulty'] = 20;
@ -181,7 +310,7 @@ $config['difficulty'] = 20;
*
* Explanation:
*
* Proportional Payout System
* Proportional + PPLNS Payout System
* When running a pool on fixed mode, each block will be paid
* out as defined in `reward`. If you wish to pass transaction
* fees inside discovered blocks on to user, set this to `block`.
@ -217,11 +346,17 @@ $config['confirmations'] = 120;
/**
* Memcache configuration
*
* To disable memcache set option $config['memcache']['enabled'] = false
* After disable memcache installation of memcache is not required.
*
* Please note that a memcache is greatly increasing performance
* when combined with the `statistics.php` cronjob. Disabling this
* is not recommended in a live environment!
*
* Explanations
* enabled : Disable (false) memcache for debugging or enable (true) it
* host : Host IP or hostname
* port : memcache port
* keyprefix : Must be changed for multiple mmcfe-ng instances on one host
* expiration : Default expiration time in seconds of all cached keys.
* Increase if caches expire too fast.
@ -247,19 +382,77 @@ $config['memcache']['splay'] = 15;
/**
* Cookie configiration
*
* For multiple installations of this cookie change the cookie name
* You can configure the cookie behaviour to secure your cookies more than the PHP defaults
*
* For multiple installations of mmcfe-ng on the same domain you must change the cookie path.
*
* Explanation:
* duration:
* the amount of time, in seconds, that a cookie should persist in the users browser.
* 0 = until closed; 1440 = 24 minutes. Check your php.ini 'session.gc_maxlifetime' value
* and ensure that it is at least the duration specified here.
*
* domain:
* the only domain name that may access this cookie in the browser
*
* path:
* the highest path on the domain that can access this cookie; i.e. if running two pools
* from a single domain you might set the path /ltc/ and /ftc/ to separate user session
* cookies between the two.
*
* httponly:
* marks the cookie as accessible only through the HTTP protocol. The cookie can't be
* accessed by scripting languages, such as JavaScript. This can help to reduce identity
* theft through XSS attacks in most browsers.
*
* secure:
* marks the cookie as accessible only through the HTTPS protocol. If you have a SSL
* certificate installed on your domain name then this will stop a user accidently
* accessing the site over a HTTP connection, without SSL, exposing their session cookie.
*
* Default:
* path = '/'
* name = 'POOLERCOOKIE'
* domain = ''
* duration = '1440'
* domain = ''
* path = '/'
* httponly = true
* secure = false
**/
$config['cookie']['path'] = '/';
$config['cookie']['name'] = 'POOLERCOOKIE';
$config['cookie']['duration'] = '1440';
$config['cookie']['domain'] = '';
$config['cookie']['path'] = '/';
$config['cookie']['httponly'] = true;
$config['cookie']['secure'] = false;
// Disable or enable smarty cache
// This is usually not required, default: 0
$config['cache'] = 0;
/**
* Enable or disable the Smarty cache
*
* Explanation:
* Smarty implements a file based cache for all HTML output generated
* from dynamic scripts. It can be enabled to cache the HTML data on disk,
* future request are served from those cache files.
*
* This may or may not work as expected, in general Memcache is used to cache
* all data so rendering the page should not take too long anyway.
*
* You can test this out and enable (1) this setting but it's not guaranteed to
* work with mmcfe-ng.
*
* Ensure that the folder `templates/cache` is writable by the webserver!
*
* cache = Enable/Disable the cache
* cache_lifetime = Time to keep files in seconds before updating them
*
* Options:
* cache:
* 0 = disabled
* 1 = enabled
* cache_lifetime:
* time in seconds
*
* Defaults:
* cache = 0, disabled
* cache_lifetime = 30 seconds
**/
$config['smarty']['cache'] = 0;
$config['smarty']['cache_lifetime'] = 30;
?>

148
public/include/lib/KLogger.php Executable file
View File

@ -0,0 +1,148 @@
<?php
/* Finally, A light, permissions-checking logging class.
*
* Author : Kenneth Katzgrau < katzgrau@gmail.com >
* Date : July 26, 2008
* Comments : Originally written for use with wpSearch
* Website : http://codefury.net
* Version : 1.0
*
* Usage:
* $log = new KLogger ( "log.txt" , KLogger::INFO );
* $log->LogInfo("Returned a million search results"); //Prints to the log file
* $log->LogFATAL("Oh dear."); //Prints to the log file
* $log->LogDebug("x = 5"); //Prints nothing due to priority setting
*/
class KLogger
{
const DEBUG = 1; // Most Verbose
const INFO = 2; // ...
const WARN = 3; // ...
const ERROR = 4; // ...
const FATAL = 5; // Least Verbose
const OFF = 6; // Nothing at all.
const LOG_OPEN = 1;
const OPEN_FAILED = 2;
const LOG_CLOSED = 3;
/* Public members: Not so much of an example of encapsulation, but that's okay. */
public $Log_Status = KLogger::LOG_CLOSED;
public $DateFormat = "Y-m-d G:i:s";
public $MessageQueue;
private $log_file;
private $priority = KLogger::INFO;
private $file_handle;
public function __construct( $filepath , $priority )
{
if ( $priority == KLogger::OFF ) return;
$this->log_file = $filepath;
$this->MessageQueue = array();
$this->priority = $priority;
if ( file_exists( $this->log_file ) )
{
if ( !is_writable($this->log_file) )
{
$this->Log_Status = KLogger::OPEN_FAILED;
$this->MessageQueue[] = "The file exists, but could not be opened for writing. Check that appropriate permissions have been set.";
return;
}
}
if ( $this->file_handle = fopen( $this->log_file , "a" ) )
{
$this->Log_Status = KLogger::LOG_OPEN;
$this->MessageQueue[] = "The log file was opened successfully.";
}
else
{
$this->Log_Status = KLogger::OPEN_FAILED;
$this->MessageQueue[] = "The file could not be opened. Check permissions.";
}
return;
}
public function __destruct()
{
if ( $this->file_handle )
fclose( $this->file_handle );
}
public function LogInfo($line)
{
$this->Log( $line , KLogger::INFO );
}
public function LogDebug($line)
{
$this->Log( $line , KLogger::DEBUG );
}
public function LogWarn($line)
{
$this->Log( $line , KLogger::WARN );
}
public function LogError($line)
{
$this->Log( $line , KLogger::ERROR );
}
public function LogFatal($line)
{
$this->Log( $line , KLogger::FATAL );
}
public function Log($line, $priority)
{
if ( $this->priority <= $priority )
{
$status = $this->getTimeLine( $priority );
$this->WriteFreeFormLine ( "$status $line \n" );
}
}
public function WriteFreeFormLine( $line )
{
if ( $this->Log_Status == KLogger::LOG_OPEN && $this->priority != KLogger::OFF )
{
if (fwrite( $this->file_handle , $line ) === false) {
$this->MessageQueue[] = "The file could not be written to. Check that appropriate permissions have been set.";
}
}
}
private function getTimeLine( $level )
{
$time = date( $this->DateFormat );
switch( $level )
{
case KLogger::INFO:
return "$time - INFO -->";
case KLogger::WARN:
return "$time - WARN -->";
case KLogger::DEBUG:
return "$time - DEBUG -->";
case KLogger::ERROR:
return "$time - ERROR -->";
case KLogger::FATAL:
return "$time - FATAL -->";
default:
return "$time - LOG -->";
}
}
}
?>

View File

@ -0,0 +1,536 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
/**
* Scrypt key derivation function
*
* @see http://www.tarsnap.com/scrypt.html
* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01
*/
abstract class Scrypt
{
/**
* Execute the scrypt algorithm
*
* @param string $password
* @param string $salt
* @param integer $n CPU cost
* @param integer $r Memory cost
* @param integer $p parallelization cost
* @param integer $length size of the output key
* @return string
*/
public static function calc($password, $salt, $n, $r, $p, $length)
{
if ($n == 0 || ($n & ($n - 1)) != 0) {
throw new Exception\InvalidArgumentException("N must be > 0 and a power of 2");
}
if ($n > PHP_INT_MAX / 128 / $r) {
throw new Exception\InvalidArgumentException("Parameter n is too large");
}
if ($r > PHP_INT_MAX / 128 / $p) {
throw new Exception\InvalidArgumentException("Parameter r is too large");
}
if (extension_loaded('Scrypt')) {
if ($length < 16) {
throw new Exception\InvalidArgumentException("Key length is too low, must be greater or equal to 16");
}
return self::hex2bin(scrypt($password, $salt, $n, $r, $p, $length));
}
$b = Pbkdf2::calc('sha256', $password, $salt, 1, $p * 128 * $r);
$s = '';
for ($i = 0; $i < $p; $i++) {
$s .= self::scryptROMix(substr($b, $i * 128 * $r, 128 * $r), $n, $r);
}
return Pbkdf2::calc('sha256', $password, $s, 1, $length);
}
/**
* scryptROMix
*
* @param string $b
* @param integer $n
* @param integer $r
* @return string
* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-4
*/
protected static function scryptROMix($b, $n, $r)
{
$x = $b;
$v = array();
for ($i = 0; $i < $n; $i++) {
$v[$i] = $x;
$x = self::scryptBlockMix($x, $r);
}
for ($i = 0; $i < $n; $i++) {
$j = self::integerify($x) % $n;
$t = $x ^ $v[$j];
$x = self::scryptBlockMix($t, $r);
}
return $x;
}
/**
* scryptBlockMix
*
* @param string $b
* @param integer $r
* @return string
* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-3
*/
protected static function scryptBlockMix($b, $r)
{
$x = substr($b, -64);
$even = '';
$odd = '';
$len = 2 * $r;
for ($i = 0; $i < $len; $i++) {
if (PHP_INT_SIZE === 4) {
$x = self::salsa208Core32($x ^ substr($b, 64 * $i, 64));
} else {
$x = self::salsa208Core64($x ^ substr($b, 64 * $i, 64));
}
if ($i % 2 == 0) {
$even .= $x;
} else {
$odd .= $x;
}
}
return $even . $odd;
}
/**
* Salsa 20/8 core (32 bit version)
*
* @param string $b
* @return string
* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-2
* @see http://cr.yp.to/salsa20.html
*/
protected static function salsa208Core32($b)
{
$b32 = array();
for ($i = 0; $i < 16; $i++) {
list(, $b32[$i]) = unpack("V", substr($b, $i * 4, 4));
}
$x = $b32;
for ($i = 0; $i < 8; $i += 2) {
$a = ($x[ 0] + $x[12]);
$x[ 4] ^= ($a << 7) | ($a >> 25) & 0x7f;
$a = ($x[ 4] + $x[ 0]);
$x[ 8] ^= ($a << 9) | ($a >> 23) & 0x1ff;
$a = ($x[ 8] + $x[ 4]);
$x[12] ^= ($a << 13) | ($a >> 19) & 0x1fff;
$a = ($x[12] + $x[ 8]);
$x[ 0] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
$a = ($x[ 5] + $x[ 1]);
$x[ 9] ^= ($a << 7) | ($a >> 25) & 0x7f;
$a = ($x[ 9] + $x[ 5]);
$x[13] ^= ($a << 9) | ($a >> 23) & 0x1ff;
$a = ($x[13] + $x[ 9]);
$x[ 1] ^= ($a << 13) | ($a >> 19) & 0x1fff;
$a = ($x[ 1] + $x[13]);
$x[ 5] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
$a = ($x[10] + $x[ 6]);
$x[14] ^= ($a << 7) | ($a >> 25) & 0x7f;
$a = ($x[14] + $x[10]);
$x[ 2] ^= ($a << 9) | ($a >> 23) & 0x1ff;
$a = ($x[ 2] + $x[14]);
$x[ 6] ^= ($a << 13) | ($a >> 19) & 0x1fff;
$a = ($x[ 6] + $x[ 2]);
$x[10] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
$a = ($x[15] + $x[11]);
$x[ 3] ^= ($a << 7) | ($a >> 25) & 0x7f;
$a = ($x[ 3] + $x[15]);
$x[ 7] ^= ($a << 9) | ($a >> 23) & 0x1ff;
$a = ($x[ 7] + $x[ 3]);
$x[11] ^= ($a << 13) | ($a >> 19) & 0x1fff;
$a = ($x[11] + $x[ 7]);
$x[15] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
$a = ($x[ 0] + $x[ 3]);
$x[ 1] ^= ($a << 7) | ($a >> 25) & 0x7f;
$a = ($x[ 1] + $x[ 0]);
$x[ 2] ^= ($a << 9) | ($a >> 23) & 0x1ff;
$a = ($x[ 2] + $x[ 1]);
$x[ 3] ^= ($a << 13) | ($a >> 19) & 0x1fff;
$a = ($x[ 3] + $x[ 2]);
$x[ 0] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
$a = ($x[ 5] + $x[ 4]);
$x[ 6] ^= ($a << 7) | ($a >> 25) & 0x7f;
$a = ($x[ 6] + $x[ 5]);
$x[ 7] ^= ($a << 9) | ($a >> 23) & 0x1ff;
$a = ($x[ 7] + $x[ 6]);
$x[ 4] ^= ($a << 13) | ($a >> 19) & 0x1fff;
$a = ($x[ 4] + $x[ 7]);
$x[ 5] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
$a = ($x[10] + $x[ 9]);
$x[11] ^= ($a << 7) | ($a >> 25) & 0x7f;
$a = ($x[11] + $x[10]);
$x[ 8] ^= ($a << 9) | ($a >> 23) & 0x1ff;
$a = ($x[ 8] + $x[11]);
$x[ 9] ^= ($a << 13) | ($a >> 19) & 0x1fff;
$a = ($x[ 9] + $x[ 8]);
$x[10] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
$a = ($x[15] + $x[14]);
$x[12] ^= ($a << 7) | ($a >> 25) & 0x7f;
$a = ($x[12] + $x[15]);
$x[13] ^= ($a << 9) | ($a >> 23) & 0x1ff;
$a = ($x[13] + $x[12]);
$x[14] ^= ($a << 13) | ($a >> 19) & 0x1fff;
$a = ($x[14] + $x[13]);
$x[15] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
}
for ($i = 0; $i < 16; $i++) {
$b32[$i] = $b32[$i] + $x[$i];
}
$result = '';
for ($i = 0; $i < 16; $i++) {
$result .= pack("V", $b32[$i]);
}
return $result;
}
/**
* Salsa 20/8 core (64 bit version)
*
* @param string $b
* @return string
* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-2
* @see http://cr.yp.to/salsa20.html
*/
protected static function salsa208Core64($b)
{
$b32 = array();
for ($i = 0; $i < 16; $i++) {
list(, $b32[$i]) = unpack("V", substr($b, $i * 4, 4));
}
$x = $b32;
for ($i = 0; $i < 8; $i += 2) {
$a = ($x[ 0] + $x[12]) & 0xffffffff;
$x[ 4] ^= ($a << 7) | ($a >> 25);
$a = ($x[ 4] + $x[ 0]) & 0xffffffff;
$x[ 8] ^= ($a << 9) | ($a >> 23);
$a = ($x[ 8] + $x[ 4]) & 0xffffffff;
$x[12] ^= ($a << 13) | ($a >> 19);
$a = ($x[12] + $x[ 8]) & 0xffffffff;
$x[ 0] ^= ($a << 18) | ($a >> 14);
$a = ($x[ 5] + $x[ 1]) & 0xffffffff;
$x[ 9] ^= ($a << 7) | ($a >> 25);
$a = ($x[ 9] + $x[ 5]) & 0xffffffff;
$x[13] ^= ($a << 9) | ($a >> 23);
$a = ($x[13] + $x[ 9]) & 0xffffffff;
$x[ 1] ^= ($a << 13) | ($a >> 19);
$a = ($x[ 1] + $x[13]) & 0xffffffff;
$x[ 5] ^= ($a << 18) | ($a >> 14);
$a = ($x[10] + $x[ 6]) & 0xffffffff;
$x[14] ^= ($a << 7) | ($a >> 25);
$a = ($x[14] + $x[10]) & 0xffffffff;
$x[ 2] ^= ($a << 9) | ($a >> 23);
$a = ($x[ 2] + $x[14]) & 0xffffffff;
$x[ 6] ^= ($a << 13) | ($a >> 19);
$a = ($x[ 6] + $x[ 2]) & 0xffffffff;
$x[10] ^= ($a << 18) | ($a >> 14);
$a = ($x[15] + $x[11]) & 0xffffffff;
$x[ 3] ^= ($a << 7) | ($a >> 25);
$a = ($x[ 3] + $x[15]) & 0xffffffff;
$x[ 7] ^= ($a << 9) | ($a >> 23);
$a = ($x[ 7] + $x[ 3]) & 0xffffffff;
$x[11] ^= ($a << 13) | ($a >> 19);
$a = ($x[11] + $x[ 7]) & 0xffffffff;
$x[15] ^= ($a << 18) | ($a >> 14);
$a = ($x[ 0] + $x[ 3]) & 0xffffffff;
$x[ 1] ^= ($a << 7) | ($a >> 25);
$a = ($x[ 1] + $x[ 0]) & 0xffffffff;
$x[ 2] ^= ($a << 9) | ($a >> 23);
$a = ($x[ 2] + $x[ 1]) & 0xffffffff;
$x[ 3] ^= ($a << 13) | ($a >> 19);
$a = ($x[ 3] + $x[ 2]) & 0xffffffff;
$x[ 0] ^= ($a << 18) | ($a >> 14);
$a = ($x[ 5] + $x[ 4]) & 0xffffffff;
$x[ 6] ^= ($a << 7) | ($a >> 25);
$a = ($x[ 6] + $x[ 5]) & 0xffffffff;
$x[ 7] ^= ($a << 9) | ($a >> 23);
$a = ($x[ 7] + $x[ 6]) & 0xffffffff;
$x[ 4] ^= ($a << 13) | ($a >> 19);
$a = ($x[ 4] + $x[ 7]) & 0xffffffff;
$x[ 5] ^= ($a << 18) | ($a >> 14);
$a = ($x[10] + $x[ 9]) & 0xffffffff;
$x[11] ^= ($a << 7) | ($a >> 25);
$a = ($x[11] + $x[10]) & 0xffffffff;
$x[ 8] ^= ($a << 9) | ($a >> 23);
$a = ($x[ 8] + $x[11]) & 0xffffffff;
$x[ 9] ^= ($a << 13) | ($a >> 19);
$a = ($x[ 9] + $x[ 8]) & 0xffffffff;
$x[10] ^= ($a << 18) | ($a >> 14);
$a = ($x[15] + $x[14]) & 0xffffffff;
$x[12] ^= ($a << 7) | ($a >> 25);
$a = ($x[12] + $x[15]) & 0xffffffff;
$x[13] ^= ($a << 9) | ($a >> 23);
$a = ($x[13] + $x[12]) & 0xffffffff;
$x[14] ^= ($a << 13) | ($a >> 19);
$a = ($x[14] + $x[13]) & 0xffffffff;
$x[15] ^= ($a << 18) | ($a >> 14);
}
for ($i = 0; $i < 16; $i++) {
$b32[$i] = ($b32[$i] + $x[$i]) & 0xffffffff;
}
$result = '';
for ($i = 0; $i < 16; $i++) {
$result .= pack("V", $b32[$i]);
}
return $result;
}
/**
* Integerify
*
* Integerify (B[0] ... B[2 * r - 1]) is defined as the result
* of interpreting B[2 * r - 1] as a little-endian integer.
* Each block B is a string of 64 bytes.
*
* @param string $b
* @return integer
* @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-4
*/
protected static function integerify($b)
{
$v = 'v';
if (PHP_INT_SIZE === 8) {
$v = 'V';
}
list(,$n) = unpack($v, substr($b, -64));
return $n;
}
/**
* Convert hex string in a binary string
*
* @param string $hex
* @return string
*/
protected static function hex2bin($hex)
{
if (version_compare(PHP_VERSION, '5.4') >= 0) {
return hex2bin($hex);
}
$len = strlen($hex);
$result = '';
for ($i = 0; $i < $len; $i+=2) {
$result .= chr(hexdec($hex[$i] . $hex[$i+1]));
}
return $result;
}
}
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
/**
* PKCS #5 v2.0 standard RFC 2898
*/
class Pbkdf2
{
/**
* Generate the new key
*
* @param string $hash The hash algorithm to be used by HMAC
* @param string $password The source password/key
* @param string $salt
* @param integer $iterations The number of iterations
* @param integer $length The output size
* @throws Exception\InvalidArgumentException
* @return string
*/
public static function calc($hash, $password, $salt, $iterations, $length)
{
if (!Hmac::isSupported($hash)) {
throw new Exception\InvalidArgumentException("The hash algorithm $hash is not supported by " . __CLASS__);
}
$num = ceil($length / Hmac::getOutputSize($hash, Hmac::OUTPUT_BINARY));
$result = '';
for ($block = 1; $block <= $num; $block++) {
$hmac = hash_hmac($hash, $salt . pack('N', $block), $password, Hmac::OUTPUT_BINARY);
$mix = $hmac;
for ($i = 1; $i < $iterations; $i++) {
$hmac = hash_hmac($hash, $hmac, $password, Hmac::OUTPUT_BINARY);
$mix ^= $hmac;
}
$result .= $mix;
}
return substr($result, 0, $length);
}
}
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
/**
* PHP implementation of the RFC 2104 Hash based Message Authentication Code
*/
class Hmac
{
const OUTPUT_STRING = false;
const OUTPUT_BINARY = true;
/**
* Last algorithm supported
*
* @var string|null
*/
protected static $lastAlgorithmSupported;
/**
* Performs a HMAC computation given relevant details such as Key, Hashing
* algorithm, the data to compute MAC of, and an output format of String,
* or Binary.
*
* @param string $key
* @param string $hash
* @param string $data
* @param bool $output
* @throws Exception\InvalidArgumentException
* @return string
*/
public static function compute($key, $hash, $data, $output = self::OUTPUT_STRING)
{
if (empty($key)) {
throw new Exception\InvalidArgumentException('Provided key is null or empty');
}
if (!$hash || ($hash !== static::$lastAlgorithmSupported && !static::isSupported($hash))) {
throw new Exception\InvalidArgumentException(
"Hash algorithm is not supported on this PHP installation; provided '{$hash}'"
);
}
return hash_hmac($hash, $data, $key, $output);
}
/**
* Get the output size according to the hash algorithm and the output format
*
* @param string $hash
* @param bool $output
* @return integer
*/
public static function getOutputSize($hash, $output = self::OUTPUT_STRING)
{
return strlen(static::compute('key', $hash, 'data', $output));
}
/**
* Get the supported algorithm
*
* @return array
*/
public static function getSupportedAlgorithms()
{
return hash_algos();
}
/**
* Is the hash algorithm supported?
*
* @param string $algorithm
* @return bool
*/
public static function isSupported($algorithm)
{
if ($algorithm === static::$lastAlgorithmSupported) {
return true;
}
if (in_array(strtolower($algorithm), hash_algos(), true)) {
static::$lastAlgorithmSupported = $algorithm;
return true;
}
return false;
}
/**
* Clear the cache of last algorithm supported
*/
public static function clearLastAlgorithmCache()
{
static::$lastAlgorithmSupported = null;
}
}
function swapEndian($input){
$output = "";
for($i=0;$i< strlen($input);$i+=2){
$output .= substr($input, -($i+2), 2);
}
return $output;
}
/*for($i=0;$i < 200;$i++){
$value = Scrypt::calc($i, $i, 1024, 1, 1, 32);
echo "scrypt ".$i." hash:". bin2hex($value)."<br/>";
}*/
/*
$i = pack("H*", "01000000f615f7ce3b4fc6b8f61e8f89aedb1d0852507650533a9e3b10b9bbcc30639f279fcaa86746e1ef52d3edb3c4ad8259920d509bd073605c9bf1d59983752a6b06b817bb4ea78e011d012d59d4");
$value = Scrypt::calc($i, $i, 1024, 1, 1, 32);
echo "scrypt ".$i." hash:". bin2hex($value)."<br/>";
print_r( swapEndian(bin2hex($value)));
*/
// Function used for pushpoold solution checks
function word_reverse($str) {
$ret = '';
while (strlen($str) > 0) {
$ret .= substr($str, -8, 8);
$str = substr($str, 0, -8);
}
return $ret;
}
?>

View File

@ -0,0 +1,17 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
// Confirm an account by token
if (!isset($_GET['token']) || empty($_GET['token'])) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Missing token', 'TYPE' => 'errormsg');
} else if (!$aToken = $oToken->getToken($_GET['token'])) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to activate your account. Invalid token', 'TYPE' => 'errormsg');
} else {
$user->changeLocked($aToken['account_id']);
$oToken->deleteToken($aToken['token']);
$_SESSION['POPUP'][] = array('CONTENT' => 'Account activated. Please login.');
}
$smarty->assign('CONTENT', 'default.tpl');
?>

View File

@ -30,7 +30,8 @@ if ($user->isAuthenticated()) {
if ($continue == true) {
// Send balance to address, mind fee for transaction!
try {
if ($setting->getValue('auto_payout_active') == 0) {
$auto_payout = $monitoring->getStatus('auto_payout_active');
if ($auto_payout['value'] == 0) {
$bitcoin->sendtoaddress($sCoinAddress, $dBalance);
} else {
$_SESSION['POPUP'][] = array('CONTENT' => 'Auto-payout active, please contact site support immidiately to revoke invalid transactions.', 'TYPE' => 'errormsg');
@ -53,14 +54,14 @@ if ($user->isAuthenticated()) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service', 'TYPE' => 'errormsg');
}
} else {
$_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than ' . $config['txfee'] . ' ' . $conifg['currency'] . ' to cover transaction fees', 'TYPE' => 'errormsg');
$_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than ' . $config['txfee'] . ' ' . $config['currency'] . ' to cover transaction fees', 'TYPE' => 'errormsg');
}
$setting->setValue('manual_payout_active', 0);
}
break;
case 'updateAccount':
if ($user->updateAccount($_SESSION['USERDATA']['id'], $_POST['paymentAddress'], $_POST['payoutThreshold'], $_POST['donatePercent'], $_POST['email'])) {
if ($user->updateAccount($_SESSION['USERDATA']['id'], $_POST['paymentAddress'], $_POST['payoutThreshold'], $_POST['donatePercent'], $_POST['email'], $_POST['is_anonymous'])) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Account details updated', 'TYPE' => 'success');
} else {
$_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update your account: ' . $user->getError(), 'TYPE' => 'errormsg');

View File

@ -0,0 +1,25 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
if ($user->isAuthenticated()) {
if (!$setting->getValue('disable_invitations')) {
if ($invitation->getCountInvitations($_SESSION['USERDATA']['id']) >= $config['accounts']['invitations']['count']) {
$_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded the allowed invitations of ' . $config['accounts']['invitations']['count'], 'TYPE' => 'errormsg');
} else if (isset($_POST['do']) && $_POST['do'] == 'sendInvitation') {
if ($invitation->sendInvitation($_SESSION['USERDATA']['id'], $_POST['data'])) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Invitation sent');
} else {
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to send invitation to recipient: ' . $invitation->getError(), 'TYPE' => 'errormsg');
}
}
$aInvitations = $invitation->getInvitations($_SESSION['USERDATA']['id']);
$smarty->assign('INVITATIONS', $aInvitations);
} else {
$aInvitations = array();
$_SESSION['POPUP'][] = array('CONTENT' => 'Invitations are disabled', 'TYPE' => 'errormsg');
}
}
$smarty->assign('CONTENT', 'default.tpl');
?>

View File

@ -8,5 +8,6 @@ if ($user->isAuthenticated()) {
$user->setUserFailed($_SESSION['USERDATA']['id'], 0);
header("Location: " . $_SERVER['HTTP_REFERER']);
}
// Somehow we still need to load this empty template
$smarty->assign("CONTENT", "../../global/empty.tpl");
?>

View File

@ -6,6 +6,6 @@ if ($user->isAuthenticated()) {
$aTransactions = $transaction->getTransactions($_SESSION['USERDATA']['id']);
if (!$aTransactions) $_SESSION['POPUP'][] = array('CONTENT' => 'Could not find any transaction', 'TYPE' => 'errormsg');
$smarty->assign('TRANSACTIONS', $aTransactions);
$smarty->assign('CONTENT', 'default.tpl');
}
$smarty->assign('CONTENT', 'default.tpl');
?>

View File

@ -0,0 +1,108 @@
<?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");
}
// Fetch settings to propagate to template
$aCronStatus = array(
'statistics' => array (
array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('statistics_status') ),
array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('statistics_active') ),
array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('statistics_runtime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('statistics_starttime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('statistics_endtime') ),
array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('statistics_message') ),
),
'auto_payout' => array (
array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('auto_payout_status') ),
array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('auto_payout_active') ),
array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('auto_payout_runtime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('auto_payout_starttime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('auto_payout_endtime') ),
array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('auto_payout_message') ),
),
'archive_cleanup' => array (
array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('archive_cleanup_status') ),
array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('archive_cleanup_active') ),
array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('archive_cleanup_runtime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('archive_cleanup_starttime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('archive_cleanup_endtime') ),
array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('archive_cleanup_message') ),
),
'blockupdate' => array (
array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('blockupdate_status') ),
array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('blockupdate_active') ),
array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('blockupdate_runtime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('blockupdate_starttime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('blockupdate_endtime') ),
array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('blockupdate_message') ),
),
'findblock' => array (
array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('findblock_status') ),
array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('findblock_active') ),
array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('findblock_runtime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('findblock_starttime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('findblock_endtime') ),
array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('findblock_message') ),
),
'notifications' => array (
array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('notifications_status') ),
array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('notifications_active') ),
array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('notifications_runtime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('notifications_starttime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('notifications_endtime') ),
array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('notifications_message') ),
),
'tickerupdate' => array (
array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('tickerupdate_status') ),
array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('tickerupdate_active') ),
array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('tickerupdate_runtime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('tickerupdate_starttime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('tickerupdate_endtime') ),
array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('tickerupdate_message') ),
)
);
// Payout system specifics
switch ($config['payout_system']) {
case 'pplns':
$aCronStatus['pplns_payout'] = array (
array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('pplns_payout_status') ),
array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('pplns_payout_active') ),
array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('pplns_payout_runtime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('pplns_payout_starttime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('pplns_payout_endtime') ),
array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('pplns_payout_message') ),
);
break;
case 'pps':
$aCronStatus['pps_payout'] = array(
array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('pps_payout_status') ),
array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('pps_payout_active') ),
array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('pps_payout_runtime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('pps_payout_starttime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('pps_payout_endtime') ),
array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('pps_payout_message') ),
);
break;
case 'prop':
$aCronStatus['proportional_payout'] = array(
array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('proportional_payout_status') ),
array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('proportional_payout_active') ),
array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('proportional_payout_runtime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('proportional_payout_starttime') ),
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('proportional_payout_endtime') ),
array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('proportional_payout_message') ),
);
break;
}
$smarty->assign("CRONSTATUS", $aCronStatus);
// Tempalte specifics
$smarty->assign("CONTENT", "default.tpl");
?>

View File

@ -19,6 +19,7 @@ if (@$_REQUEST['do'] == 'save' && !empty($_REQUEST['data'])) {
// Fetch settings to propagate to template
$smarty->assign("MAINTENANCE", $setting->getValue('maintenance'));
$smarty->assign("LOCKREGISTRATION", $setting->getValue('lock_registration'));
$smarty->assign("DISABLEINVITATIONS", $setting->getValue('disable_invitations'));
// Tempalte specifics
$smarty->assign("CONTENT", "default.tpl");

View File

@ -2,10 +2,20 @@
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
if ($user->isAuthenticated()) {
// Check user to ensure they are admin
if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) {
header("HTTP/1.1 404 Page not found");
die("404 Page not found");
}
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
$debug->append('No cached version available, fetching from backend', 3);
$aTransactions = $transaction->getAllTransactions(@$_REQUEST['start']);
if (!$aTransactions) $_SESSION['POPUP'][] = array('CONTENT' => 'Could not find any transaction', 'TYPE' => 'errormsg');
$smarty->assign('TRANSACTIONS', $aTransactions);
$smarty->assign('CONTENT', 'default.tpl');
} else {
$debug->append('Using cached page', 3);
}
$smarty->assign('TRANSACTIONS', $aTransactions);
$smarty->assign('CONTENT', 'default.tpl');
?>

View File

@ -34,10 +34,17 @@ if (@$_POST['query']) {
$aBalance = $transaction->getBalance($aUser['id']);
$aUser['balance'] = $aBalance['confirmed'];
$aUser['hashrate'] = $statistics->getUserHashrate($aUser['id']);
$aUser['payout']['est_block'] = round(( (int)$aUser['shares'] / (int)$aRoundShares['valid'] ) * (int)$config['reward'], 3);
$aUser['payout']['est_fee'] = round(($config['fees'] / 100) * $aUser['payout']['est_block'], 3);
$aUser['payout']['est_donation'] = round((( $aUser['donate_percent'] / 100) * ($aUser['payout']['est_block'] - $aUser['payout']['est_fee'])), 3);
$aUser['payout']['est_payout'] = round($aUser['payout']['est_block'] - $aUser['payout']['est_donation'] - $aUser['payout']['est_fee'], 3);
if ($aUser['shares'] > 0) {
$aUser['payout']['est_block'] = round(( (int)$aUser['shares'] / (int)$aRoundShares['valid'] ) * (int)$config['reward'], 3);
$aUser['payout']['est_fee'] = round(($config['fees'] / 100) * $aUser['payout']['est_block'], 3);
$aUser['payout']['est_donation'] = round((( $aUser['donate_percent'] / 100) * ($aUser['payout']['est_block'] - $aUser['payout']['est_fee'])), 3);
$aUser['payout']['est_payout'] = round($aUser['payout']['est_block'] - $aUser['payout']['est_donation'] - $aUser['payout']['est_fee'], 3);
} else {
$aUser['payout']['est_block'] = 0;
$aUser['payout']['est_fee'] = 0;
$aUser['payout']['est_donation'] = 0;
$aUser['payout']['est_payout'] = 0;
}
$aUsers[$iKey] = $aUser;
}
// Assign our variables

View File

@ -9,15 +9,22 @@ if (!$user->isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) {
die("404 Page not found");
}
if ($bitcoin->can_connect() === true){
$dBalance = $bitcoin->query('getbalance');
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
$debug->append('No cached version available, fetching from backend', 3);
if ($bitcoin->can_connect() === true){
$dBalance = $bitcoin->query('getbalance');
} else {
$dBalance = 0;
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
}
// Fetch locked balance from transactions
$dLockedBalance = $transaction->getLockedBalance();
} else {
$dBalance = 0;
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
$debug->append('Using cached page', 3);
}
$smarty->assign("BALANCE", $dBalance);
$smarty->assign("LOCKED", $transaction->getLockedBalance());
$smarty->assign("LOCKED", $dLockedBalance);
// Tempalte specifics
$smarty->assign("CONTENT", "default.tpl");

View File

@ -1,8 +1,10 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
// Check if the API is activated
$api->isActive();
// Check for valid API key
$id = $user->checkApiKey($_REQUEST['api_key']);

View File

@ -1,8 +1,10 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
// Check if the API is activated
$api->isActive();
// Check user token
$id = $user->checkApiKey($_REQUEST['api_key']);

View File

@ -1,8 +1,10 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
// Check if the API is activated
$api->isActive();
// Check user token
$id = $user->checkApiKey($_REQUEST['api_key']);

View File

@ -1,8 +1,10 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
// Check if the API is activated
$api->isActive();
// Check user token
$id = $user->checkApiKey($_REQUEST['api_key']);

View File

@ -1,8 +1,10 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
// Check if the API is activated
$api->isActive();
// Check user token
$id = $user->checkApiKey($_REQUEST['api_key']);

View File

@ -1,8 +1,10 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
// Check if the API is activated
$api->isActive();
// Check user token
$id = $user->checkApiKey($_REQUEST['api_key']);

View File

@ -1,8 +1,10 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
// Check if the API is activated
$api->isActive();
// Check user token
$id = $user->checkApiKey($_REQUEST['api_key']);

View File

@ -1,8 +1,10 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
// Check if the API is activated
$api->isActive();
// Check user token
$id = $user->checkApiKey($_REQUEST['api_key']);

View File

@ -3,6 +3,9 @@
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
// Check if the API is activated
$api->isActive();
// Check user token
$user_id = $user->checkApiKey($_REQUEST['api_key']);

View File

@ -1,8 +1,10 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
// Check if the API is activated
$api->isActive();
// Check user token
$id = $user->checkApiKey($_REQUEST['api_key']);

View File

@ -1,8 +1,10 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
// Check if the API is activated
$api->isActive();
// Check user token
$user_id = $user->checkApiKey($_REQUEST['api_key']);

View File

@ -1,8 +1,10 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
// Check if the API is activated
$api->isActive();
// Check user token
$user_id = $user->checkApiKey($_REQUEST['api_key']);

View File

@ -1,10 +1,10 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
// {"pool_name":"Pool-X.eu","hashrate":"511128.99","workers":"2104","shares_this_round":92450,"last_block":"365294","network_hashrate":17327056.06}
// Check if the API is activated
$api->isActive();
// Fetch last block information
$aLastBlock = $block->getLast();

View File

@ -6,16 +6,21 @@ if (!defined('SECURITY')) die('Hacking attempt');
// Include markdown library
use \Michelf\Markdown;
// Fetch active news to display
$aNews = $news->getAllActive();
if (is_array($aNews)) {
foreach ($aNews as $key => $aData) {
// Transform Markdown content to HTML
$aNews[$key]['content'] = Markdown::defaultTransform($aData['content']);
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
$debug->append('No cached version available, fetching from backend', 3);
// Fetch active news to display
$aNews = $news->getAllActive();
if (is_array($aNews)) {
foreach ($aNews as $key => $aData) {
// Transform Markdown content to HTML
$aNews[$key]['content'] = Markdown::defaultTransform($aData['content']);
}
}
$smarty->assign("NEWS", $aNews);
} else {
$debug->append('Using cached page', 3);
}
// Load news entries for Desktop site and unauthenticated users
$smarty->assign("NEWS", $aNews);
$smarty->assign("CONTENT", "default.tpl");
?>

View File

@ -4,14 +4,14 @@
if (!defined('SECURITY'))
die('Hacking attempt');
if ($_POST['do'] == 'useToken') {
if ($user->useToken($_POST['token'], $_POST['newPassword'], $_POST['newPassword2'])) {
if (isset($_POST['do']) && $_POST['do'] == 'resetPassword') {
if ($user->resetPassword($_POST['token'], $_POST['newPassword'], $_POST['newPassword2'])) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Password reset complete! Please login.');
} else {
$_SESSION['POPUP'][] = array('CONTENT' => $user->getError(), 'TYPE' => 'errormsg');
}
}
// Tempalte specifics
$smarty->assign("CONTENT", "default.tpl");
?>

View File

@ -5,7 +5,7 @@ if (!defined('SECURITY'))
die('Hacking attempt');
// Process password reset request
if ($user->resetPassword($_POST['username'], $smarty)) {
if ($user->initResetPassword($_POST['username'], $smarty)) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mail account to finish your password reset');
} else {
$_SESSION['POPUP'][] = array('CONTENT' => $user->getError(), 'TYPE' => 'errormsg');

View File

@ -3,9 +3,12 @@
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
if ($setting->getValue('lock_registration')) {
if ($setting->getValue('lock_registration') && $setting->getValue('disable_invitations')) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg');
$smarty->assign("CONTENT", "disabled.tpl");
} else if ($setting->getValue('lock_registration') && !$setting->getValue('disable_invitations') && !isset($_GET['token'])) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Only invited users are allowed to register.', 'TYPE' => 'errormsg');
$smarty->assign("CONTENT", "disabled.tpl");
} else {
if ($config['recaptcha']['enabled']) {
require_once(INCLUDE_DIR . '/lib/recaptchalib.php');

View File

@ -13,33 +13,37 @@ if ($config['recaptcha']['enabled']) {
);
}
// Check if recaptcha is enabled, process form data if valid
if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_POST["recaptcha_response_field"]!=''){
if ($rsp->is_valid) {
$smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key']));
if ($setting->getValue('lock_registration')) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg');
} else if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2']) && !$setting->getValue('lock_registration')) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login');
if ($setting->getValue('disable_invitations') && $setting->getValue('lock_registration')) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg');
} else if ($setting->getValue('lock_registration') && !$setting->getValue('disable_invitations') && !isset($_POST['token'])) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Only invited users are allowed to register.', 'TYPE' => 'errormsg');
} else {
// Check if recaptcha is enabled, process form data if valid
if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_POST["recaptcha_response_field"]!=''){
if ($rsp->is_valid) {
$smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key']));
isset($_POST['token']) ? $token = $_POST['token'] : $token = '';
if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $token)) {
$config['accounts']['confirm_email']['enabled'] ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login');
} else {
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg');
}
} else {
$smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'], $rsp->error));
$_SESSION['POPUP'][] = array('CONTENT' => 'Invalid Captcha, please try again. (' . $rsp->error . ')', 'TYPE' => 'errormsg');
}
// Empty captcha
} else if ($config['recaptcha']['enabled']) {
$smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'], $rsp->error));
$_SESSION['POPUP'][] = array('CONTENT' => 'Empty Captcha, please try again.', 'TYPE' => 'errormsg');
// Captcha disabled
} else {
isset($_POST['token']) ? $token = $_POST['token'] : $token = '';
if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $token)) {
$config['accounts']['confirm_email']['enabled'] ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login');
} else {
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg');
}
} else {
$smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'], $rsp->error));
$_SESSION['POPUP'][] = array('CONTENT' => 'Invalid Captcha, please try again. (' . $rsp->error . ')', 'TYPE' => 'errormsg');
}
// Empty captcha
} else if ($config['recaptcha']['enabled']) {
$smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'], $rsp->error));
$_SESSION['POPUP'][] = array('CONTENT' => 'Empty Captcha, please try again.', 'TYPE' => 'errormsg');
// Captcha disabled
} else {
if ($setting->getValue('lock_registration')) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg');
} else if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2']) && !$setting->getValue('lock_registration')) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login');
} else {
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg');
}
}

View File

@ -4,17 +4,23 @@
if (!defined('SECURITY'))
die('Hacking attempt');
if ($bitcoin->can_connect() === true){
$dDifficulty = $bitcoin->query('getdifficulty');
if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty))
$dDifficulty = $dDifficulty['proof-of-work'];
$iBlock = $bitcoin->query('getblockcount');
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
$debug->append('No cached version available, fetching from backend', 3);
if ($bitcoin->can_connect() === true){
$dDifficulty = $bitcoin->query('getdifficulty');
if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty))
$dDifficulty = $dDifficulty['proof-of-work'];
$iBlock = $bitcoin->query('getblockcount');
} else {
$dDifficulty = 1;
$iBlock = 0;
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
}
$smarty->assign("CURRENTBLOCK", $iBlock);
$smarty->assign("DIFFICULTY", $dDifficulty);
} else {
$dDifficulty = 1;
$iBlock = 0;
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
$debug->append('Using cached page', 3);
}
$smarty->assign("CURRENTBLOCK", $iBlock);
$smarty->assign("DIFFICULTY", $dDifficulty);
$smarty->assign("CONTENT", "default.tpl");
?>

View File

@ -2,15 +2,24 @@
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
if (!$user->isAuthenticated()) header("Location: index.php?page=home");
// Grab the last blocks found
empty($_REQUEST['limit']) ? $iLimit = 20 : $iLimit = $_REQUEST['limit'];
$aBlocksFoundData = $statistics->getBlocksFound($iLimit);
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
$debug->append('No cached version available, fetching from backend', 3);
// Grab the last blocks found
$iLimit = 20;
$aBlocksFoundData = $statistics->getBlocksFound($iLimit);
// Propagate content our template
$smarty->assign("BLOCKSFOUND", $aBlocksFoundData);
$smarty->assign("BLOCKLIMIT", $iLimit);
// Propagate content our template
$smarty->assign("BLOCKSFOUND", $aBlocksFoundData);
$smarty->assign("BLOCKLIMIT", $iLimit);
} else {
$debug->append('Using cached page', 3);
}
$smarty->assign("CONTENT", "default.tpl");
if ($config['website']['acl']['statistics']['blocks'] == 'public') {
$smarty->assign("CONTENT", "default.tpl");
} else if ($user->isAuthenticated()) {
$smarty->assign("CONTENT", "default.tpl");
}
?>

View File

@ -1,16 +1,19 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
if ($user->isAuthenticated()) {
$aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA']['id']);
$aPoolHourlyHashRates = $statistics->getHourlyHashrateByPool();
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
$debug->append('No cached version available, fetching from backend', 3);
if ($user->isAuthenticated()) {
$aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA']['id']);
$aPoolHourlyHashRates = $statistics->getHourlyHashrateByPool();
}
$smarty->assign("YOURHASHRATES", @$aHourlyHashRates);
$smarty->assign("POOLHASHRATES", @$aPoolHourlyHashRates);
} else {
$debug->append('Using cached page', 3);
}
// Propagate content our template
$smarty->assign("YOURHASHRATES", @$aHourlyHashRates);
$smarty->assign("POOLHASHRATES", @$aPoolHourlyHashRates);
$smarty->assign("CONTENT", "default.tpl");
?>

View File

@ -1,59 +1,74 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
// Fetch data from wallet
if ($bitcoin->can_connect() === true){
$dDifficulty = $bitcoin->getdifficulty();
if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty))
$dDifficulty = $dDifficulty['proof-of-work'];
$iBlock = $bitcoin->getblockcount();
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
$debug->append('No cached version available, fetching from backend', 3);
// Fetch data from wallet
if ($bitcoin->can_connect() === true){
$dDifficulty = $bitcoin->getdifficulty();
if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty))
$dDifficulty = $dDifficulty['proof-of-work'];
$iBlock = $bitcoin->getblockcount();
$sBlockHash = $bitcoin->query('getblockhash', $iBlock);
} else {
$dDifficulty = 1;
$iBlock = 0;
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
}
// Top share contributors
$aContributorsShares = $statistics->getTopContributors('shares', 15);
// Top hash contributors
$aContributorsHashes = $statistics->getTopContributors('hashes', 15);
// Grab the last 10 blocks found
$iLimit = 5;
$aBlocksFoundData = $statistics->getBlocksFound($iLimit);
count($aBlocksFoundData) > 0 ? $aBlockData = $aBlocksFoundData[0] : $aBlockData = array();
// Estimated time to find the next block
$iCurrentPoolHashrate = $statistics->getCurrentHashrate();
// Time in seconds, not hours, using modifier in smarty to translate
$iCurrentPoolHashrate > 0 ? $iEstTime = $dDifficulty * pow(2,32) / ($iCurrentPoolHashrate * 1000) : $iEstTime = 0;
// Time since last block
$now = new DateTime( "now" );
if (!empty($aBlockData)) {
$dTimeSinceLast = ($now->getTimestamp() - $aBlockData['time']);
if ($dTimeSinceLast < 0) $dTimeSinceLast = 0;
} else {
$dTimeSinceLast = 0;
}
// Propagate content our template
$smarty->assign("ESTTIME", $iEstTime);
$smarty->assign("TIMESINCELAST", $dTimeSinceLast);
$smarty->assign("BLOCKSFOUND", $aBlocksFoundData);
$smarty->assign("BLOCKLIMIT", $iLimit);
$smarty->assign("CONTRIBSHARES", $aContributorsShares);
$smarty->assign("CONTRIBHASHES", $aContributorsHashes);
$smarty->assign("CURRENTBLOCK", $iBlock);
$smarty->assign("CURRENTBLOCKHASH", $sBlockHash);
if (count($aBlockData) > 0) {
$smarty->assign("LASTBLOCK", $aBlockData['height']);
$smarty->assign("LASTBLOCKHASH", $aBlockData['blockhash']);
} else {
$smarty->assign("LASTBLOCK", 0);
}
$smarty->assign("DIFFICULTY", $dDifficulty);
$smarty->assign("REWARD", $config['reward']);
} else {
$dDifficulty = 1;
$iBlock = 0;
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
$debug->append('Using cached page', 3);
}
// Top share contributors
$aContributorsShares = $statistics->getTopContributors('shares', 15);
// Top hash contributors
$aContributorsHashes = $statistics->getTopContributors('hashes', 15);
// Grab the last 10 blocks found
$iLimit = 5;
$aBlocksFoundData = $statistics->getBlocksFound($iLimit);
count($aBlocksFoundData) > 0 ? $aBlockData = $aBlocksFoundData[0] : $aBlockData = array();
// Estimated time to find the next block
$iCurrentPoolHashrate = $statistics->getCurrentHashrate();
// Time in seconds, not hours, using modifier in smarty to translate
$iCurrentPoolHashrate > 0 ? $iEstTime = $dDifficulty * pow(2,32) / ($iCurrentPoolHashrate * 1000) : $iEstTime = 0;
// Time since last block
$now = new DateTime( "now" );
if (!empty($aBlockData)) {
$dTimeSinceLast = ($now->getTimestamp() - $aBlockData['time']);
} else {
$dTimeSinceLast = 0;
}
// Propagate content our template
$smarty->assign("ESTTIME", $iEstTime);
$smarty->assign("TIMESINCELAST", $dTimeSinceLast);
$smarty->assign("BLOCKSFOUND", $aBlocksFoundData);
$smarty->assign("BLOCKLIMIT", $iLimit);
$smarty->assign("CONTRIBSHARES", $aContributorsShares);
$smarty->assign("CONTRIBHASHES", $aContributorsHashes);
$smarty->assign("CURRENTBLOCK", $iBlock);
count($aBlockData) > 0 ? $smarty->assign("LASTBLOCK", $aBlockData['height']) : $smarty->assign("LASTBLOCK", 0);
$smarty->assign("DIFFICULTY", $dDifficulty);
$smarty->assign("REWARD", $config['reward']);
if ($user->isAuthenticated()) {
// Public / private page detection
if ($config['website']['acl']['statistics']['pool'] == 'public') {
$smarty->assign("CONTENT", "authenticated.tpl");
} else if ($user->isAuthenticated() && $config['website']['acl']['statistics']['pool'] == 'private') {
$smarty->assign("CONTENT", "authenticated.tpl");
} else {
$smarty->assign("CONTENT", "../default.tpl");

View File

@ -18,8 +18,14 @@ $smarty = new Smarty;
$debug->append('Define Smarty Paths', 3);
$smarty->template_dir = BASEPATH . 'templates/' . THEME . '/';
$smarty->compile_dir = BASEPATH . 'templates/compile/';
$smarty_cache_key = md5(serialize($_REQUEST) . serialize(@$_SESSION['USERDATA']['id']));
// Optional smarty caching, check Smarty documentation for details
$smarty->caching = $config['cache'];
$smarty->cache_dir = BASEPATH . "templates/cache";
if ($config['smarty']['cache']) {
$debug->append('Enable smarty cache');
$smarty->setCaching(Smarty::CACHING_LIFETIME_SAVED);
$smarty->cache_lifetime = $config['smarty']['cache_lifetime'];
$smarty->cache_dir = BASEPATH . "templates/cache";
$smarty->use_sub_dirs = true;
}
?>

View File

@ -1,12 +1,12 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY'))
die('Hacking attempt');
if (!defined('SECURITY')) die('Hacking attempt');
// Globally available variables
$debug->append('Global smarty variables', 3);
$debug->append('No cached page detected, loading smarty globals', 3);
// Defaults to get rid of PHP Notice warnings
$dDifficulty = 1;
$aRoundShares = 1;
@ -24,7 +24,7 @@ if (@$_SESSION['AUTHENTICATED']) {
$bitcoin->can_connect() === true ? $dNetworkHashrate = $bitcoin->query('getnetworkhashps') : $dNetworkHashrate = 0;
// Fetch some data
$iCurrentActiveWorkers = $worker->getCountAllActiveWorkers();
if (!$iCurrentActiveWorkers = $worker->getCountAllActiveWorkers()) $iCurrentActiveWorkers = 0;
$iCurrentPoolHashrate = $statistics->getCurrentHashrate();
$iCurrentPoolShareRate = $statistics->getCurrentShareRate();
@ -47,7 +47,9 @@ $aGlobal = array(
'blockexplorer' => $config['blockexplorer'],
'chaininfo' => $config['chaininfo'],
'config' => array(
'website' => array( 'title' => $config['website']['title'] ),
'website' => $config['website'],
'accounts' => $config['accounts'],
'disable_invitations' => $setting->getValue('disable_invitations'),
'price' => array( 'currency' => $config['price']['currency'] ),
'targetdiff' => $config['difficulty'],
'currency' => $config['currency'],
@ -83,9 +85,7 @@ if (@$_SESSION['USERDATA']['id']) {
$aGlobal['userdata']['sharerate'] = $statistics->getUserSharerate($_SESSION['USERDATA']['id']);
switch ($config['payout_system']) {
case 'pps':
break;
default:
case 'prop' || 'pplns':
// Some estimations
if (@$aRoundShares['valid'] > 0) {
$aGlobal['userdata']['est_block'] = round(( (int)$aGlobal['userdata']['shares']['valid'] / (int)$aRoundShares['valid'] ) * (float)$config['reward'], 8);
@ -98,12 +98,20 @@ if (@$_SESSION['USERDATA']['id']) {
$aGlobal['userdata']['est_donation'] = 0;
$aGlobal['userdata']['est_payout'] = 0;
}
case 'pplns':
if ($iAvgBlockShares = round($block->getAvgBlockShares($config['pplns']['blockavg']['blockcount']))) {
$aGlobal['pplns']['target'] = $iAvgBlockShares;
} else {
$aGlobal['pplns']['target'] = $config['pplns']['shares']['default'];
}
break;
case 'pps':
break;
}
// Site-wide notifications, based on user events
if ($aGlobal['userdata']['balance']['confirmed'] >= $config['ap_threshold']['max'])
$_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded your accounts balance. Please transfer some ' . $config['currency'] . "!", 'TYPE' => 'errormsg');
$_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded the pools configured ' . $config['currency'] . ' warning threshold. Please initiate a transfer!', 'TYPE' => 'errormsg');
if ($user->getUserFailed($_SESSION['USERDATA']['id']) > 0)
$_SESSION['POPUP'][] = array('CONTENT' => 'You have ' . $user->getUserFailed($_SESSION['USERDATA']['id']) . ' failed login attempts! <a href="?page=account&action=reset_failed">Reset Counter</a>', 'TYPE' => 'errormsg');
}

View File

@ -24,13 +24,15 @@ define("BASEPATH", "./");
// Our security check
define("SECURITY", 1);
// Start a session
session_start();
$session_id = session_id();
// Include our configuration (holding defines for the requires)
if (!include_once(BASEPATH . 'include/config/global.inc.php')) die('Unable to load site configuration');
// Start a session
session_set_cookie_params(time()+$config['cookie']['duration'], $config['cookie']['path'], $config['cookie']['domain'], $config['cookie']['secure'], $config['cookie']['httponly']);
session_start();
setcookie(session_name(),session_id(),time()+$config['cookie']['duration'], $config['cookie']['path'], $config['cookie']['domain'], $config['cookie']['secure'], $config['cookie']['httponly']);
$session_id = session_id();
// Load Classes, they name defines the $ variable used
// We include all needed files here, even though our templates could load them themself
require_once(INCLUDE_DIR . '/autoloader.inc.php');
@ -82,7 +84,7 @@ $smarty->assign('DebuggerInfo', $debug->getDebugInfo());
// Display our page
if (!@$supress_master)
$smarty->display("master.tpl", md5(serialize($_REQUEST)));
$smarty->display("master.tpl", $smarty_cache_key);
// Unset any temporary values here
unset($_SESSION['POPUP']);

View File

@ -0,0 +1,11 @@
<html>
<body>
<p>Hello valued miner,</p><br />
<p>{$DATA.username} invited you to participate on this pool:
<p>http://{$smarty.server.SERVER_NAME}{$smarty.server.PHP_SELF}?page=register&token={$DATA.token}</p>
{if $DATA.message}<p>Personal message:</p><p>{$DATA.message}</p>{/if}
<p></p>
<p>Cheers,</p>
<p>Website Administration</p>
</body>
</html>

View File

@ -1,8 +1,8 @@
<html>
<body>
<p>Hello {$USERNAME},</p><br />
<p>Hello {$DATA.username},</p><br />
<p>You have requested a password reset through our online form. In order to complete the request please follow this link:</p>
<p>http://{$smarty.server.SERVER_NAME}{$smarty.server.PHP_SELF}?page=password&action=change&token={$TOKEN}</p>
<p>http://{$smarty.server.SERVER_NAME}{$smarty.server.PHP_SELF}?page=password&action=change&token={$DATA.token}</p>
<p>You will be asked to change your password. You can then use this new password to login to your account.</p>
<p>Cheers,</p>
<p>Website Administration</p>

View File

@ -0,0 +1,10 @@
<html>
<body>
<p>Hello {$DATA.username},</p><br />
<p>You have create a new account. In order to complete the registration process please follow this link:</p>
<p>http://{$smarty.server.SERVER_NAME}{$smarty.server.PHP_SELF}?page=account&action=confirm&token={$DATA.token}</p>
<p></p>
<p>Cheers,</p>
<p>Website Administration</p>
</body>
</html>

View File

@ -12,7 +12,7 @@
<tbody>
{section name=donor loop=$DONORS}
<tr>
<td>{$DONORS[donor].username}</td>
<td>{if $DONORS[donor].is_anonymous|default:"0" == 1}anonymous{else}{$DONORS[donor].username}{/if}</td>
<td class="center">{$DONORS[donor].donate_percent}</td>
<td class="right">{$DONORS[donor].donation|number_format:"2"}</td>
</tr>

View File

@ -0,0 +1 @@

View File

@ -4,13 +4,17 @@
<input type="hidden" name="action" value="{$smarty.request.action|escape}">
<input type="hidden" name="do" value="updateAccount">
<table>
<tbody><tr><td>Username: </td><td>{$GLOBAL.userdata.username}</td></tr>
<tbody><tr><td>Username: </td><td>{$GLOBAL.userdata.username|escape}</td></tr>
<tr><td>User Id: </td><td>{$GLOBAL.userdata.id}</td></tr>
<tr><td>API Key: </td><td><a href="{$smarty.server.PHP_SELF}?page=api&action=getuserstatus&api_key={$GLOBAL.userdata.api_key}&id={$GLOBAL.userdata.id}">{$GLOBAL.userdata.api_key}</a></td></tr>
<tr><td>E-Mail: </td><td><input type="text" name="email" value="{$GLOBAL.userdata.email|escape}" size="20"></td></tr>
<tr><td>Payment Address: </td><td><input type="text" name="paymentAddress" value="{$smarty.request.paymentAddress|default:$GLOBAL.userdata.coin_address|escape}" size="40"></td></tr>
<tr><td>Donation %: </td><td><input type="text" name="donatePercent" value="{$smarty.request.donatePercent|default:$GLOBAL.userdata.donate_percent|escape}" size="4"><font size="1"> [donation amount in percent (example: 0.5)]</font></td></tr>
{if !$GLOBAL.config.website.api.disabled}<tr><td>API Key: </td><td><a href="{$smarty.server.PHP_SELF}?page=api&action=getuserstatus&api_key={$GLOBAL.userdata.api_key}&id={$GLOBAL.userdata.id}">{$GLOBAL.userdata.api_key}</a></td></tr>{/if}
<tr><td>E-Mail: </td><td><input type="text" name="email" value="{nocache}{$GLOBAL.userdata.email|escape}{/nocache}" size="20"></td></tr>
<tr><td>Payment Address: </td><td><input type="text" name="paymentAddress" value="{nocache}{$smarty.request.paymentAddress|default:$GLOBAL.userdata.coin_address|escape}{nocache}" size="40"></td></tr>
<tr><td>Donation %: </td><td><input type="text" name="donatePercent" value="{nocache}{$smarty.request.donatePercent|default:$GLOBAL.userdata.donate_percent|escape}{nocache}" size="4"><font size="1"> [donation amount in percent (example: 0.5)]</font></td></tr>
<tr><td>Automatic Payout Threshold: </td><td valign="top"><input type="text" name="payoutThreshold" value="{$smarty.request.payoutThreshold|default:$GLOBAL.userdata.ap_threshold|escape}" size="5" maxlength="5"> <font size="1">[{$GLOBAL.config.ap_threshold.min}-{$GLOBAL.config.ap_threshold.max} {$GLOBAL.config.currency}. Set to '0' for no auto payout]</font></td></tr>
<tr><td>Anonymous Account <span id='tt'><img src='{$PATH}/images/questionmark.png' height='15px' width='15px' title='Will hide your username on the website for others. Only admins can still get your user information.'></span>:</td><td>
<input type="checkbox" name="is_anonymous" value="1" id="is_anonymous" {if $GLOBAL.userdata.is_anonymous}checked{/if} />
<label for="is_anonymous"></label>
</td></tr>
<tr><td>4 digit PIN: </td><td><input type="password" name="authPin" size="4" maxlength="4"><font size="1"> [The 4 digit PIN you chose when registering]</font></td></tr>
</tbody></table>
<input type="submit" class="submit long" value="Update Settings"></form>
@ -23,8 +27,8 @@
<input type="hidden" name="action" value="{$smarty.request.action|escape}">
<input type="hidden" name="do" value="cashOut">
<table>
<tbody><tr><td>Account Balance: &nbsp;&nbsp;&nbsp;</td><td>{$GLOBAL.userdata.balance.confirmed|escape} {$GLOBAL.config.currency}</td></tr>
<tr><td>Payout to: </td><td><h6>{$GLOBAL.userdata.coin_address|escape}</h6></td></tr>
<tbody><tr><td>Account Balance: &nbsp;&nbsp;&nbsp;</td><td>{nocache}{$GLOBAL.userdata.balance.confirmed|escape}{/nocache} {$GLOBAL.config.currency}</td></tr>
<tr><td>Payout to: </td><td><h6>{nocache}{$GLOBAL.userdata.coin_address|escape}{/nocache}</h6></td></tr>
<tr><td>4 digit PIN: </td><td><input type="password" name="authPin" size="4" maxlength="4"></td></tr>
</tbody></table>
<input type="submit" class="submit mid" value="Cash Out"></form>

View File

@ -0,0 +1,43 @@
{include file="global/block_header.tpl" ALIGN="left" BLOCK_HEADER="Invitations"}
<form action="{$smarty.server.PHP_SELF}" method="POST">
<input type="hidden" name="page" value="{$smarty.request.page}">
<input type="hidden" name="action" value="{$smarty.request.action}">
<input type="hidden" name="do" value="sendInvitation">
<table>
<tr>
<td>E-Mail</td>
<td><input type="text" name="data[email]" value="{$smarty.request.data.email|escape|default:""}" size="30" /></td>
</tr>
<tr>
<td>Message</td>
<td><textarea name="data[message]">{$smarty.request.data.message|escape|default:"Please accept my invitation to this awesome pool."}</textarea></td>
</tr>
<tr>
<td colspan="2" class="center">
<input type="submit" class="submit small" value="Send">
</td>
</tr>
</table>
</form>
{include file="global/block_footer.tpl"}
{include file="global/block_header.tpl" ALIGN="right" BLOCK_HEADER="Past Invitations"}
<table width="100%">
<thead style="font-size:13px;">
<tr>
<th>E-Mail</th>
<th class="center">Sent</th>
<th class="center">Activated</th>
</tr>
</thead>
<tbody>
{section name=invite loop=$INVITATIONS}
<tr>
<td>{$INVITATIONS[invite].email}</td>
<td class="center">{$INVITATIONS[invite].time|date_format:"%d/%m/%Y %H:%M:%S"}</td>
<td class="center"><img src="{$PATH}/images/{if $INVITATIONS[invite].is_activated}success{else}error{/if}.gif" /></td>
</tr>
{/section}
<tbody>
</table>
{include file="global/block_footer.tpl"}

View File

@ -12,7 +12,7 @@
<td class="left">IDLE Worker</td>
<td class="center">
<input type="hidden" name="data[idle_worker]" value="0" />
<input type="checkbox" name="data[idle_worker]" id="data[idle_worker]" value="1"{if $SETTINGS['idle_worker']}checked{/if} />
<input type="checkbox" name="data[idle_worker]" id="data[idle_worker]" value="1"{nocache}{if $SETTINGS['idle_worker']}checked{/if}{/nocache} />
<label for="data[idle_worker]"></label>
</td>
</tr>
@ -20,7 +20,7 @@
<td class="left">New Blocks</td>
<td class="center">
<input type="hidden" name="data[new_block]" value="0" />
<input type="checkbox" name="data[new_block]" id="data[new_block]" value="1"{if $SETTINGS['new_block']}checked{/if} />
<input type="checkbox" name="data[new_block]" id="data[new_block]" value="1"{nocache}{if $SETTINGS['new_block']}checked{/if}{/nocache} />
<label for="data[new_block]"></label>
</td>
</tr>
@ -28,7 +28,7 @@
<td class="left">Auto Payout</td>
<td class="center">
<input type="hidden" name="data[auto_payout]" value="0" />
<input type="checkbox" name="data[auto_payout]" id="data[auto_payout]" value="1"{if $SETTINGS['auto_payout']}checked{/if} />
<input type="checkbox" name="data[auto_payout]" id="data[auto_payout]" value="1"{nocache}{if $SETTINGS['auto_payout']}checked{/if}{/nocache} />
<label for="data[auto_payout]"></label>
</td>
</tr>
@ -36,7 +36,7 @@
<td class="left">Manual Payout</td>
<td class="center">
<input type="hidden" name="data[manual_payout]" value="0" />
<input type="checkbox" name="data[manual_payout]" id="data[manual_payout]" value="1"{if $SETTINGS['manual_payout']}checked{/if} />
<input type="checkbox" name="data[manual_payout]" id="data[manual_payout]" value="1"{nocache}{if $SETTINGS['manual_payout']}checked{/if}{/nocache} />
<label for="data[manual_payout]"></label>
</td>
</tr>

View File

@ -17,15 +17,9 @@
{assign var=has_confirmed value=false}
{section transaction $TRANSACTIONS}
{if (
(($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus')and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations)
or ($TRANSACTIONS[transaction].type == 'Donation' and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations)
or ($TRANSACTIONS[transaction].type == 'Fee' and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations)
or $TRANSACTIONS[transaction].type == 'Credit_PPS'
or $TRANSACTIONS[transaction].type == 'Fee_PPS'
or $TRANSACTIONS[transaction].type == 'Donation_PPS'
or $TRANSACTIONS[transaction].type == 'Debit_AP'
or $TRANSACTIONS[transaction].type == 'Debit_MP'
or $TRANSACTIONS[transaction].type == 'TXFee'
( ( $TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus' or $TRANSACTIONS[transaction].type == 'Donation' or $TRANSACTIONS[transaction].type == 'Fee' ) and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations )
or $TRANSACTIONS[transaction].type == 'Credit_PPS' or $TRANSACTIONS[transaction].type == 'Fee_PPS' or $TRANSACTIONS[transaction].type == 'Donation_PPS'
or $TRANSACTIONS[transaction].type == 'Debit_AP' or $TRANSACTIONS[transaction].type == 'Debit_MP' or $TRANSACTIONS[transaction].type == 'TXFee'
)}
{assign var=has_credits value=true}
<tr class="{cycle values="odd,even"}">
@ -67,11 +61,9 @@
<tbody style="font-size:12px;">
{assign var=has_unconfirmed value=false}
{section transaction $TRANSACTIONS}
{if (
($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus') and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations
or ($TRANSACTIONS[transaction].type == 'Donation' and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations)
or ($TRANSACTIONS[transaction].type == 'Fee' and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations)
)}
{if
(($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus' or $TRANSACTIONS[transaction].type == 'Donation' or $TRANSACTIONS[transaction].type == 'Fee') and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations and $TRANSACTIONS[transaction].confirmations >= 0)
}
{assign var=has_unconfirmed value=true}
<tr class="{cycle values="odd,even"}">
<td>{$TRANSACTIONS[transaction].id}</td>
@ -117,21 +109,16 @@
<tbody style="font-size:12px;">
{assign var=has_orphaned value=false}
{section transaction $TRANSACTIONS}
{if (
$TRANSACTIONS[transaction].type == 'Orphan_Credit'
or $TRANSACTIONS[transaction].type == 'Orphan_Donation'
or $TRANSACTIONS[transaction].type == 'Orphan_Fee'
or $TRANSACTIONS[transaction].type == 'Orphan_Bonus'
)}
{if ($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Fee' or $TRANSACTIONS[transaction].type == 'Donation' or $TRANSACTIONS[transaction].type == 'Bonus') and $TRANSACTIONS[transaction].confirmations == -1}
<tr class="{cycle values="odd,even"}">
<td>{$TRANSACTIONS[transaction].id}</td>
<td>{$TRANSACTIONS[transaction].timestamp}</td>
<td>{$TRANSACTIONS[transaction].type}</td>
<td>{$TRANSACTIONS[transaction].coin_address}</td>
<td>{if $TRANSACTIONS[transaction].height == 0}n/a{else}{$TRANSACTIONS[transaction].height}{/if}</td>
<td><font color="{if $TRANSACTIONS[transaction].type == 'Orphan_Credit' or $TRANSACTIONS[transaction].type == 'Orphan_Bonus'}green{else}red{/if}">{$TRANSACTIONS[transaction].amount|number_format:"8"}</td>
<td><font color="{if $TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus'}green{else}red{/if}">{$TRANSACTIONS[transaction].amount|number_format:"8"}</td>
</tr>
{if $TRANSACTIONS[transaction].type == 'Orphan_Credit' or $TRANSACTIONS[transaction].type == 'Orphan_Bonus'}
{if $TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus'}
{assign var="orphan_credits" value="`$orphan_credits|default:"0"+$TRANSACTIONS[transaction].amount`"}
{else}
{assign var="orphan_debits" value="`$orphan_debits|default:"0"+$TRANSACTIONS[transaction].amount`"}

View File

@ -15,6 +15,7 @@
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
{nocache}
{section worker $WORKERS}
{assign var="username" value="."|escape|explode:$WORKERS[worker].username:2}
<tr>
@ -29,6 +30,7 @@
<td align="right"><a href="{$smarty.server.PHP_SELF}?page={$smarty.request.page|escape}&action={$smarty.request.action|escape}&do=delete&id={$WORKERS[worker].id|escape}"><button style="padding:5px" type="button">Delete</button></a></td>
</tr>
{/section}
{/nocache}
</tbody>
</table>
<input type="submit" value="Update Workers" style="padding:5px">

View File

@ -0,0 +1,45 @@
{include file="global/block_header.tpl" BLOCK_HEADER="Monitoring"}
<table width="88%">
<thead>
<th>Cronjob</th>
<th>Exit Code</th>
<th>Active</th>
<th>Runtime</th>
<th>Start Time</th>
<th>End Time</th>
<th>Message</th>
</thead>
<tbody>
{foreach $CRONSTATUS as $cron=>$v}
<tr>
<td>{$cron}</td>
{foreach $v as $event}
<td>
{if $event.STATUS.type == 'okerror'}
{if $event.STATUS.value == 0}
<font color="green">OK</font>
{else}
<font color="red">ERROR</font>
{/if}
{else if $event.STATUS.type == 'message'}
<i>{$event.STATUS.value}</i>
{else if $event.STATUS.type == 'yesno'}
{if $event.STATUS.value == 1}
Yes
{else}
No
{/if}
{else if $event.STATUS.type == 'time'}
{$event.STATUS.value|default:"0"|number_format:"2"} seconds
{else if $event.STATUS.type == 'date'}
{$event.STATUS.value|date_format:"%m/%d %H:%M:%S"}
{else}
{$event.STATUS.value|default:""}
{/if}
</td>
{/foreach}
</tr>
{/foreach}
</tbody>
</table>
{include file="global/block_footer.tpl"}

View File

@ -17,6 +17,7 @@
</table>
</form>
{include file="global/block_footer.tpl"}
{nocache}
{section name=news loop=$NEWS}
{include
file="global/block_header.tpl"
@ -35,4 +36,5 @@
</form>
{include file="global/block_footer.tpl"}
{/section}
{/nocache}
{include file="global/block_footer.tpl"}

View File

@ -10,7 +10,7 @@
Active
</th>
<td>
<input type="checkbox" name="active" value="1" id="active" {if $NEWS.active}checked{/if} />
<input type="checkbox" name="active" value="1" id="active" {nocache}{if $NEWS.active}checked{/if}{/nocache} />
<label for="active"></label>
</td>
</tr>
@ -18,13 +18,13 @@
<th>
Header
</th>
<td><input name="header" type="text" size="30" value="{$NEWS.header}" required /></td>
<td><input name="header" type="text" size="30" value="{nocache}{$NEWS.header}{/nocache}" required /></td>
</tr>
<tr>
<th>
Content
</th>
<td><textarea name="content" rows="500" type="text" required>{$NEWS.content}</textarea></td>
<td><textarea name="content" rows="500" type="text" required>{nocache}{$NEWS.content}{/nocache}</textarea></td>
</tr>
</table>
<input type="submit" value="Save" class="submit small" />

View File

@ -16,7 +16,7 @@
<td>
<select name="data[maintenance]">
<option value="1">Yes</option>
<option value="0"{if !$MAINTENANCE} selected{/if}>No</option>
<option value="0"{nocache}{if !$MAINTENANCE} selected{/if}>{/nocache}No</option>
</select>
</td>
</tr>
@ -26,7 +26,17 @@
<td>
<select name="data[lock_registration]">
<option value="1">Yes</option>
<option value="0"{if !$LOCKREGISTRATION} selected{/if}>No</option>
<option value="0"{nocache}{if !$LOCKREGISTRATION} selected{/if}{/nocache}>No</option>
</select>
</td>
</tr>
<tr>
<td class="left">Disable Invitations</td>
<td class="center"><span id='tt'><img src='{$PATH}/images/questionmark.png' height='15px' width='15px' title='Enable or disable users to invite others. Configuration file defines number of allowed invitations.'></span></td>
<td>
<select name="data[disable_invitations]">
<option value="1">Yes</option>
<option value="0"{nocache}{if !$DISABLEINVITATIONS} selected{/if}{/nocache}>No</option>
</select>
</td>
</tr>

View File

@ -21,15 +21,9 @@
{assign var=confirmed value=0}
{section transaction $TRANSACTIONS}
{if (
(($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus')and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations)
or ($TRANSACTIONS[transaction].type == 'Donation' and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations)
or ($TRANSACTIONS[transaction].type == 'Fee' and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations)
or $TRANSACTIONS[transaction].type == 'Credit_PPS'
or $TRANSACTIONS[transaction].type == 'Fee_PPS'
or $TRANSACTIONS[transaction].type == 'Donation_PPS'
or $TRANSACTIONS[transaction].type == 'Debit_AP'
or $TRANSACTIONS[transaction].type == 'Debit_MP'
or $TRANSACTIONS[transaction].type == 'TXFee'
( ( $TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus' or $TRANSACTIONS[transaction].type == 'Donation' or $TRANSACTIONS[transaction].type == 'Fee' ) and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations )
or $TRANSACTIONS[transaction].type == 'Credit_PPS' or $TRANSACTIONS[transaction].type == 'Fee_PPS' or $TRANSACTIONS[transaction].type == 'Donation_PPS'
or $TRANSACTIONS[transaction].type == 'Debit_AP' or $TRANSACTIONS[transaction].type == 'Debit_MP' or $TRANSACTIONS[transaction].type == 'TXFee'
)}
{assign var=confirmed value=1}
<tr class="{cycle values="odd,even"}">
@ -74,11 +68,7 @@
<tbody style="font-size:12px;">
{assign var=unconfirmed value=0}
{section transaction $TRANSACTIONS}
{if (
($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus') and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations
or ($TRANSACTIONS[transaction].type == 'Donation' and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations)
or ($TRANSACTIONS[transaction].type == 'Fee' and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations)
)}
{if ($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus' or $TRANSACTIONS[transaction].type == 'Donation' or $TRANSACTIONS[transaction].type == 'Fee') and $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations and $TRANSACTIONS[transaction].confirmations >= 0}
{assign var=unconfirmed value=1}
<tr class="{cycle values="odd,even"}">
<td>{$TRANSACTIONS[transaction].id}</td>
@ -118,12 +108,7 @@
<tbody style="font-size:12px;">
{assign var=orphaned value=0}
{section transaction $TRANSACTIONS}
{if (
$TRANSACTIONS[transaction].type == 'Orphan_Credit'
or $TRANSACTIONS[transaction].type == 'Orphan_Donation'
or $TRANSACTIONS[transaction].type == 'Orphan_Fee'
or $TRANSACTIONS[transaction].type == 'Orphan_Bonus'
)}
{if ($TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Fee' or $TRANSACTIONS[transaction].type == 'Donation' or $TRANSACTIONS[transaction].type == 'Bonus') and $TRANSACTIONS[transaction].confirmations == -1}
{assign var=orphaned value=1}
<tr class="{cycle values="odd,even"}">
<td>{$TRANSACTIONS[transaction].id}</td>
@ -132,7 +117,7 @@
<td>{$TRANSACTIONS[transaction].type}</td>
<td>{$TRANSACTIONS[transaction].coin_address}</td>
<td>{if $TRANSACTIONS[transaction].height == 0}n/a{else}{$TRANSACTIONS[transaction].height}{/if}</td>
<td><font color="{if $TRANSACTIONS[transaction].type == 'Orphan_Credit' or $TRANSACTIONS[transaction].type == 'Orphan_Bonus'}green{else}red{/if}">{$TRANSACTIONS[transaction].amount|number_format:"8"}</td>
<td><font color="{if $TRANSACTIONS[transaction].type == 'Credit' or $TRANSACTIONS[transaction].type == 'Bonus'}green{else}red{/if}">{$TRANSACTIONS[transaction].amount|number_format:"8"}</td>
</tr>
{/if}
{/section}

View File

@ -44,11 +44,12 @@
</tr>
</thead>
<tbody>
{nocache}
{section name=user loop=$USERS|default}
<tr>
<td class="center">{$USERS[user].id}</td>
<td>{$USERS[user].username}</td>
<td>{$USERS[user].email}</td>
<td>{$USERS[user].username|escape}</td>
<td>{$USERS[user].email|escape}</td>
<td class="right">{$USERS[user].shares}</td>
<td class="right">{$USERS[user].hashrate}</td>
<td class="right">{$USERS[user].payout.est_donation|number_format:"8"}</td>
@ -70,6 +71,7 @@
<td colspan="10"></td>
</tr>
{/section}
{/nocache}
</tbody>
<tfoot>
<tr>

View File

View File

@ -4,11 +4,11 @@
<div id="ministats">
<table border="0">
<tr>
{if $GLOBAL.config.price.currency}<td><li>{$GLOBAL.config.currency}/{$GLOBAL.config.price.currency}: {$GLOBAL.price|default:"n/a"|number_format:"4"}&nbsp;&nbsp;&nbsp;&nbsp;</li></td>{/if}
{if $GLOBAL.config.price.currency}<td><li>{$GLOBAL.config.currency}/{$GLOBAL.config.price.currency}: {$GLOBAL.price|default:"0"|number_format:"4"}&nbsp;&nbsp;&nbsp;&nbsp;</li></td>{/if}
<td><li>Network Hashrate: {($GLOBAL.nethashrate / 1000 / 1000 )|default:"0"|number_format:"3"} MH/s&nbsp;&nbsp;&nbsp;&nbsp;</li></td>
<td><li>Pool Hashrate: {($GLOBAL.hashrate / 1000)|number_format:"3"} MH/s&nbsp;&nbsp;&nbsp;&nbsp;</li></td>
<td><li>Pool Sharerate: {$GLOBAL.sharerate|number_format:"2"} Shares/s&nbsp;&nbsp;&nbsp;&nbsp;</li></td>
<td><li>Pool Workers: {$GLOBAL.workers}&nbsp;&nbsp;&nbsp;&nbsp;</li></td>
<td><li>Pool Workers: {$GLOBAL.workers|default:"0"}&nbsp;&nbsp;&nbsp;&nbsp;</li></td>
</tr>
</table>
</div>

View File

@ -7,12 +7,14 @@
<li><a href="{$smarty.server.PHP_SELF}?page=account&action=workers">My Workers</a></li>
<li><a href="{$smarty.server.PHP_SELF}?page=account&action=transactions">Transactions</a></li>
<li><a href="{$smarty.server.PHP_SELF}?page=account&action=notifications">Notifications</a></li>
{if !$GLOBAL.config.disable_invitations}<li><a href="{$smarty.server.PHP_SELF}?page=account&action=invitations">Invitations</a></li>{/if}
</ul>
</li>
{/if}
{if $smarty.session.AUTHENTICATED|default:"0" == 1 && $GLOBAL.userdata.is_admin == 1}
<li><a href="{$smarty.server.PHP_SELF}?page=admin">Admin Panel</a>
<ul>
<li><a href="{$smarty.server.PHP_SELF}?page=admin&action=monitoring">Monitoring</a></li>
<li><a href="{$smarty.server.PHP_SELF}?page=admin&action=user">User Info</a></li>
<li><a href="{$smarty.server.PHP_SELF}?page=admin&action=wallet">Wallet Info</a></li>
<li><a href="{$smarty.server.PHP_SELF}?page=admin&action=transactions">Transactions</a></li>
@ -31,13 +33,21 @@
</li>
{else}
<li><a href="{$smarty.server.PHP_SELF}?page=statistics">Statistics</a>
<ul>
{if $GLOBAL.config.website.acl.statistics.pool == 'public'}
<li><a href="{$smarty.server.PHP_SELF}?page=statistics&action=pool">Pool Stats</a></li>
{/if}
{if $GLOBAL.config.website.acl.statistics.blocks == 'public'}
<li><a href="{$smarty.server.PHP_SELF}?page=statistics&action=blocks">Block Stats</a></li>
{/if}
</ul>
{/if}
<li><a href="{$smarty.server.PHP_SELF}?page=gettingstarted">Getting Started</a></li>
<li><a href="{$smarty.server.PHP_SELF}?page=support">Support</a></li>
<li><a href="{$smarty.server.PHP_SELF}?page=about&action=pool">About</a>
<ul>
<li><a href="{$smarty.server.PHP_SELF}?page=about&action=pool">This Pool</a></li>
<li><a href="{$smarty.server.PHP_SELF}?page=about&action=api">API Reference</a></li>
{if !$GLOBAL.config.website.api.disabled}<li><a href="{$smarty.server.PHP_SELF}?page=about&action=api">API Reference</a></li>{/if}
<li><a href="{$smarty.server.PHP_SELF}?page=about&action=donors">Pool Donors</a></li>
</ul>
</li>

View File

@ -0,0 +1,75 @@
<div class="block" style="clear:none; margin-top:15px; margin-left:13px;">
<div class="block_head">
<div class="bheadl"></div>
<div class="bheadr"></div>
<h1>Dashboard</h1>
</div>
<div class="block_content" style="padding-top:10px;">
<table class="sidebar" style="width: 196px">
<tr>
<td><b>PPLNS Target</b></td>
<td class="right">{$GLOBAL.pplns.target|number_format}</td>
</tr>
<tr><td colspan="2">&nbsp;</td></tr>
<tr>
<td colspan="2"><b><u>Your Stats</u></b></td>
</tr>
<tr>
<td><b>Hashrate</b></td>
<td class="right">{$GLOBAL.userdata.hashrate|number_format} KH/s</td>
</tr>
<tr>
<td colspan="2"><b><u>Unpaid Shares</u></b> <span id='tt'><img src='{$PATH}/images/questionmark.png' height='15px' width='15px' title='Submitted shares between the last 120 confirms block until now.'></span></td>
</tr>
<tr>
<td><b>Your Valid<b></td>
<td class="right"><i>{$GLOBAL.userdata.shares.valid|number_format}</i><font size='1px'></font></b></td>
</tr>
<tr>
<td><b>Pool Valid</td>
<td class="right"><i>{$GLOBAL.roundshares.valid|number_format}</i> <font size='1px'></font></b></td>
</tr>
<tr>
<td colspan="2"><b><u>Round Shares</u></b> <span id='tt'><img src='{$PATH}/images/questionmark.png' height='15px' width='15px' title='Submitted shares since last found block (ie. round shares)'></span></td>
</tr>
<tr>
<td><b>Pool Valid</b></td>
<td class="right"><i>{$GLOBAL.roundshares.valid|number_format}</i></td>
</tr>
<tr>
<td><b>Pool Invalid</b></td>
<td class="right"><i>{$GLOBAL.roundshares.invalid|number_format}</i>{if $GLOBAL.roundshares.valid > 0}<font size='1px'> ({(100 / $GLOBAL.roundshares.valid * $GLOBAL.roundshares.invalid)|number_format:"2"}%)</font>{/if}</td>
</tr>
<tr>
<td><b>Your Invalid</b></td>
<td class="right"><i>{$GLOBAL.userdata.shares.invalid|number_format}</i>{if $GLOBAL.roundshares.valid > 0}<font size='1px'> ({(100 / $GLOBAL.roundshares.valid * $GLOBAL.userdata.shares.invalid)|number_format:"2"}%)</font>{/if}</td>
</tr>
<tr>
<td colspan="2"><b><u>{$GLOBAL.config.currency} Round Estimate</u></b></td>
</tr>
<tr>
<td><b>Block</b></td>
<td class="right">{$GLOBAL.userdata.est_block|number_format:"3"}</td>
</tr>
<tr>
<td><b>Fees</b></td>
<td class="right">{$GLOBAL.userdata.est_fee|number_format:"3"}</td>
</tr>
<tr>
<td><b>Donation</b></td>
<td class="right">{$GLOBAL.userdata.est_donation|number_format:"3"}</td>
</tr>
<tr>
<td><b>Payout</b></td>
<td class="right">{$GLOBAL.userdata.est_payout|number_format:"3"}</td>
</tr>
<tr><td colspan="2">&nbsp;</td></tr>
<tr><td colspan="2"><b><u>{$GLOBAL.config.currency} Account Balance</u></b></td></tr>
<tr><td>Confirmed</td><td class="right"><b>{$GLOBAL.userdata.balance.confirmed|default:"0"}</td></tr>
<tr><td>Unconfirmed</td><td class="right"><b>{$GLOBAL.userdata.balance.unconfirmed|default:"0"}</td></tr>
<tr><td>Orphaned</td><td class="right"><b>{$GLOBAL.userdata.balance.orphaned|default:"0"}</td></tr>
</table>
</div>
<div class="bendl"></div>
<div class="bendr"></div>
</div>

View File

@ -62,6 +62,7 @@
<tr><td colspan="2"><b><u>{$GLOBAL.config.currency} Account Balance</u></b></td></tr>
<tr><td>Confirmed</td><td class="right"><b>{$GLOBAL.userdata.balance.confirmed|default:"0"}</td></tr>
<tr><td>Unconfirmed</td><td class="right"><b>{$GLOBAL.userdata.balance.unconfirmed|default:"0"}</td></tr>
<tr><td>Orphaned</td><td class="right"><b>{$GLOBAL.userdata.balance.orphaned|default:"0"}</td></tr>
</table>
</div>
<div class="bendl"></div>

View File

@ -1,5 +1,5 @@
{if $GLOBAL.userdata.username|default}
<h2>Welcome, {$smarty.session.USERDATA.username} <font size='1px'><b>Active Account</b>: <b>{$GLOBAL.fees}%</b> Pool Fee</font> <font size='1px'><i>(You are <a href='{$smarty.server.PHP_SELF}?page=account&action=edit'>donating</a> <b></i>{$GLOBAL.userdata.donate_percent}%</b> <i>of your earnings)</i></font></h2>
<h2>Welcome, {$smarty.session.USERDATA.username|escape} <font size='1px'><b>{if $GLOBAL.userdata.is_anonymous}Anonymous{else}Active{/if} Account</b>: <b>{$GLOBAL.fees|escape}%</b> Pool Fee</font> <font size='1px'><i>(You are <a href='{$smarty.server.PHP_SELF}?page=account&action=edit'>donating</a> <b></i>{$GLOBAL.userdata.donate_percent|escape}%</b> <i>of your earnings)</i></font></h2>
{else}
<h2>Welcome guest, <font size="1px"> please <a href="{$smarty.server.PHP_SELF}?page=register">register</a> to user this pool.</font></h2>
{/if}

View File

@ -69,7 +69,7 @@
</div>
</div>
<div id="debug">
{include file="system/debugger.tpl"}
{nocache}{include file="system/debugger.tpl"}{/nocache}
</div>
</div>
</body>

View File

@ -3,7 +3,7 @@
<input type="hidden" name="token" value="{$smarty.request.token|escape}">
<input type="hidden" name="page" value="{$smarty.request.page|escape}">
<input type="hidden" name="action" value="{$smarty.request.action|escape}">
<input type="hidden" name="do" value="useToken">
<input type="hidden" name="do" value="resetPassword">
<table>
<tr><td>New Password: </td><td><input type="password" name="newPassword"></td></tr>
<tr><td>New Password Repeat: </td><td><input type="password" name="newPassword2"></td></tr>

View File

@ -1,6 +1,9 @@
{include file="global/block_header.tpl" BLOCK_HEADER="Join our pool" BLOCK_STYLE="clear:none;"}
<form action="{$smarty.server.PHP_SELF}" method="post">
<input type="hidden" name="page" value="{$smarty.request.page|escape}">
{if $smarty.request.token|default:""}
<input type="hidden" name="token" value="{$smarty.request.token|escape}" />
{/if}
<input type="hidden" name="action" value="register">
<table width="90%" border="0">
<tbody>

View File

@ -57,18 +57,18 @@ target and network difficulty and assuming a zero variance scenario.
{else if $BLOCKSFOUND[block].confirmations == -1}
<font color="red">Orphan</font>
{else}{$GLOBAL.confirmations - $BLOCKSFOUND[block].confirmations} left{/if}</td>
<td>{$BLOCKSFOUND[block].finder|default:"unknown"}</td>
<td>{if $BLOCKSFOUND[block].is_anonymous|default:"0" == 1}anonymous{else}{$BLOCKSFOUND[block].finder|default:"unknown"|escape}{/if}</td>
<td class="center">{$BLOCKSFOUND[block].time|date_format:"%d/%m %H:%M:%S"}</td>
<td class="right">{$BLOCKSFOUND[block].difficulty|number_format:"2"}</td>
<td class="right">{$BLOCKSFOUND[block].amount|number_format:"2"}</td>
<td class="right">
{math assign="estshares" equation="(pow(2,32 - targetdiff) * blockdiff)" targetdiff=$GLOBAL.config.targetdiff blockdiff=$BLOCKSFOUND[block].difficulty}
{$estshares}
{$estshares|number_format}
</td>
<td class="right">{$BLOCKSFOUND[block].shares|number_format}</td>
<td class="right">
{math assign="percentage" equation="shares / estshares * 100" shares=$BLOCKSFOUND[block].shares estshares=$estshares}
<font color="{if ($percentage <= 100)}green{else}red{/if}">{$percentage}</font>
<font color="{if ($percentage <= 100)}green{else}red{/if}">{$percentage|number_format:"2"}</font>
</td>
</tr>
{/section}

View File

@ -14,7 +14,7 @@
{section block $BLOCKSFOUND}
<tr class="{cycle values="odd,even"}">
<td class="center"><a href="{$GLOBAL.blockexplorer}{$BLOCKSFOUND[block].height}" target="_blank">{$BLOCKSFOUND[block].height}</a></td>
<td>{$BLOCKSFOUND[block].finder|default:"unknown"}</td>
<td>{if $BLOCKSFOUND[block].is_anonymous|default:"0" == 1}anonymous{else}{$BLOCKSFOUND[block].finder|default:"unknown"|escape}{/if}</td>
<td class="center">{$BLOCKSFOUND[block].time|date_format:"%d/%m %H:%M:%S"}</td>
<td class="right">{$BLOCKSFOUND[block].shares|number_format}</td>
</tr>

View File

@ -19,5 +19,5 @@
</tr>
</tbody>
</table>
<li>These stats are also available in JSON format <a href="{$smarty.server.PHP_SELF}?page=api&action=public" target="_api">HERE</a></li>
{if !$GLOBAL.config.website.api.disabled}<li>These stats are also available in JSON format <a href="{$smarty.server.PHP_SELF}?page=api&action=public" target="_api">HERE</a></li>{/if}
{include file="global/block_footer.tpl"}

View File

@ -1,7 +1,6 @@
{if is_array($YOURHASHRATES) && is_array($POOLHASHRATES)}
<div class="block_content tab_content" id="both" style="padding-left:30px;">
{foreach from=array('area','pie') item=chartType}
<table width="60%" class="stats" rel="{$chartType}">
<table width="60%" class="stats" rel="area">
<caption>Your vs Pool Hashrate</caption>
<thead>
<tr>
@ -30,14 +29,11 @@
<td>{$POOLHASHRATES.$i|default:"0"}</td>
{/for}
{for $i=0 to date('G', time() - 60 * 60)}
<td>{$POOLHASHRATES.$i|default:"0"}</td>
<td>{$POOLHASHRATES.$i - $YOURHASHRATES.$i|default:"0"}</td>
{/for}
</tr>
</tbody>
</table>
<br />
{/foreach}
</div>
{else}
<p><li>No shares available to start calculations</li></p>
{/if}

View File

@ -26,6 +26,4 @@
</tbody>
</table>
</div>
{else}
<p><li>No shares available to start calculations</li></p>
{/if}

View File

@ -26,6 +26,4 @@
</tbody>
</table>
</div>
{else}
<p><li>No shares available to start calculations</li></p>
{/if}

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