commit
d3266c3ff7
36
README.md
36
README.md
@ -3,7 +3,7 @@ Description
|
||||
|
||||
mmcFE-ng is a web frontend for Pooled LTC Mining.
|
||||
|
||||
This is based on mmcFE, the original work by Greedi:
|
||||
The web frontend layout is based on mmcFE, the original work by Greedi:
|
||||
https://github.com/Greedi/mmcFE
|
||||
|
||||
After working a few days trying to learn to run my own pool and the
|
||||
@ -12,6 +12,10 @@ understand how it works. While doing so I also migrated the existing
|
||||
code over to my own framework so maintenance would be easier in the
|
||||
future.
|
||||
|
||||
**NOTE**: This project is still under development and commits are happening on a daily basis.
|
||||
I do not recommend using this for a live setup as of yet. Wait for the later Release Candidate
|
||||
if you wish to run your pool with it. Testing pools are much appreciated though!
|
||||
|
||||
Requirements
|
||||
============
|
||||
|
||||
@ -22,7 +26,7 @@ in the appropriate forums.
|
||||
|
||||
* Apache2
|
||||
* libapache2-mod-php5
|
||||
* PHP 5.4+ (5.3 might work too)
|
||||
* PHP 5.4+
|
||||
* php5-mysqlnd
|
||||
* php5-memcached
|
||||
* MySQL Server
|
||||
@ -31,6 +35,24 @@ in the appropriate forums.
|
||||
* pushpoold
|
||||
* litecoind
|
||||
|
||||
Features
|
||||
========
|
||||
|
||||
The following feature have been implemented so far:
|
||||
|
||||
* Use of memcache for statistics instead of a cronjob
|
||||
* Web User accounts
|
||||
* Worker accounts
|
||||
* Worker activity (live, past 10 minutes)
|
||||
* Worker hashrates (live, past 10 minutes)
|
||||
* Pool statistics
|
||||
* Minimal Block statistics
|
||||
* Pool donations
|
||||
* Pool fees
|
||||
* Manual payout with 0.1 LTC fee
|
||||
* Auto payout with 0.1 LTC fee
|
||||
* Transaction list (confirmed and unconfirmed)
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
@ -76,7 +98,8 @@ Memcache
|
||||
|
||||
Please install and start a default memcache instance. Not only would you
|
||||
need one for `pushpoold` but the statistics page is storing data in
|
||||
`memcache` as well to improve performance.
|
||||
`memcache` as well to improve performance. Your memcache can be
|
||||
configured in the global configuration file (see below).
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
@ -123,8 +146,9 @@ me know by creating an [Issue][1] marked as `Feature Request`.
|
||||
Disclaimer
|
||||
==========
|
||||
|
||||
This is a *WIP Project*. Most functionality is now added, the core
|
||||
features are available and the backend cronjobs are working. If you
|
||||
encounter any problems related to the code please create a new [Issue][1]
|
||||
This is a **WIP Project**. Most functionality is now added, the core
|
||||
features are available and the backend cronjobs are working. I would not recommend
|
||||
running this on a live pool yet. You can play around and test basic functionality but
|
||||
wait for any live deployment for at least a stable Release Candidate.
|
||||
|
||||
[1]: https://github.com/TheSerapher/php-mmcfe-ng/issues "Issue"
|
||||
|
||||
71
cronjobs/auto_payout.php
Executable file
71
cronjobs/auto_payout.php
Executable file
@ -0,0 +1,71 @@
|
||||
#!/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 ($bitcoin->can_connect() !== true) {
|
||||
verbose("Unable to connect to RPC server, exiting");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Fetch all users with setup AP
|
||||
$users = $user->getAllAutoPayout();
|
||||
|
||||
// Go through users and run transactions
|
||||
if (! empty($users)) {
|
||||
verbose("UserID\tUsername\tBalance\tThreshold\tAddress\t\t\t\t\tStatus\n\n");
|
||||
|
||||
foreach ($users as $aUserData) {
|
||||
$dBalance = $transaction->getBalance($aUserData['id']);
|
||||
verbose($aUserData['id'] . "\t" . $aUserData['username'] . "\t" . $dBalance . "\t" . $aUserData['ap_threshold'] . "\t\t" . $aUserData['coin_address'] . "\t");
|
||||
|
||||
// Only run if balance meets threshold and can pay the transaction fee
|
||||
if ($dBalance > $aUserData['ap_threshold'] && $dBalance > 0.1) {
|
||||
// Validate address against RPC
|
||||
try {
|
||||
$bitcoin->validateaddress($aUserData['coin_address']);
|
||||
} catch (BitcoinClientException $e) {
|
||||
verbose("VERIFY FAILED\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Send balance - 0.1 Fee to address
|
||||
try {
|
||||
$bitcoin->sendtoaddress($aUserData['coin_address'], $dBalance - 0.1);
|
||||
} catch (BitcoinClientException $e) {
|
||||
verbose("SEND FAILED\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create transaction record
|
||||
if ($transaction->addTransaction($aUserData['id'], $dBalance, 'Debit_AP', NULL, $aUserData['coin_address'], 0.1)) {
|
||||
verbose("OK\n");
|
||||
} else {
|
||||
verbose("FAILED\n");
|
||||
}
|
||||
} else {
|
||||
verbose("SKIPPED\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
verbose("No user has configured their AP > 0\n");
|
||||
}
|
||||
@ -45,7 +45,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
|
||||
}
|
||||
$aAccountShares = $share->getSharesForAccounts($share->getLastUpstreamId(), $iCurrentUpstreamId);
|
||||
$iRoundShares = $share->getRoundShares($share->getLastUpstreamId(), $iCurrentUpstreamId);
|
||||
verbose("ID\tHeight\tTime\t\tShares\tFinder\t\tShare ID\tPrev Share\tStatus\n");
|
||||
verbose("ID\tHeight\tTime\t\tShares\tFinder\t\tShare ID\tPrev Share\t\tStatus\n");
|
||||
verbose($aBlock['id'] . "\t" . $aBlock['height'] . "\t" . $aBlock['time'] . "\t" . $iRoundShares . "\t" . $share->getUpstreamFinder() . "\t" . $share->getUpstreamId() . "\t\t" . $share->getLastUpstreamId());
|
||||
if (empty($aAccountShares)) {
|
||||
verbose("\nNo shares found for this block\n\n");
|
||||
@ -58,24 +58,45 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
|
||||
if (!$block->setShares($aBlock['id'], $iRoundShares))
|
||||
$strStatus = "Shares Failed";
|
||||
verbose("\t\t$strStatus\n\n");
|
||||
verbose("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tStatus\n");
|
||||
verbose("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee\t\tStatus\n");
|
||||
foreach ($aAccountShares as $key => $aData) {
|
||||
// Payout based on shares, PPS system
|
||||
$aData['percentage'] = number_format(round(( 100 / $iRoundShares ) * $aData['valid'], 8), 8);
|
||||
$aData['payout'] = number_format(round(( $aData['percentage'] / 100 ) * $config['reward'], 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
|
||||
verbose($aData['id'] . "\t" .
|
||||
$aData['username'] . "\t" .
|
||||
$aData['valid'] . "\t" .
|
||||
$aData['invalid'] . "\t" .
|
||||
$aData['percentage'] . "\t" .
|
||||
$aData['payout'] . "\t");
|
||||
$aData['payout'] . "\t" .
|
||||
$aData['donation'] . "\t" .
|
||||
$aData['fee'] . "\t");
|
||||
|
||||
// Do all database updates for block, statistics and payouts
|
||||
$strStatus = "OK";
|
||||
// Update user share statistics
|
||||
if (!$statistics->updateShareStatistics($aData, $aBlock['id']))
|
||||
$strStatus = "Stats Failed";
|
||||
// Add new credit transaction
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit', $aBlock['id']))
|
||||
$strStatus = "Transaction Failed";
|
||||
verbose("$strStatus\n");
|
||||
// Add new donation debit
|
||||
if ($aData['donation'] > 0)
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation', $aBlock['id']))
|
||||
$strStatus = "Donation Failed";
|
||||
if ($aData['fee'] > 0 && $config['fees'] > 0)
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee', $aBlock['id']))
|
||||
$strStatus = "Fee Failed";
|
||||
verbose("\t$strStatus\n");
|
||||
}
|
||||
verbose("------------------------------------------------------------------------\n\n");
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
require_once(CLASS_DIR . '/debug.class.php');
|
||||
require_once(CLASS_DIR . '/bitcoin.class.php');
|
||||
require_once(CLASS_DIR . '/statscache.class.php');
|
||||
require_once(INCLUDE_DIR . '/database.inc.php');
|
||||
require_once(INCLUDE_DIR . '/smarty.inc.php');
|
||||
// Load classes that need the above as dependencies
|
||||
@ -12,7 +13,3 @@ 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 . '/settings.class.php');
|
||||
|
||||
// Use Memcache to store our data
|
||||
$memcache = new Memcached();
|
||||
$memcache->addServer('localhost', 11211);
|
||||
|
||||
@ -66,7 +66,7 @@ class Debug {
|
||||
}
|
||||
return $backtrace;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* We fill our data array here
|
||||
* @param string $msg Debug Message
|
||||
|
||||
@ -4,21 +4,29 @@
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
|
||||
/*
|
||||
* We give access to plenty of statistics through this class
|
||||
* Statistics should be non-intrusive and not change any
|
||||
* rows in our database to ensure data integrity for the backend
|
||||
**/
|
||||
class Statistics {
|
||||
private $sError = '';
|
||||
private $table = 'statistics_shares';
|
||||
|
||||
public function __construct($debug, $mysqli, $config, $share, $user, $block) {
|
||||
public function __construct($debug, $mysqli, $config, $share, $user, $block, $memcache) {
|
||||
$this->debug = $debug;
|
||||
$this->mysqli = $mysqli;
|
||||
$this->share = $share;
|
||||
$this->config = $config;
|
||||
$this->user = $user;
|
||||
$this->block = $block;
|
||||
$this->memcache = $memcache;
|
||||
$this->debug->append("Instantiated Share class", 2);
|
||||
}
|
||||
|
||||
// get and set methods
|
||||
/* Some basic get and set methods
|
||||
**/
|
||||
private function setErrorMessage($msg) {
|
||||
$this->sError = $msg;
|
||||
}
|
||||
@ -35,7 +43,26 @@ class Statistics {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Another wrapper, we want to store data in memcache and return the actual data
|
||||
* for further processing
|
||||
* @param key string Our memcache key
|
||||
* @param data mixed Our data to store in Memcache
|
||||
* @param expiration time Our expiration time, see Memcached documentation
|
||||
* @return data mixed Return our stored data unchanged
|
||||
**/
|
||||
public function setCache($key, $data, $expiration=NULL) {
|
||||
if ($this->config['memcache']['enabled']) $this->memcache->set($key, $data, $expiration);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get our last $limit blocks found
|
||||
* @param limit int Last limit blocks
|
||||
* @return array
|
||||
**/
|
||||
public function getBlocksFound($limit=10) {
|
||||
if ($data = $this->memcache->get(__FUNCTION__ . $limit)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT b.*, a.username as finder
|
||||
FROM " . $this->block->getTableName() . " AS b
|
||||
@ -43,11 +70,19 @@ class Statistics {
|
||||
ON b.account_id = a.id
|
||||
ORDER BY height DESC LIMIT ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
return $this->setCache(__FUNCTION__ . $limit, $result->fetch_all(MYSQLI_ASSOC), 5);
|
||||
// Catchall
|
||||
$this->debug->append("Failed to find blocks:" . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Currently the only function writing to the database
|
||||
* Stored per block user statistics of valid and invalid shares
|
||||
* @param aStats array Array with user id, valid and invalid shares
|
||||
* @param iBlockId int Block ID as store in the Block table
|
||||
* @return bool
|
||||
**/
|
||||
public function updateShareStatistics($aStats, $iBlockId) {
|
||||
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, valid, invalid, block_id) VALUES (?, ?, ?, ?)");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $aStats['id'], $aStats['valid'], $aStats['invalid'], $iBlockId) && $stmt->execute()) return true;
|
||||
@ -56,7 +91,14 @@ class Statistics {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get our current pool hashrate for the past 10 minutes across both
|
||||
* shares and shares_archive table
|
||||
* @param none
|
||||
* @return data object Return our hashrateas an object
|
||||
**/
|
||||
public function getCurrentHashrate() {
|
||||
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT SUM(hashrate) AS hashrate FROM
|
||||
(
|
||||
@ -65,12 +107,18 @@ class Statistics {
|
||||
SELECT ROUND(COUNT(id) * POW(2, " . $this->config['difficulty'] . ")/600/1000) AS hashrate FROM " . $this->share->getArchiveTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
) AS sum");
|
||||
// Catchall
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() ) return $result->fetch_object()->hashrate;
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() ) return $this->setCache(__FUNCTION__, $result->fetch_object()->hashrate);
|
||||
$this->debug->append("Failed to get hashrate: " . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as getCurrentHashrate but for Shares
|
||||
* @param none
|
||||
* @return data object Our share rate in shares per second
|
||||
**/
|
||||
public function getCurrentShareRate() {
|
||||
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT ROUND(SUM(sharerate) / 600, 2) AS sharerate FROM
|
||||
(
|
||||
@ -78,13 +126,19 @@ class Statistics {
|
||||
UNION ALL
|
||||
SELECT COUNT(id) AS sharerate FROM " . $this->share->getArchiveTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
) AS sum");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() ) return $result->fetch_object()->sharerate;
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() ) return $this->setCache(__FUNCTION__, $result->fetch_object()->sharerate);
|
||||
// Catchall
|
||||
$this->debug->append("Failed to fetch share rate: " . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total shares for this round, since last block found
|
||||
* @param none
|
||||
* @return data array invalid and valid shares
|
||||
**/
|
||||
public function getRoundShares() {
|
||||
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
( SELECT IFNULL(count(id), 0)
|
||||
@ -95,13 +149,20 @@ class Statistics {
|
||||
FROM " . $this->share->getTableName() . "
|
||||
WHERE UNIX_TIMESTAMP(time) >IFNULL((SELECT MAX(time) FROM blocks),0)
|
||||
AND our_result = 'N' ) as invalid");
|
||||
if ( $this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() ) return $result->fetch_assoc();
|
||||
if ( $this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() )
|
||||
return $this->setCache(__FUNCTION__, $result->fetch_assoc());
|
||||
// Catchall
|
||||
$this->debug->append("Failed to fetch round shares: " . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get amount of shares for a specific user
|
||||
* @param account_id int User ID
|
||||
* @return data array invalid and valid share counts
|
||||
**/
|
||||
public function getUserShares($account_id) {
|
||||
if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
(
|
||||
@ -109,7 +170,7 @@ class Statistics {
|
||||
FROM " . $this->share->getTableName() . " AS s,
|
||||
" . $this->user->getTableName() . " AS u
|
||||
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND UNIX_TIMESTAMP(s.time) >IFNULL((SELECT MAX(b.time) FROM blocks AS b),0)
|
||||
AND UNIX_TIMESTAMP(s.time) >IFNULL((SELECT MAX(b.time) FROM " . $this->block->getTableName() . " AS b),0)
|
||||
AND our_result = 'Y'
|
||||
AND u.id = ?
|
||||
) AS valid,
|
||||
@ -118,17 +179,24 @@ class Statistics {
|
||||
FROM " . $this->share->getTableName() . " AS s,
|
||||
" . $this->user->getTableName() . " AS u
|
||||
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND UNIX_TIMESTAMP(s.time) >IFNULL((SELECT MAX(b.time) FROM blocks AS b),0)
|
||||
AND UNIX_TIMESTAMP(s.time) >IFNULL((SELECT MAX(b.time) FROM " . $this->block->getTableName() . " AS b),0)
|
||||
AND our_result = 'N'
|
||||
AND u.id = ?
|
||||
) AS invalid");
|
||||
if ($stmt && $stmt->bind_param("ii", $account_id, $account_id) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_assoc();
|
||||
if ($stmt && $stmt->bind_param("ii", $account_id, $account_id) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $this->setCache(__FUNCTION__ . $account_id, $result->fetch_assoc());
|
||||
// Catchall
|
||||
$this->debug->append("Unable to fetch user round shares: " . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as getUserShares for Hashrate
|
||||
* @param account_id integer User ID
|
||||
* @return data integer Current Hashrate in khash/s
|
||||
**/
|
||||
public function getUserHashrate($account_id) {
|
||||
if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT ROUND(COUNT(s.id) * POW(2, " . $this->config['difficulty'] . ")/600/1000) AS hashrate
|
||||
FROM " . $this->share->getTableName() . " AS s,
|
||||
@ -136,13 +204,20 @@ class Statistics {
|
||||
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND s.time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
AND u.id = ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result() ) return $result->fetch_object()->hashrate;
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result() )
|
||||
return $this->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->hashrate);
|
||||
// Catchall
|
||||
$this->debug->append("Failed to fetch hashrate: " . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hashrate for a specific worker
|
||||
* @param worker_id int Worker ID to fetch hashrate for
|
||||
* @return data int Current hashrate in khash/s
|
||||
**/
|
||||
public function getWorkerHashrate($worker_id) {
|
||||
if ($data = $this->memcache->get(__FUNCTION__ . $worker_id)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT ROUND(COUNT(s.id) * POW(2,21)/600/1000) AS hashrate
|
||||
FROM " . $this->share->getTableName() . " AS s,
|
||||
@ -150,40 +225,62 @@ class Statistics {
|
||||
WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND s.time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
AND u.id = ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result() ) return $result->fetch_object()->hashrate;
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result() )
|
||||
return $this->setCache(__FUNCTION__ . $worker_id, $result->fetch_object()->hashrate);
|
||||
// Catchall
|
||||
$this->debug->append("Failed to fetch hashrate: " . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getTopContributors($limit=15) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
ROUND(COUNT(id) / 60 / 10, 2) AS sharesps,
|
||||
ROUND(COUNT(id) * POW(2," . $this->config['difficulty'] . ")/600/1000,2) AS hashrate,
|
||||
SUBSTRING_INDEX( username, '.', 1 ) AS account
|
||||
FROM " . $this->share->getTableName() . "
|
||||
WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
GROUP BY account
|
||||
ORDER BY hashrate DESC LIMIT ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $hashrates= $stmt->get_result()) {
|
||||
$aHashData = $hashrates->fetch_all(MYSQLI_ASSOC);
|
||||
$stmt->close();
|
||||
} else {
|
||||
/**
|
||||
* get our top contributors for either shares or hashrate
|
||||
* @param type string shares or hashes
|
||||
* @param limit int Limit result to $limit
|
||||
* @return data array Users with shares, account or hashrate, account
|
||||
**/
|
||||
public function getTopContributors($type='shares', $limit=15) {
|
||||
if ($data = $this->memcache->get(__FUNCTION__ . $type . $limit)) return $data;
|
||||
switch ($type) {
|
||||
case 'shares':
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
COUNT(id) AS shares,
|
||||
SUBSTRING_INDEX( username, '.', 1 ) AS account
|
||||
FROM " . $this->share->getTableName() . "
|
||||
GROUP BY account
|
||||
ORDER BY shares DESC
|
||||
LIMIT ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $this->setCache(__FUNCTION__ . $type . $limit, $result->fetch_all(MYSQLI_ASSOC));
|
||||
$this->debug->append("Fetching shares failed: ");
|
||||
return false;
|
||||
break;
|
||||
|
||||
case 'hashes':
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
ROUND(COUNT(id) * POW(2," . $this->config['difficulty'] . ")/600/1000,2) AS hashrate,
|
||||
SUBSTRING_INDEX( username, '.', 1 ) AS account
|
||||
FROM " . $this->share->getTableName() . "
|
||||
WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)
|
||||
GROUP BY account
|
||||
ORDER BY hashrate DESC LIMIT ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $this->setCache(__FUNCTION__ . $type . $limit, $result->fetch_all(MYSQLI_ASSOC));
|
||||
$this->debug->append("Fetching shares failed: ");
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
foreach ($aHashData as $key => $aData) {
|
||||
$stmt = $this->mysqli->prepare("SELECT COUNT(id) FROM " . $this->share->getTableName() . " WHERE SUBSTRING_INDEX( username , '.', 1 ) = ?");
|
||||
if ($stmt->bind_param("s", $aData['username']) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
$aHashData[$key]['shares'] = $this->getUserShares($this->user->getUserId($aData['account']))['valid'];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return $aHashData;
|
||||
}
|
||||
|
||||
/**
|
||||
* get Hourly hashrate for a user
|
||||
* Not working yet since I was not able to solve this via SQL queries
|
||||
* @param account_id int User ID
|
||||
* @return data array NOT FINISHED YET
|
||||
**/
|
||||
public function getHourlyHashrateByAccount($account_id) {
|
||||
if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
ROUND(COUNT(s.id) * POW(2, 12)/600/1000) AS hashrate,
|
||||
@ -202,11 +299,12 @@ class Statistics {
|
||||
AND a.username = SUBSTRING_INDEX( s.username, '.', 1 )
|
||||
AND a.id = ?
|
||||
GROUP BY HOUR(time)");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $account_id, $account_id) && $stmt->execute() && $hourlyhashrates = $stmt->get_result())
|
||||
return $hourlyhashrates->fetch_all(MYSQLI_ASSOC);
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $account_id, $account_id) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $this->setCache(__FUNCTION__ . $account_id, $result->fetch_all(MYSQLI_ASSOC), 3600);
|
||||
// Catchall
|
||||
$this->debug->append("Failed to fetch hourly hashrate: " . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$statistics = new Statistics($debug, $mysqli, $config, $share, $user, $block);
|
||||
|
||||
$statistics = new Statistics($debug, $mysqli, $config, $share, $user, $block, $memcache);
|
||||
|
||||
49
public/include/classes/statscache.class.php
Normal file
49
public/include/classes/statscache.class.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
/**
|
||||
* A wrapper class used to store values transparently in memcache
|
||||
* 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 {
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around memcache->set
|
||||
* Do not store values if memcache is disabled
|
||||
**/
|
||||
public function set($key, $value, $expiration=NULL) {
|
||||
if (! $this->config['memcache']['enabled']) return false;
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around memcache->get
|
||||
* Always return false if memcache is disabled
|
||||
**/
|
||||
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)) {
|
||||
$this->debug->append("Found key in cache", 3);
|
||||
return $data;
|
||||
} else {
|
||||
$this->debug->append("Key not found", 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$memcache = new StatsCache($config, $debug);
|
||||
$memcache->addServer($config['memcache']['host'], $config['memcache']['port']);
|
||||
@ -9,10 +9,11 @@ class Transaction {
|
||||
private $table = 'transactions';
|
||||
private $tableBlocks = 'blocks';
|
||||
|
||||
public function __construct($debug, $mysqli, $config) {
|
||||
public function __construct($debug, $mysqli, $config, $block) {
|
||||
$this->debug = $debug;
|
||||
$this->mysqli = $mysqli;
|
||||
$this->config = $config;
|
||||
$this->block = $block;
|
||||
$this->debug->append("Instantiated Transaction class", 2);
|
||||
}
|
||||
|
||||
@ -24,10 +25,10 @@ class Transaction {
|
||||
return $this->sError;
|
||||
}
|
||||
|
||||
public function addTransaction($account_id, $amount, $type='Credit', $block_id=NULL, $coin_address=NULL, $fee=0) {
|
||||
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, amount, block_id, type, coin_address, fee_amount) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
public function addTransaction($account_id, $amount, $type='Credit', $block_id=NULL, $coin_address=NULL) {
|
||||
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, amount, block_id, type, coin_address) VALUES (?, ?, ?, ?, ?)");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param("idissd", $account_id, $amount, $block_id, $type, $coin_address, $fee);
|
||||
$stmt->bind_param("idiss", $account_id, $amount, $block_id, $type, $coin_address);
|
||||
if ($stmt->execute()) {
|
||||
$this->setErrorMessage("Failed to store transaction");
|
||||
$stmt->close();
|
||||
@ -75,23 +76,33 @@ class Transaction {
|
||||
|
||||
public function getBalance($account_id) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT IFNULL(c.credit, 0) - IFNULL(d.debit,0) AS balance
|
||||
FROM (
|
||||
SELECT t.account_id, sum(t.amount) AS credit
|
||||
SELECT ROUND(IFNULL(t1.credit, 0) - IFNULL(t2.debit, 0) - IFNULL(t3.other, 0), 8) AS balance
|
||||
FROM
|
||||
(
|
||||
SELECT sum(t.amount) AS credit
|
||||
FROM $this->table AS t
|
||||
LEFT JOIN $this->tableBlocks AS b ON t.block_id = b.id
|
||||
WHERE type = 'Credit'
|
||||
AND b.confirmations > ?
|
||||
AND t.account_id = ? ) AS c
|
||||
LEFT JOIN (
|
||||
SELECT t.account_id, sum(amount) AS debit
|
||||
LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
|
||||
WHERE t.type = 'Credit'
|
||||
AND b.confirmations >= ?
|
||||
AND t.account_id = ?
|
||||
) AS t1,
|
||||
(
|
||||
SELECT sum(t.amount) AS debit
|
||||
FROM $this->table AS t
|
||||
WHERE type IN ('Debit_MP','Debit_AP')
|
||||
AND t.account_id = ? ) AS d
|
||||
ON c.account_id = d.account_id
|
||||
WHERE t.type IN ('Debit_MP', 'Debit_AP')
|
||||
AND t.account_id = ?
|
||||
) AS t2,
|
||||
(
|
||||
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 >= ?
|
||||
AND t.account_id = ?
|
||||
) AS t3
|
||||
");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param("iii", $this->config['confirmations'], $account_id, $account_id);
|
||||
$stmt->bind_param("iiiii", $this->config['confirmations'], $account_id, $account_id, $this->config['confirmations'], $account_id);
|
||||
if (!$stmt->execute()) {
|
||||
$this->debug->append("Unable to execute statement: " . $stmt->error);
|
||||
$this->setErrorMessage("Fetching balance failed");
|
||||
@ -104,4 +115,4 @@ class Transaction {
|
||||
}
|
||||
}
|
||||
|
||||
$transaction = new Transaction($debug, $mysqli, $config);
|
||||
$transaction = new Transaction($debug, $mysqli, $config, $block);
|
||||
|
||||
@ -34,6 +34,12 @@ class User {
|
||||
return $this->getSingle($username, 'id', 'username', 's');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check user login
|
||||
* @param username string Username
|
||||
* @param password string Password
|
||||
* @return bool
|
||||
**/
|
||||
public function checkLogin($username, $password) {
|
||||
$this->debug->append("Checking login for $username with password $password", 2);
|
||||
if ( $this->checkUserPassword($username, $password) ) {
|
||||
@ -43,6 +49,12 @@ class User {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the users PIN for confirmation
|
||||
* @param userID int User ID
|
||||
* @param pin int PIN to check
|
||||
* @return bool
|
||||
**/
|
||||
public function checkPin($userId, $pin=false) {
|
||||
$this->debug->append("Confirming PIN for $userId and pin $pin", 2);
|
||||
$stmt = $this->mysqli->prepare("SELECT pin FROM $this->table WHERE id=? AND pin=? LIMIT 1");
|
||||
@ -55,6 +67,14 @@ class User {
|
||||
return $pin_hash === $row_pin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single row from the table
|
||||
* @param value string Value to search for
|
||||
* @param search Return column to search for
|
||||
* @param field string Search column
|
||||
* @param type string Type of value
|
||||
* @return array Return result
|
||||
**/
|
||||
private function getSingle($value, $search='id', $field='id', $type="i") {
|
||||
$stmt = $this->mysqli->prepare("SELECT $search FROM $this->table WHERE $field = ? LIMIT 1");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
@ -68,12 +88,56 @@ class User {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getCoinAddress($userID) {
|
||||
return $this->getSingle($userID, 'coin_address', 'id', 's');
|
||||
/**
|
||||
* Get all users that have auto payout setup
|
||||
* @param none
|
||||
* @return data array All users with payout setup
|
||||
**/
|
||||
public function getAllAutoPayout() {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
id, username, coin_address, ap_threshold
|
||||
FROM " . $this->getTableName() . "
|
||||
WHERE ap_threshold > 0
|
||||
AND coin_address IS NOT NULL
|
||||
");
|
||||
if ( $this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
$this->debug->append("Unable to fetch users with AP set");
|
||||
echo $this->mysqli->error;
|
||||
return false;
|
||||
}
|
||||
|
||||
private function updateSingle($userID, $field, $table) {
|
||||
$stmt = $this->mysqli->prepare("UPDATE $table SET " . $field['name'] . " = ? WHERE userId = ? LIMIT 1");
|
||||
/**
|
||||
* Fetch users coin address
|
||||
* @param userID int UserID
|
||||
* @return data string Coin Address
|
||||
**/
|
||||
public function getCoinAddress($userID) {
|
||||
return $this->getSingle($userID, 'coin_address', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch users donation value
|
||||
* @param userID int UserID
|
||||
* @return data string Coin Address
|
||||
**/
|
||||
public function getDonatePercent($userID) {
|
||||
$dPercent = $this->getSingle($userID, 'donate_percent', 'id');
|
||||
if ($dPercent > 100) $dPercent = 100;
|
||||
if ($dPercent < 0) $dPercent = 0;
|
||||
return $dPercent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single row in a table
|
||||
* @param userID int Account ID
|
||||
* @param field string Field to update
|
||||
* @return bool
|
||||
**/
|
||||
private function updateSingle($userID, $field) {
|
||||
$stmt = $this->mysqli->prepare("UPDATE $this->table SET " . $field['name'] . " = ? WHERE userId = ? LIMIT 1");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param($field['type'].'i', $field['value'], $userID);
|
||||
$stmt->execute();
|
||||
|
||||
@ -32,7 +32,13 @@ class Worker {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Worker code, could possibly be moved to it's own class someday
|
||||
|
||||
/**
|
||||
* Update worker list for a user
|
||||
* @param account_id int User ID
|
||||
* @param data array All workers and their settings
|
||||
* @return bool
|
||||
**/
|
||||
public function updateWorkers($account_id, $data) {
|
||||
$username = $this->user->getUserName($account_id);
|
||||
foreach ($data as $key => $value) {
|
||||
@ -47,23 +53,32 @@ class Worker {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all workers for an account
|
||||
* @param account_id int User ID
|
||||
* @return mixed array Workers and their settings or false
|
||||
**/
|
||||
public function getWorkers($account_id) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT $this->table.username, $this->table.password,
|
||||
( SELECT SIGN(count(id)) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS active,
|
||||
SELECT id, username, password,
|
||||
( SELECT SIGN(COUNT(id)) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS active,
|
||||
( SELECT ROUND(COUNT(id) * POW(2,21)/600/1000) FROM " . $this->share->getTableName() . " WHERE username = $this->table.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS hashrate
|
||||
FROM $this->table
|
||||
WHERE account_id = ?");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
if (!$stmt->bind_param('i', $account_id)) return false;
|
||||
if (!$stmt->execute()) return false;
|
||||
$result = $stmt->get_result();
|
||||
$stmt->close();
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
// Catchall
|
||||
$this->setErrorMessage('Failed to fetch workers for your account');
|
||||
$this->debug->append('Fetching workers failed: ' . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all currently active workers in the past 10 minutes
|
||||
* @param none
|
||||
* @return data mixed int count if any workers are active, false otherwise
|
||||
**/
|
||||
public function getCountAllActiveWorkers() {
|
||||
$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)) {
|
||||
@ -77,6 +92,15 @@ class Worker {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new worker to an existing web account
|
||||
* The webuser name is prefixed to the worker name
|
||||
* Passwords are plain text for pushpoold
|
||||
* @param account_id int User ID
|
||||
* @param workerName string Worker name
|
||||
* @param workerPassword string Worker password
|
||||
* @return bool
|
||||
**/
|
||||
public function addWorker($account_id, $workerName, $workerPassword) {
|
||||
$username = $this->user->getUserName($account_id);
|
||||
$workerName = "$username.$workerName";
|
||||
@ -92,6 +116,13 @@ class Worker {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete existing worker from account
|
||||
* @param account_id int User ID
|
||||
* @param id int Worker ID
|
||||
* @return bool
|
||||
**/
|
||||
public function deleteWorker($account_id, $id) {
|
||||
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE account_id = ? AND id = ?");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
|
||||
@ -25,9 +25,18 @@ $config = array(
|
||||
'name' => 'The Pool',
|
||||
'slogan' => 'Resistance is futile',
|
||||
),
|
||||
'difficulty' => '31', // Target difficulty for this pool
|
||||
'reward' => '50', // Reward for finding blocks
|
||||
'confirmations' => '120', // Confirmations per block found to credit transactions
|
||||
'fees' => 0,
|
||||
'difficulty' => '31', // Target difficulty for this pool as set in pushpoold json
|
||||
'reward' => '50', // Reward for finding blocks, fixed value but changes someday
|
||||
'confirmations' => '120', // Confirmations per block needed to credit transactions
|
||||
'memcache' => array(
|
||||
'enabled' => true,
|
||||
'host' => 'localhost', // Memcache Host
|
||||
'port' => 11211, // Memcache Port
|
||||
'keyprefix' => 'mmcfe_ng_', // Prefix for all keys
|
||||
'expiration'=> '90', // Cache time
|
||||
'splay' => '15' // Splay time
|
||||
),
|
||||
'wallet' => array(
|
||||
'type' => 'http', // http or https are supported
|
||||
'host' => 'localhost:9332',
|
||||
|
||||
@ -8,11 +8,11 @@ if (!defined('SECURITY'))
|
||||
if ($bitcoin->can_connect() === true){
|
||||
if (!$dDifficulty = $memcache->get('dDifficulty')) {
|
||||
$dDifficulty = $bitcoin->query('getdifficulty');
|
||||
$memcache->set('dDifficulty', $dDifficulty, 60);
|
||||
$memcache->set('dDifficulty', $dDifficulty);
|
||||
}
|
||||
if (!$iBlock = $memcache->get('iBlock')) {
|
||||
$iBlock = $bitcoin->query('getblockcount');
|
||||
$memcache->set('iBlock', $iBlock, 60);
|
||||
$memcache->set('iBlock', $iBlock);
|
||||
}
|
||||
} else {
|
||||
$iDifficulty = 1;
|
||||
@ -20,23 +20,18 @@ if ($bitcoin->can_connect() === true){
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to pushpool service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
|
||||
if (!$aHashData = $memcache->get('aHashData')) {
|
||||
$debug->append('STA Fetching Hashrates from database');
|
||||
$aHashData = $statistics->getTopContributors();
|
||||
$memcache->set('aHashData', $aHashData, 60);
|
||||
$debug->append('END Fetching Hashrates from database');
|
||||
}
|
||||
// Top share contributors
|
||||
$aContributorsShares = $statistics->getTopContributors('shares', 15);
|
||||
|
||||
// Top hash contributors
|
||||
$aContributorsHashes = $statistics->getTopContributors('hashes', 15);
|
||||
|
||||
// Grab the last 10 blocks found
|
||||
$aBlocksFoundData = $statistics->getBlocksFound(10);
|
||||
$aBlockData = $aBlocksFoundData[0];
|
||||
|
||||
// Estimated time to find the next block
|
||||
if (!$iCurrentPoolHashrate = $memcache->get('iCurrentPoolHashrate')) {
|
||||
$debug->append('Fetching iCurrentPoolHashrate from database');
|
||||
$iCurrentPoolHashrate = $statistics->getCurrentHashrate();
|
||||
$memcache->set('iCurrentPoolHashrate', $iCurrentPoolHashrate, 60);
|
||||
}
|
||||
$iCurrentPoolHashrate = $statistics->getCurrentHashrate();
|
||||
// Time in seconds, not hours, using modifier in smarty to translate
|
||||
$iEstTime = $dDifficulty * pow(2,32) / ($iCurrentPoolHashrate * 1000);
|
||||
|
||||
@ -52,7 +47,8 @@ if (!empty($aBlockData)) {
|
||||
$smarty->assign("ESTTIME", $iEstTime);
|
||||
$smarty->assign("TIMESINCELAST", $dTimeSinceLast);
|
||||
$smarty->assign("BLOCKSFOUND", $aBlocksFoundData);
|
||||
$smarty->assign("TOPHASHRATES", $aHashData);
|
||||
$smarty->assign("CONTRIBSHARES", $aContributorsShares);
|
||||
$smarty->assign("CONTRIBHASHES", $aContributorsHashes);
|
||||
$smarty->assign("CURRENTBLOCK", $iBlock);
|
||||
$smarty->assign("LASTBLOCK", $aBlockData['height']);
|
||||
$smarty->assign("DIFFICULTY", $dDifficulty);
|
||||
|
||||
@ -8,11 +8,11 @@ if (!defined('SECURITY'))
|
||||
if ($bitcoin->can_connect() === true){
|
||||
if (!$dDifficulty = $memcache->get('dDifficulty')) {
|
||||
$dDifficulty = $bitcoin->query('getdifficulty');
|
||||
$memcache->set('dDifficulty', $dDifficulty, 60);
|
||||
$memcache->set('dDifficulty', $dDifficulty);
|
||||
}
|
||||
if (!$iBlock = $memcache->get('iBlock')) {
|
||||
$iBlock = $bitcoin->query('getblockcount');
|
||||
$memcache->set('iBlock', $iBlock, 60);
|
||||
$memcache->set('iBlock', $iBlock);
|
||||
}
|
||||
} else {
|
||||
$iDifficulty = 1;
|
||||
@ -20,12 +20,7 @@ if ($bitcoin->can_connect() === true){
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to pushpool service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
|
||||
if (!$aHourlyHashRates = $memcache->get('mmcfe_' . $_SESSION['USERDATA']['id'] . '_hourlyhashrate')) {
|
||||
$debug->append('STA Fetching hourly hashrates from database');
|
||||
$aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA']['id']);
|
||||
$memcache->set('mmcfe_' . $_SESSION['USERDATA']['id'] . '_hourlyhashrate', $aHourlyHashRates, 600);
|
||||
$debug->append('END Fetching hourly hashrates from database');
|
||||
}
|
||||
$aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA']['id']);
|
||||
|
||||
// Propagate content our template
|
||||
$smarty->assign("YOURHASHRATES", $aHourlyHashRates);
|
||||
|
||||
@ -7,34 +7,11 @@ if (!defined('SECURITY'))
|
||||
// Globally available variables
|
||||
$debug->append('Global smarty variables', 3);
|
||||
|
||||
// Store some stuff in memcache prior to assigning it to Smarty
|
||||
if (!$aRoundShares = $memcache->get('aRoundShares')) {
|
||||
$debug->append('STA Fetching aRoundShares from database');
|
||||
$aRoundShares = $statistics->getRoundShares();
|
||||
$debug->append('END Fetching aRoundShares from database');
|
||||
$memcache->set('aRoundShares', $aRoundShares, 90);
|
||||
}
|
||||
|
||||
if (!$iCurrentActiveWorkers = $memcache->get('iCurrentActiveWorkers')) {
|
||||
$debug->append('STA Fetching iCurrentActiveWorkers from database');
|
||||
$iCurrentActiveWorkers = $worker->getCountAllActiveWorkers();
|
||||
$debug->append('END Fetching iCurrentActiveWorkers from database');
|
||||
$memcache->set('iCurrentActiveWorkers', $iCurrentActiveWorkers, 80);
|
||||
}
|
||||
|
||||
if (!$iCurrentPoolHashrate = $memcache->get('iCurrentPoolHashrate')) {
|
||||
$debug->append('STA Fetching iCurrentPoolHashrate from database');
|
||||
$iCurrentPoolHashrate = $statistics->getCurrentHashrate();
|
||||
$debug->append('END Fetching iCurrentPoolHashrate from database');
|
||||
$memcache->set('iCurrentPoolHashrate', $iCurrentPoolHashrate, 90);
|
||||
}
|
||||
|
||||
if (!$iCurrentPoolShareRate = $memcache->get('iCurrentPoolShareRate')) {
|
||||
$debug->append('STA Fetching iCurrentPoolShareRate from database');
|
||||
$iCurrentPoolShareRate = $statistics->getCurrentShareRate();
|
||||
$debug->append('END Fetching iCurrentPoolShareRate from database');
|
||||
$memcache->set('iCurrentPoolShareRate', $iCurrentPoolShareRate, 90);
|
||||
}
|
||||
// Fetch some data
|
||||
$aRoundShares = $statistics->getRoundShares();
|
||||
$iCurrentActiveWorkers = $worker->getCountAllActiveWorkers();
|
||||
$iCurrentPoolHashrate = $statistics->getCurrentHashrate();
|
||||
$iCurrentPoolShareRate = $statistics->getCurrentShareRate();
|
||||
|
||||
$aGlobal = array(
|
||||
'slogan' => $config['website']['slogan'],
|
||||
@ -43,30 +20,24 @@ $aGlobal = array(
|
||||
'sharerate' => $iCurrentPoolShareRate,
|
||||
'workers' => $iCurrentActiveWorkers,
|
||||
'roundshares' => $aRoundShares,
|
||||
'fees' => $config['fees'],
|
||||
'confirmations' => $config['confirmations'],
|
||||
'reward' => $config['reward']
|
||||
);
|
||||
|
||||
// We don't want the session infos cached
|
||||
// We don't want these session infos cached
|
||||
$aGlobal['userdata'] = $_SESSION['USERDATA']['id'] ? $user->getUserData($_SESSION['USERDATA']['id']) : array();
|
||||
|
||||
// Balance should also not be cached
|
||||
$aGlobal['userdata']['balance'] = $transaction->getBalance($_SESSION['USERDATA']['id']);
|
||||
|
||||
// Other userdata that we can cache savely
|
||||
if (!$aGlobal['userdata']['shares'] = $memcache->get('global_' . $_SESSION['USERDATA']['id'] . '_shares')) {
|
||||
$debug->append('STA Loading user shares from database');
|
||||
$aGlobal['userdata']['shares'] = $statistics->getUserShares($_SESSION['USERDATA']['id']);
|
||||
$debug->append('END Loading user shares from database');
|
||||
$memcache->set('global_' . $_SESSION['USERDATA']['id'] . '_shares', $aGlobal['userdata']['shares'], 80);
|
||||
}
|
||||
$aGlobal['userdata']['shares'] = $statistics->getUserShares($_SESSION['USERDATA']['id']);
|
||||
$aGlobal['userdata']['hashrate'] = $statistics->getUserHashrate($_SESSION['USERDATA']['id']);
|
||||
|
||||
if (!$aGlobal['userdata']['hashrate'] = $memcache->get('global_' . $_SESSION['USERDATA']['id'] . '_hashrate') ) {
|
||||
$debug->append('STA Loading user hashrate from database');
|
||||
$aGlobal['userdata']['hashrate'] = $statistics->getUserHashrate($_SESSION['USERDATA']['id']);
|
||||
$debug->append('END Loading user hashrate from database');
|
||||
$memcache->set('global_' . $_SESSION['USERDATA']['id'] . '_hashrate', $aGlobal['userdata']['hashrate'], 70);
|
||||
}
|
||||
// Some estimations
|
||||
$aGlobal['userdata']['est_block'] = round(( (int)$aGlobal['userdata']['shares']['valid'] / (int)$aRoundShares['valid'] ) * (int)$config['reward'], 3);
|
||||
$aGlobal['userdata']['est_donation'] = round((( $aGlobal['userdata']['donate_percent'] / 100) * $aGlobal['userdata']['est_block']), 3);
|
||||
$aGlobal['userdata']['est_fee'] = round((($config['fees'] / 100) * ($aGlobal['userdata']['est_block'] - $aGlobal['userdata']['est_donation'])), 3);
|
||||
$aGlobal['userdata']['est_payout'] = round($aGlobal['userdata']['est_block'] - $aGlobal['userdata']['est_donation'] - $aGlobal['userdata']['est_fee'], 3);
|
||||
|
||||
// Make it available in Smarty
|
||||
$smarty->assign('PATH', 'site_assets/' . THEME);
|
||||
|
||||
2
public/templates/cache/README.md
vendored
Normal file
2
public/templates/cache/README.md
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
Please ensure the webserver has access to this folder to write the
|
||||
caching templates.
|
||||
@ -1,11 +1,6 @@
|
||||
{include file="global/block_header.tpl" BLOCK_HEADER="Transaction Log" BUTTONS=array(Confirmed,Unconfirmed)}
|
||||
<div class="block_content tab_content" id="Confirmed" style="clear:;">
|
||||
<center>
|
||||
<p>
|
||||
<font color="" size="1">
|
||||
<b>ATP</b> = Auto Threshold Payment, <b>MP</b> = Manual Payment, <b>Don_Fee</b> = donation amount + pool fees (if applicable)
|
||||
</font>
|
||||
</p>
|
||||
<table cellpadding="1" cellspacing="1" width="98%" class="sortable">
|
||||
<thead style="font-size:13px;">
|
||||
<tr>
|
||||
@ -19,12 +14,18 @@
|
||||
</thead>
|
||||
<tbody style="font-size:12px;">
|
||||
{section transaction $TRANSACTIONS}
|
||||
{if (($TRANSACTIONS[transaction].type == 'Credit' and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations) or $TRANSACTIONS[transaction].type != 'Credit')}
|
||||
{if (
|
||||
($TRANSACTIONS[transaction].type == 'Credit' and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations)
|
||||
or ($TRANSACTIONS[transaction].type == 'Donation' and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations)
|
||||
or ($TRANSACTIONS[transaction].type == 'Fee' and $TRANSACTIONS[transaction].confirmations >= $GLOBAL.confirmations)
|
||||
or $TRANSACTIONS[transaction].type == 'Debit_AP'
|
||||
or $TRANSACTIONS[transaction].type == 'Debit_MP'
|
||||
)}
|
||||
<tr class="{cycle values="odd,even"}">
|
||||
<td>{$TRANSACTIONS[transaction].id}</td>
|
||||
<td>{$TRANSACTIONS[transaction].timestamp}</td>
|
||||
<td>{$TRANSACTIONS[transaction].type}</td>
|
||||
<td>{$TRANSACTIONS[transaction].sendAddress}</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 == Credit}green{else}red{/if}">{$TRANSACTIONS[transaction].amount}</td>
|
||||
</tr>
|
||||
@ -32,11 +33,15 @@
|
||||
{/section}
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
<font color="" size="1">
|
||||
<b>Credit_AP</b> = Auto Threshold Payment, <b>Credit_MP</b> = Manual Payment, <b>Donation</b> = Donation, <b>Fee</b> = Pool Fees (if applicable)
|
||||
</font>
|
||||
</p>
|
||||
</center>
|
||||
</div>
|
||||
<div class="block_content tab_content" id="Unconfirmed" style="">
|
||||
<center>
|
||||
<p><font color="" sizeze="1">Listed below are your estimated rewards and donations/fees for all blocks awaiting 120 confirmations.</font></p>
|
||||
<table cellpadding="1" cellspacing="1" width="98%" class="sortable">
|
||||
<thead style="font-size:13px;">
|
||||
<tr>
|
||||
@ -50,24 +55,33 @@
|
||||
</thead>
|
||||
<tbody style="font-size:12px;">
|
||||
{section transaction $TRANSACTIONS}
|
||||
{if $TRANSACTIONS[transaction].type == 'Credit' && $TRANSACTIONS[transaction].confirmations < $GLOBAL.confirmations}
|
||||
{if (
|
||||
$TRANSACTIONS[transaction].type == 'Credit' && $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)
|
||||
)}
|
||||
<tr class="{cycle values="odd,even"}">
|
||||
<td>{$TRANSACTIONS[transaction].id}</td>
|
||||
<td>{$TRANSACTIONS[transaction].timestamp}</td>
|
||||
<td>{$TRANSACTIONS[transaction].type}</td>
|
||||
<td>{$TRANSACTIONS[transaction].sendAddress}</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 == Credit}green{else}red{/if}">{$TRANSACTIONS[transaction].amount}</td>
|
||||
</tr>
|
||||
{assign var="sum" value="`$sum+$TRANSACTIONS[transaction].amount`"}
|
||||
{if $TRANSACTIONS[transaction].type == Credit}
|
||||
{assign var="credits" value="`$credits+$TRANSACTIONS[transaction].amount`"}
|
||||
{else}
|
||||
{assign var="debits" value="`$debits+$TRANSACTIONS[transaction].amount`"}
|
||||
{/if}
|
||||
{/if}
|
||||
{/section}
|
||||
<tr>
|
||||
<td colspan="5"><b>Unconfirmed Totals:</b></td>
|
||||
<td><b>{$sum}</b></td>
|
||||
<td><b>{$credits - $debits}</b></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><font color="" sizeze="1">Listed are your estimated rewards and donations/fees for all blocks awaiting {$GLOBAL.confirmations} confirmations.</font></p>
|
||||
</center>
|
||||
</div>
|
||||
{include file="global/block_footer.tpl"}
|
||||
|
||||
@ -1,25 +1,63 @@
|
||||
<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;">
|
||||
<p>
|
||||
<b><u>Your Current Hashrate</u></b><br/>
|
||||
<i><b>{$GLOBAL.userdata.hashrate} KH/s</b></i><br/><br/>
|
||||
<u><b>Unpaid Shares</b></u><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><br/>
|
||||
Your Valid: <b><i>{$GLOBAL.userdata.shares.valid}</i><font size='1px'></font></b><br/>
|
||||
Pool Valid: <b><i>{$GLOBAL.roundshares.valid}</i> <font size='1px'></font></b><br/><br>
|
||||
<u><b>Round Shares </b></u><span id='tt'><img src='{$PATH}/images/questionmark.png' height='15px' width='15px' title='Submitted shares since last found block (ie. round shares)'></span><br/>
|
||||
Pool Valid: <b><i>{$GLOBAL.roundshares.valid}</i></b><br>
|
||||
Pool Inalid: <b><i>{$GLOBAL.roundshares.invalid}</i></b><br>
|
||||
Your Invalid: <b><i>{$GLOBAL.userdata.shares.invalid}</i><font size='1px'></font></b><br/><br>
|
||||
<u><b>Round Estimate</b></u><font size='1'></font></u><br>
|
||||
<b><i>{math equation="round(( x / y ) * z, 8)" x=$GLOBAL.userdata.shares.valid y=$GLOBAL.roundshares.valid z=$GLOBAL.reward}</i> <font size='1px'>LTC</font></b><br><br>
|
||||
<u><b>Account Balance</b></u><br><b><i>{$GLOBAL.userdata.balance|default:"0"}</i><font size='1px'> LTC</font></b><br/><br>
|
||||
</p>
|
||||
</div>
|
||||
<div class="bendl"></div>
|
||||
<div class="bendr"></div>
|
||||
<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">
|
||||
<tr><td colspan="2"><b>Your Current Hashrate</b></td></tr>
|
||||
<tr><td colspan="2">{$GLOBAL.userdata.hashrate} 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><i>{$GLOBAL.userdata.shares.valid}</i><font size='1px'></font></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Pool Valid</td>
|
||||
<td><i>{$GLOBAL.roundshares.valid}</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><i>{$GLOBAL.roundshares.valid}</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Pool Invalid</b></td>
|
||||
<td><i>{$GLOBAL.roundshares.invalid}</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Your Invalid</b></td>
|
||||
<td><i>{$GLOBAL.userdata.shares.invalid}</i><font size='1px'></font></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>Round Estimate</u></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Block</b></td>
|
||||
<td>{$GLOBAL.userdata.est_block} LTC</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Donation</b></td>
|
||||
<td>{$GLOBAL.userdata.est_donation} LTC</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Fees</b></td>
|
||||
<td>{$GLOBAL.userdata.est_fee} LTC</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Payout</b></td>
|
||||
<td>{$GLOBAL.userdata.est_payout} LTC</td>
|
||||
</tr>
|
||||
<tr><td colspan="2"> </td></tr>
|
||||
<tr><td colspan="2"><b><u>Account Balance</u></b></td></tr>
|
||||
<tr><td colspan="2"><b>{$GLOBAL.userdata.balance|default:"0"} LTC</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="bendl"></div>
|
||||
<div class="bendr"></div>
|
||||
</div>
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
{section block $BLOCKSFOUND}
|
||||
<tr class="{cycle values="odd,even"}">
|
||||
<td>{$BLOCKSFOUND[block].height}</td>
|
||||
<td>{if $BLOCKSFOUND[block].confirmations >= 120}<font color="green">Confirmed</font>{else}{120 - $BLOCKSFOUND[block].confirmations} left{/if}</td>
|
||||
<td>{if $BLOCKSFOUND[block].confirmations >= $GLOBAL.confirmations}<font color="green">Confirmed</font>{else}{$GLOBAL.confirmations - $BLOCKSFOUND[block].confirmations} left{/if}</td>
|
||||
<td>{$BLOCKSFOUND[block].finder|default:"unknown"}</td>
|
||||
<td>{$BLOCKSFOUND[block].time|date_format:"%d/%m/%Y %H:%M:%S"}</td>
|
||||
<td>{$BLOCKSFOUND[block].difficulty|number_format:"8"}</td>
|
||||
@ -27,6 +27,6 @@
|
||||
</table>
|
||||
</center>
|
||||
<ul>
|
||||
<li>Note: <font color="orange">Round Earnings are not credited until 120 confirms.</font></li>
|
||||
<li>Note: <font color="orange">Round Earnings are not credited until {$GLOBAL.confirmations} confirms.</font></li>
|
||||
</ul>
|
||||
{include file="global/block_footer.tpl"}
|
||||
|
||||
@ -1,34 +1,8 @@
|
||||
{include file="global/block_header.tpl" BLOCK_HEADER="Pool Statistics" BLOCK_STYLE="clear:none;"}
|
||||
{include file="global/block_header.tpl" BLOCK_HEADER="Top Contributers"}
|
||||
<center>
|
||||
<table width="100%" border="0" style="font-size:13px;" class="sortable">
|
||||
<thead>
|
||||
<tr style="background-color:#B6DAFF;">
|
||||
<th align="left">Rank</th>
|
||||
<th align="left" scope="col">User Name</th>
|
||||
<th align="left" scope="col">KH/s</th>
|
||||
<th align="left" scope="col">Shares</th>
|
||||
<th align="left" scope="col">Shares/s</th>
|
||||
<th align="left">Ł/Day<font size="1"> (est)</font></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{assign var=rank value=1}
|
||||
{section hashrate $TOPHASHRATES}
|
||||
<tr class="{cycle values="odd,even"}">
|
||||
<td>{$rank++}</td>
|
||||
<td>{$TOPHASHRATES[hashrate].account}</td>
|
||||
<td>{$TOPHASHRATES[hashrate].hashrate|number_format}</td>
|
||||
<td>{$TOPHASHRATES[hashrate].shares|number_format}</td>
|
||||
<td>{$TOPHASHRATES[hashrate].sharesps}</td>
|
||||
<td>{math equation="round(reward / ( diff * pow(2,32) / ( hashrate * 1000 ) / 3600 / 24),3)" diff=$DIFFICULTY reward=$REWARD hashrate=$TOPHASHRATES[hashrate].hashrate}</td>
|
||||
</tr>
|
||||
{/section}
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="pagination" class="pagination"></div>
|
||||
</center>
|
||||
{include file="global/block_footer.tpl"}
|
||||
|
||||
{include file="statistics/pool/contributors_shares.tpl"}
|
||||
|
||||
{include file="statistics/pool/contributors_hashrate.tpl"}
|
||||
|
||||
{include file="global/block_header.tpl" BLOCK_HEADER="Server Stats" BLOCK_STYLE="clear:all;" STYLE="padding-left:5px;padding-right:5px;"}
|
||||
<table class="" width="100%" style="font-size:13px;">
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
{include file="global/block_header.tpl" ALIGN="left" BLOCK_HEADER="Top Hashrate Contributers"}
|
||||
<center>
|
||||
<table width="100%" border="0" style="font-size:13px;" class="sortable">
|
||||
<thead>
|
||||
<tr style="background-color:#B6DAFF;">
|
||||
<th align="left">Rank</th>
|
||||
<th align="left" scope="col">User Name</th>
|
||||
<th align="left" scope="col">KH/s</th>
|
||||
<th align="left">Ł/Day<font size="1"> (est)</font></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{assign var=rank value=1}
|
||||
{section contrib $CONTRIBHASHES}
|
||||
<tr class="{cycle values="odd,even"}">
|
||||
<td>{$rank++}</td>
|
||||
<td>{$CONTRIBHASHES[contrib].account}</td>
|
||||
<td>{$CONTRIBHASHES[contrib].hashrate|number_format}</td>
|
||||
<td>{math equation="round(reward / ( diff * pow(2,32) / ( hashrate * 1000 ) / 3600 / 24),3)" diff=$DIFFICULTY reward=$REWARD hashrate=$CONTRIBHASHES[contrib].hashrate}</td>
|
||||
</tr>
|
||||
{/section}
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="pagination" class="pagination"></div>
|
||||
</center>
|
||||
{include file="global/block_footer.tpl"}
|
||||
@ -0,0 +1,24 @@
|
||||
{include file="global/block_header.tpl" ALIGN="right" BLOCK_HEADER="Top Share Contributers"}
|
||||
<center>
|
||||
<table width="100%" border="0" style="font-size:13px;" class="sortable">
|
||||
<thead>
|
||||
<tr style="background-color:#B6DAFF;">
|
||||
<th align="left">Rank</th>
|
||||
<th align="left" scope="col">User Name</th>
|
||||
<th align="left" scope="col">Shares</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{assign var=rank value=1}
|
||||
{section hashrate $CONTRIBSHARES}
|
||||
<tr class="{cycle values="odd,even"}">
|
||||
<td>{$rank++}</td>
|
||||
<td>{$CONTRIBSHARES[hashrate].account}</td>
|
||||
<td>{$CONTRIBSHARES[hashrate].shares|number_format}</td>
|
||||
</tr>
|
||||
{/section}
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="pagination" class="pagination"></div>
|
||||
</center>
|
||||
{include file="global/block_footer.tpl"}
|
||||
@ -1,11 +1,11 @@
|
||||
-- phpMyAdmin SQL Dump
|
||||
-- version 3.5.1
|
||||
-- version 3.5.8.1deb1
|
||||
-- http://www.phpmyadmin.net
|
||||
--
|
||||
-- Host: localhost
|
||||
-- Generation Time: May 16, 2013 at 09:25 PM
|
||||
-- Server version: 5.5.31-log
|
||||
-- PHP Version: 5.4.15
|
||||
-- Generation Time: May 20, 2013 at 07:35 PM
|
||||
-- Server version: 5.5.31-0ubuntu0.13.04.1
|
||||
-- PHP Version: 5.4.9-4ubuntu2
|
||||
|
||||
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
|
||||
SET time_zone = "+00:00";
|
||||
@ -17,7 +17,7 @@ SET time_zone = "+00:00";
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
|
||||
--
|
||||
-- Database: `mmcfe_ng_db`
|
||||
-- Database: `mmcfe_ng`
|
||||
--
|
||||
|
||||
-- --------------------------------------------------------
|
||||
@ -63,7 +63,7 @@ CREATE TABLE IF NOT EXISTS `blocks` (
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `height` (`height`,`blockhash`),
|
||||
KEY `time` (`time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Discovered blocks persisted from Litecoin Service';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Discovered blocks persisted from Litecoin Service';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
@ -115,7 +115,7 @@ CREATE TABLE IF NOT EXISTS `shares_archive` (
|
||||
`time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `share_id` (`share_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
@ -132,7 +132,7 @@ CREATE TABLE IF NOT EXISTS `statistics_shares` (
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `account_id` (`account_id`),
|
||||
KEY `block_id` (`block_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
@ -143,17 +143,16 @@ CREATE TABLE IF NOT EXISTS `statistics_shares` (
|
||||
CREATE TABLE IF NOT EXISTS `transactions` (
|
||||
`id` int(255) NOT NULL AUTO_INCREMENT,
|
||||
`account_id` int(255) unsigned NOT NULL,
|
||||
`type` enum('Credit','Debit_MP','Debit_AP') DEFAULT NULL,
|
||||
`type` enum('Credit','Debit_MP','Debit_AP','Fee','Donation') DEFAULT NULL,
|
||||
`coin_address` varchar(255) DEFAULT NULL,
|
||||
`amount` double DEFAULT '0',
|
||||
`fee_amount` float DEFAULT '0',
|
||||
`block_id` int(255) DEFAULT NULL,
|
||||
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `block_id` (`block_id`),
|
||||
KEY `account_id` (`account_id`),
|
||||
KEY `type` (`type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user