commit
86381f7052
60
CONTRIBUTING.md
Normal file
60
CONTRIBUTING.md
Normal file
@ -0,0 +1,60 @@
|
||||
# How to contribute
|
||||
|
||||
Third-party patches are much appreciated. This is a rather large project
|
||||
and a single person can not work on it 24/7 to address all issues and
|
||||
feature requests. If you feel comfortable with PHP and Smarty you should
|
||||
consider following this contribution guide!
|
||||
|
||||
## Getting Started
|
||||
|
||||
* Make sure you have a [GitHub account][4].
|
||||
* Submit an [Issue][1] for your issue, assuming one does not already exist.
|
||||
* Clearly describe the issue including steps to reproduce when it is a bug.
|
||||
* Make sure you fill in the earliest version that you know has the issue.
|
||||
* Fork the repository into your GitHub account
|
||||
|
||||
## Making Changes
|
||||
|
||||
* Create a topic branch from where you want to base your work.
|
||||
* This is usually the `next` branch.
|
||||
* Only target release branches if you are certain your fix must be on that
|
||||
branch.
|
||||
* To quickly create a topic branch based on `next`; `git branch
|
||||
fix/next/my_contribution next` then checkout the new branch with `git
|
||||
checkout fix/next/my_contribution`. Please avoid working directly on the
|
||||
`next` branch.
|
||||
* Make commits of logical units.
|
||||
* Check for unnecessary whitespace with `git diff --check` before committing.
|
||||
* Make sure your commit messages are in the proper format.
|
||||
|
||||
````
|
||||
(#99999) Make the example in CONTRIBUTING imperative and concrete
|
||||
|
||||
Without this patch applied the example commit message in the CONTRIBUTING
|
||||
document is not a concrete example. This is a problem because the
|
||||
contributor is left to imagine what the commit message should look like
|
||||
based on a description rather than an example. This patch fixes the
|
||||
problem by making the example concrete and imperative.
|
||||
|
||||
The first line is a real life imperative statement with an issue number
|
||||
from our issue tracker. The body describes the behavior without the patch,
|
||||
why this is a problem, and how the patch fixes the problem when applied.
|
||||
````
|
||||
|
||||
## Submitting Changes
|
||||
|
||||
* Push your changes to a topic branch in your fork of the repository.
|
||||
* Submit a pull request to the origin repository.
|
||||
* Update your issue that you have submitted code and are ready for it to be reviewed.
|
||||
* Include a link to the pull request in the ticket
|
||||
|
||||
# Additional Resources
|
||||
|
||||
* [Issue Tracker][1]
|
||||
* [General GitHub documentation][2]
|
||||
* [GitHub pull request documentation][3]
|
||||
|
||||
[1]: https://github.com/TheSerapher/php-mmcfe-ng/issues "Issue"
|
||||
[2]: http://help.github.com/ "GitHub documentation"
|
||||
[3]: http://help.github.com/send-pull-requests/ "GitHub pull request documentation"
|
||||
[4]: https://github.com/signup/free "GitHub account"
|
||||
120
README.md
120
README.md
@ -1,7 +1,7 @@
|
||||
Description
|
||||
===========
|
||||
|
||||
mmcFE-ng is a web frontend for Pooled LTC Mining.
|
||||
mmcFE-ng is a web frontend for Pooled LTC Mining. A pool using this interface is running at http://pool.grewe.ca
|
||||
|
||||
The web frontend layout is based on mmcFE, the original work by Greedi:
|
||||
https://github.com/Greedi/mmcFE
|
||||
@ -23,6 +23,13 @@ I was hoping to keep this out of the README but apparently people remove or chan
|
||||
at the bottom of the page. For those of you finding my project and are willing to appreciate the work
|
||||
with some hard earned LTC feel free to donate to my LTC address: `Lge95QR2frp9y1wJufjUPCycVsg5gLJPW8`
|
||||
|
||||
Donors
|
||||
======
|
||||
|
||||
These people have supported this project with a donation:
|
||||
|
||||
* [obigal](https://github.com/obigal)
|
||||
|
||||
Requirements
|
||||
============
|
||||
|
||||
@ -36,6 +43,7 @@ in the appropriate forums.
|
||||
* PHP 5.4+
|
||||
* php5-mysqlnd
|
||||
* php5-memcached
|
||||
* php5-curl
|
||||
* MySQL Server
|
||||
* mysql-server
|
||||
* memcached
|
||||
@ -47,6 +55,10 @@ Features
|
||||
|
||||
The following feature have been implemented so far:
|
||||
|
||||
* Reward Systems
|
||||
* Propotional
|
||||
* (Planned) PPS
|
||||
* (Planned) PPLNS
|
||||
* Use of memcache for statistics instead of a cronjob
|
||||
* Web User accounts
|
||||
* Worker accounts
|
||||
@ -63,107 +75,27 @@ The following feature have been implemented so far:
|
||||
Installation
|
||||
============
|
||||
|
||||
Please ensure you fullfill the minimal installation requirements listed above
|
||||
and install any missing packages or software.
|
||||
Please take a look at the [Quick Start Guide](https://github.com/TheSerapher/php-mmcfe-ng/wiki/Quick-Start-Guide). This will give you
|
||||
an idea how to setup `mmcfe-ng`.
|
||||
|
||||
Download Source
|
||||
---------------
|
||||
Contributing
|
||||
============
|
||||
|
||||
Download the (stable) master branch from Github:
|
||||
You can contribute to this project in different ways:
|
||||
|
||||
```
|
||||
git clone -b master git://github.com/TheSerapher/php-mmcfe-ng.git mmcfe-ng
|
||||
```
|
||||
* Report outstanding issues and bugs by creating an [Issue][1]
|
||||
* Suggest feature enhancements also via [Issues][1]
|
||||
* Fork the project, create a branch and file a pull request to improve the code itself
|
||||
|
||||
Or, if you are not using git, use the ZIP file provided:
|
||||
Contact
|
||||
=======
|
||||
|
||||
```
|
||||
wget https://github.com/TheSerapher/php-mmcfe-ng/archive/master.zip
|
||||
unzip master.zip
|
||||
mv php-mmcfe-ng-master mmcfe-ng
|
||||
```
|
||||
|
||||
Permissions
|
||||
-----------
|
||||
|
||||
Please ensure your webuser (e.g. `www-data`, `apache`) has write access to
|
||||
the `mmcfe-ng/public/templates/compile` folder! Otherwise compiled
|
||||
templates can not be stored:
|
||||
|
||||
```
|
||||
sudo chown www-data mmcfe-ng/public/templates/compile
|
||||
```
|
||||
|
||||
Apache2 Configuration
|
||||
---------------------
|
||||
|
||||
Please point your website document root to the `mmcfe-ng/public` folder
|
||||
and enable auto-index for `index.php`.
|
||||
|
||||
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. Your memcache can be
|
||||
configured in the global configuration file (see below).
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Please create the `mmcfe-ng/public/include/config/global.inc.php`
|
||||
configuration from the supplied template
|
||||
`mmcfe-ng/public/include/config/global.inc.dist.php`.
|
||||
|
||||
Pushpoold
|
||||
---------
|
||||
|
||||
Please ensure the passwords are read from the proper table by adding this to your configuration:
|
||||
|
||||
```
|
||||
# database settings
|
||||
"database" : {
|
||||
"engine" : "mysql",
|
||||
"port" : "3306",
|
||||
"name" : "mmcfeng_database_name",
|
||||
"username" : "someuser",
|
||||
"password" : "somepass",
|
||||
"sharelog" : true,
|
||||
"stmt.pwdb":"SELECT `password` FROM `workers` WHERE `username` = ?",
|
||||
"stmt.sharelog":"INSERT INTO shares (rem_host, username, our_result, upstream_result, reason, solution) VALUES (?, ?, ?, ?, ?, ?)"
|
||||
},
|
||||
|
||||
```
|
||||
|
||||
Database
|
||||
========
|
||||
|
||||
Now that the software is ready we need to import the database.
|
||||
You will find the SQL file in the `mmcfe-ng/sql` folder.
|
||||
Import this file into an existing database and you should
|
||||
have the proper structure ready.
|
||||
|
||||
TODO
|
||||
====
|
||||
|
||||
I tried to cover most features available in mmcFE. There might be some missing still
|
||||
(like graphs, some stats) but if you figure there is a core function missing please let
|
||||
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. 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"
|
||||
You can find me on Freenode.net, #mmcfe-ng.
|
||||
|
||||
License and Author
|
||||
==================
|
||||
|
||||
Copyright 2012, Sebastian Grewe
|
||||
Copyright 2012, Sebastian Grewe
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -177,3 +109,5 @@ 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.
|
||||
|
||||
|
||||
[1]: https://github.com/TheSerapher/php-mmcfe-ng/issues "Issue"
|
||||
|
||||
@ -33,7 +33,18 @@ $aAllBlocks = $block->getAllUnconfirmed($config['confirmations']);
|
||||
verbose("ID\tBlockhash\tConfirmations\t\n");
|
||||
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");
|
||||
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");
|
||||
} else {
|
||||
verbose("ORPHAN_ERR");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ($aBlock['confirmations'] == $aBlockInfo['confirmations']) {
|
||||
verbose("SKIPPED\n");
|
||||
} else if ($block->setConfirmations($aBlock['id'], $aBlockInfo['confirmations'])) {
|
||||
|
||||
@ -23,7 +23,8 @@ limitations under the License.
|
||||
require_once('shared.inc.php');
|
||||
|
||||
// Fetch our last block found from the DB as a starting point
|
||||
$strLastBlockHash = @$block->getLast()->blockhash;
|
||||
$aLastBlock = @$block->getLast();
|
||||
$strLastBlockHash = $aLastBlock['blockhash'];
|
||||
if (!$strLastBlockHash) {
|
||||
$strLastBlockHash = '';
|
||||
}
|
||||
@ -39,28 +40,63 @@ if ( $bitcoin->can_connect() === true ){
|
||||
// Nothing to do so bail out
|
||||
if (empty($aTransactions['transactions'])) {
|
||||
verbose("No new transactions since last block\n");
|
||||
exit(0);
|
||||
}
|
||||
} else {
|
||||
|
||||
// Table header
|
||||
verbose("Blockhash\t\tHeight\tAmount\tConfirmations\tDiff\t\tTime\t\t\tStatus\n");
|
||||
// Table header
|
||||
verbose("Blockhash\t\tHeight\tAmount\tConfirmations\tDiff\t\tTime\t\t\tStatus\n");
|
||||
|
||||
foreach ($aTransactions['transactions'] as $iIndex => $aData) {
|
||||
if ( $aData['category'] == 'generate' || $aData['category'] == 'immature' ) {
|
||||
$aBlockInfo = $bitcoin->query('getblock', $aData['blockhash']);
|
||||
$aData['height'] = $aBlockInfo['height'];
|
||||
$aData['difficulty'] = $aBlockInfo['difficulty'];
|
||||
verbose(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");
|
||||
// Let us add those blocks as unaccounted
|
||||
foreach ($aTransactions['transactions'] as $iIndex => $aData) {
|
||||
if ( $aData['category'] == 'generate' || $aData['category'] == 'immature' ) {
|
||||
$aBlockInfo = $bitcoin->query('getblock', $aData['blockhash']);
|
||||
$aData['height'] = $aBlockInfo['height'];
|
||||
$aData['difficulty'] = $aBlockInfo['difficulty'];
|
||||
verbose(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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now with our blocks added we can scan for their upstream shares
|
||||
$aAllBlocks = $block->getAllUnaccounted('ASC');
|
||||
|
||||
// Loop through our unaccounted blocks
|
||||
verbose("Block ID\tBlock Height\tShare ID\tFinder\t\t\tStatus\n");
|
||||
foreach ($aAllBlocks as $iIndex => $aBlock) {
|
||||
if (empty($aBlock['share_id'])) {
|
||||
// Fetch this blocks upstream ID
|
||||
if ($share->setUpstream($block->getLastUpstreamId())) {
|
||||
$iCurrentUpstreamId = $share->getUpstreamId();
|
||||
$iAccountId = $user->getUserId($share->getUpstreamFinder());
|
||||
} else {
|
||||
verbose("Unable to fetch blocks upstream share\n");
|
||||
verbose($share->getError() . "\n");
|
||||
continue;
|
||||
}
|
||||
// Store new information
|
||||
$strStatus = "OK";
|
||||
if (!$block->setShareId($aBlock['id'], $iCurrentUpstreamId))
|
||||
$strStatus = "Share ID Failed";
|
||||
if (!$block->setFinder($aBlock['id'], $iAccountId))
|
||||
$strStatus = "Finder Failed";
|
||||
verbose(
|
||||
$aBlock['id'] . "\t\t"
|
||||
. $aBlock['height'] . "\t\t"
|
||||
. $iCurrentUpstreamId . "\t\t"
|
||||
. "[$iAccountId] " . $user->getUserName($iAccountId) . "\t\t"
|
||||
. $strStatus
|
||||
. "\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
|
||||
@ -32,33 +32,30 @@ if (empty($aAllBlocks)) {
|
||||
$count = 0;
|
||||
foreach ($aAllBlocks as $iIndex => $aBlock) {
|
||||
if (!$aBlock['accounted']) {
|
||||
if ($share->setUpstream(@$aAllBlocks[$iIndex - 1]['time'])) {
|
||||
$share->setLastUpstreamId();
|
||||
}
|
||||
$iPreviousShareId = $aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0;
|
||||
$iCurrentUpstreamId = $aBlock['share_id'];
|
||||
$aAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']);
|
||||
$iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']);
|
||||
|
||||
if ($share->setUpstream($aBlock['time'])) {
|
||||
$iCurrentUpstreamId = $share->getUpstreamId();
|
||||
} else {
|
||||
verbose("Unable to fetch blocks upstream share\n");
|
||||
verbose($share->getError() . "\n");
|
||||
continue;
|
||||
}
|
||||
$aAccountShares = $share->getSharesForAccounts($share->getLastUpstreamId(), $iCurrentUpstreamId);
|
||||
$iRoundShares = $share->getRoundShares($share->getLastUpstreamId(), $iCurrentUpstreamId);
|
||||
// Table header for block details
|
||||
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());
|
||||
verbose($aBlock['id'] . "\t" . $aBlock['height'] . "\t" . $aBlock['time'] . "\t" . $iRoundShares . "\t" . $user->getUserName($aBlock['account_id']) . "\t" . $iCurrentUpstreamId . "\t\t" . $iPreviousShareId);
|
||||
|
||||
if (empty($aAccountShares)) {
|
||||
verbose("\nNo shares found for this block\n\n");
|
||||
sleep(2);
|
||||
continue;
|
||||
}
|
||||
$strStatus = "OK";
|
||||
if (!$block->setFinder($aBlock['id'], $user->getUserId($share->getUpstreamFinder())))
|
||||
$strStatus = "Finder Failed";
|
||||
// Store share information for this block
|
||||
if (!$block->setShares($aBlock['id'], $iRoundShares))
|
||||
$strStatus = "Shares Failed";
|
||||
verbose("\t\t$strStatus\n\n");
|
||||
|
||||
// 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
|
||||
$aData['percentage'] = number_format(round(( 100 / $iRoundShares ) * $aData['valid'], 8), 8);
|
||||
@ -99,10 +96,19 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
|
||||
$strStatus = "Donation Failed";
|
||||
verbose("\t$strStatus\n");
|
||||
}
|
||||
verbose("------------------------------------------------------------------------\n\n");
|
||||
|
||||
// Move counted shares to archive before this blockhash upstream share
|
||||
$share->moveArchive($share->getLastUpstreamId(), $iCurrentUpstreamId, $aBlock['id']);
|
||||
$block->setAccounted($aBlock['id']);
|
||||
if ($config['archive_shares']) $share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId);
|
||||
// Delete all accounted shares
|
||||
if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) {
|
||||
verbose("\nERROR : Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting!\n");
|
||||
exit(1);
|
||||
}
|
||||
// Mark this block as accounted for
|
||||
if (!$block->setAccounted($aBlock['id'])) {
|
||||
verbose("\nERROR : Failed to mark block as accounted! Aborting!\n");
|
||||
}
|
||||
|
||||
verbose("------------------------------------------------------------------------\n\n");
|
||||
}
|
||||
}
|
||||
74
cronjobs/run-crons.sh
Executable file
74
cronjobs/run-crons.sh
Executable file
@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
#########################
|
||||
# #
|
||||
# Configuration Options #
|
||||
# #
|
||||
#########################
|
||||
# PHP Detections, if this fails hard code it
|
||||
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 blockupdate.php auto_payout.php tickerupdate.php"
|
||||
|
||||
# Additional arguments to pass to cronjobs
|
||||
CRONARGS="-v"
|
||||
|
||||
# Output additional runtime information
|
||||
VERBOSE="0"
|
||||
|
||||
################################################################
|
||||
# #
|
||||
# You probably don't need to change anything beyond this point #
|
||||
# #
|
||||
################################################################
|
||||
|
||||
# Change working director to CRONHOME
|
||||
if ! cd $CRONHOME 2>/dev/null; then
|
||||
echo "Unable to change to working directory \$CRONHOME: $CRONHOME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Confiuration checks
|
||||
if [[ -z $PHP_BIN || ! -x $PHP_BIN ]]; then
|
||||
echo "Unable to locate you php binary."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -e 'shared.inc.php' ]]; then
|
||||
echo "Not in cronjobs folder, please ensure \$CRONHOME is set!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Our PID of this shell
|
||||
PID=$$
|
||||
|
||||
if [[ -e $PIDFILE ]]; then
|
||||
echo "Cron seems to be running already"
|
||||
RUNPID=$( cat $PIDFILE )
|
||||
if ps fax | grep -q "^\<$RUNPID\>"; then
|
||||
echo "Process found in process table, aborting"
|
||||
exit 1
|
||||
else
|
||||
echo "Process $RUNPID not found. Plese remove $PIDFILE if process is indeed dead."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Write our PID file
|
||||
echo $PID > $PIDFILE
|
||||
|
||||
for cron in $CRONS; do
|
||||
[[ $VERBOSE == 1 ]] && echo "Running $cron, see output below for details"
|
||||
$PHP_BIN $cron $CRONARGS
|
||||
done
|
||||
|
||||
# Remove pidfile
|
||||
rm -f $PIDFILE
|
||||
36
cronjobs/tickerupdate.php
Executable file
36
cronjobs/tickerupdate.php
Executable 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');
|
||||
|
||||
// Include additional file not set in autoloader
|
||||
require_once(BASEPATH . CLASS_DIR . '/tools.class.php');
|
||||
|
||||
verbose("Running updates\n");
|
||||
if ($aData = $tools->getApi($config['price']['url'], $config['price']['target'])) {
|
||||
if (!$setting->setValue('price', $aData['ticker']['last']))
|
||||
verbose("ERR Table update failed");
|
||||
} else {
|
||||
verbose("ERR Failed download JSON data from " . $config['price']['url'].$config['price']['target'] . "\n");
|
||||
}
|
||||
|
||||
?>
|
||||
@ -3,6 +3,7 @@
|
||||
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 . '/database.inc.php');
|
||||
require_once(INCLUDE_DIR . '/smarty.inc.php');
|
||||
// Load classes that need the above as dependencies
|
||||
@ -12,4 +13,4 @@ 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 . '/settings.class.php');
|
||||
require_once(CLASS_DIR . '/setting.class.php');
|
||||
|
||||
@ -898,6 +898,3 @@ class BitcoinClient extends jsonrpc_client {
|
||||
return $this->query("getaddressesbyaccount", $account);
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-load this class
|
||||
$bitcoin = new BitcoinClient($config['wallet']['type'], $config['wallet']['username'], $config['wallet']['password'], $config['wallet']['host']);
|
||||
|
||||
45
public/include/classes/bitcoinwrapper.class.php
Normal file
45
public/include/classes/bitcoinwrapper.class.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
/**
|
||||
* We use a wrapper class around BitcoinClient to add
|
||||
* some basic caching functionality and some debugging
|
||||
**/
|
||||
class BitcoinWrapper extends BitcoinClient {
|
||||
public function __construct($type, $username, $password, $host, $debug, $memcache) {
|
||||
$this->type = $type;
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
$this->host = $host;
|
||||
// $this->debug is already used
|
||||
$this->oDebug = $debug;
|
||||
$this->memcache = $memcache;
|
||||
return parent::__construct($this->type, $this->username, $this->password, $this->host);
|
||||
}
|
||||
/**
|
||||
* Wrap variouns methods to add caching
|
||||
**/
|
||||
public function getblockcount() {
|
||||
$this->oDebug->append("STA " . __METHOD__, 4);
|
||||
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
return $this->memcache->setCache(__FUNCTION__, parent::getblockcount());
|
||||
}
|
||||
public function getdifficulty() {
|
||||
$this->oDebug->append("STA " . __METHOD__, 4);
|
||||
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
return $this->memcache->setCache(__FUNCTION__, parent::getdifficulty());
|
||||
}
|
||||
public function getestimatedtime($iCurrentPoolHashrate) {
|
||||
$this->oDebug->append("STA " . __METHOD__, 4);
|
||||
if ($iCurrentPoolHashrate == 0) return 0;
|
||||
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
$dDifficulty = parent::getdifficulty();
|
||||
return $this->memcache->setCache(__FUNCTION__, $dDifficulty * pow(2,32) / $iCurrentPoolHashrate);
|
||||
}
|
||||
}
|
||||
|
||||
// Load this wrapper
|
||||
$bitcoin = new BitcoinWrapper($config['wallet']['type'], $config['wallet']['username'], $config['wallet']['password'], $config['wallet']['host'], $debug, $memcache);
|
||||
@ -27,17 +27,27 @@ class Block {
|
||||
return $this->table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specific method to fetch the latest block found
|
||||
* @param none
|
||||
* @return data array Array with database fields as keys
|
||||
**/
|
||||
public function getLast() {
|
||||
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table ORDER BY height DESC LIMIT 1");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$stmt->close();
|
||||
return $result->fetch_object();
|
||||
return $result->fetch_assoc();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all unaccounted blocks
|
||||
* @param order string Sort order, default ASC
|
||||
* @return data array Array with database fields as keys
|
||||
**/
|
||||
public function getAllUnaccounted($order='ASC') {
|
||||
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE accounted = 0 ORDER BY height $order");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
@ -49,8 +59,13 @@ class Block {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all unconfirmed blocks from table
|
||||
* @param confirmations int Required confirmations to consider block confirmed
|
||||
* @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 < ?");
|
||||
$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();
|
||||
@ -61,6 +76,12 @@ class Block {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update confirmations for an existing block
|
||||
* @param block_id int Block ID to update
|
||||
* @param confirmations int New confirmations value
|
||||
* @return bool
|
||||
**/
|
||||
public function setConfirmations($block_id, $confirmations) {
|
||||
$stmt = $this->mysqli->prepare("UPDATE $this->table SET confirmations = ? WHERE id = ?");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
@ -72,6 +93,11 @@ class Block {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all blocks ordered by DESC height
|
||||
* @param order string ASC or DESC ordering
|
||||
* @return data array Array with database fields as keys
|
||||
**/
|
||||
public function getAll($order='DESC') {
|
||||
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table ORDER BY height $order");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
@ -83,6 +109,11 @@ class Block {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new new block to the database
|
||||
* @param block array Block data as an array, see bind_param
|
||||
* @return bool
|
||||
**/
|
||||
public function addBlock($block) {
|
||||
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (height, blockhash, confirmations, amount, difficulty, time) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
@ -99,6 +130,23 @@ class Block {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getLastUpstreamId() {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT MAX(share_id) AS share_id FROM $this->table
|
||||
");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $stmt->bind_result($share_id) && $stmt->fetch())
|
||||
return $share_id ? $share_id : 0;
|
||||
// Catchall
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single column within a single row
|
||||
* @param block_id int Block ID to update
|
||||
* @param field string Column name to update
|
||||
* @param value string Value to insert
|
||||
* @return bool
|
||||
**/
|
||||
private function updateSingle($block_id, $field, $value) {
|
||||
$stmt = $this->mysqli->prepare("UPDATE $this->table SET $field = ? WHERE id = ?");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
@ -114,19 +162,49 @@ class Block {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set finder of a block
|
||||
* @param block_id int Block ID
|
||||
* @param account_id int Account ID of finder
|
||||
* @return bool
|
||||
**/
|
||||
public function setFinder($block_id, $account_id=NULL) {
|
||||
return $this->updateSingle($block_id, 'account_id', $account_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set finding share for a block
|
||||
* @param block_id int Block ID
|
||||
* @param share_id int Upstream valid share ID
|
||||
* @return bool
|
||||
**/
|
||||
public function setShareId($block_id, $share_id) {
|
||||
return $this->updateSingle($block_id, 'share_id', $share_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set counted shares for a block
|
||||
* @param block_id int Block ID
|
||||
* @param shares int Share count
|
||||
* @return bool
|
||||
**/
|
||||
public function setShares($block_id, $shares=NULL) {
|
||||
return $this->updateSingle($block_id, 'shares', $shares);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set block to be accounted for
|
||||
* @param block_id int Block ID
|
||||
* @return bool
|
||||
**/
|
||||
public function setAccounted($block_id=NULL) {
|
||||
if (empty($block_id)) return false;
|
||||
return $this->updateSingle($block_id, 'accounted', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function
|
||||
**/
|
||||
private function checkStmt($bState) {
|
||||
if ($bState ===! true) {
|
||||
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error);
|
||||
@ -137,4 +215,5 @@ class Block {
|
||||
}
|
||||
}
|
||||
|
||||
// Automatically load our class for furhter usage
|
||||
$block = new Block($debug, $mysqli, SALT);
|
||||
|
||||
55
public/include/classes/setting.class.php
Normal file
55
public/include/classes/setting.class.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
class Setting {
|
||||
public function __construct($debug, $mysqli, $salt) {
|
||||
$this->debug = $debug;
|
||||
$this->mysqli = $mysqli;
|
||||
$this->salt = $salt;
|
||||
$this->table = 'settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a value from our table
|
||||
* @param name string Setting name
|
||||
* @return value string Value
|
||||
**/
|
||||
public function getValue($name) {
|
||||
$query = $this->mysqli->prepare("SELECT value FROM $this->table WHERE name=? LIMIT 1");
|
||||
if ($query) {
|
||||
$query->bind_param('s', $name);
|
||||
$query->execute();
|
||||
$query->bind_result($value);
|
||||
$query->fetch();
|
||||
$query->close();
|
||||
} else {
|
||||
$this->debug->append("Failed to fetch variable $name from $this->table");
|
||||
return false;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or update a setting
|
||||
* @param name string Name of the variable
|
||||
* @param value string Variable value
|
||||
* @return bool
|
||||
**/
|
||||
public function setValue($name, $value) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
INSERT INTO $this->table (name, value)
|
||||
VALUES (?, ?)
|
||||
ON DUPLICATE KEY UPDATE value = ?
|
||||
");
|
||||
if ($stmt && $stmt->bind_param('sss', $name, $value, $value) && $stmt->execute())
|
||||
return true;
|
||||
$this->debug->append("Failed to set $name to $value");
|
||||
echo $this->mysqli->error;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$setting = new Setting($debug, $mysqli, SALT);
|
||||
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
class Settings {
|
||||
public function __construct($debug, $mysqli, $salt) {
|
||||
$this->debug = $debug;
|
||||
$this->mysqli = $mysqli;
|
||||
$this->salt = $salt;
|
||||
$this->table = 'settings';
|
||||
}
|
||||
|
||||
public function getValue($name) {
|
||||
$query = $this->mysqli->prepare("SELECT value FROM $this->table WHERE setting=? LIMIT 1");
|
||||
if ($query) {
|
||||
$query->bind_param('s', $name);
|
||||
$query->execute();
|
||||
$query->bind_result($value);
|
||||
$query->fetch();
|
||||
$query->close();
|
||||
} else {
|
||||
$this->debug->append("Failed to fetch variable $name from $this->table");
|
||||
return false;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
$settings = new Settings($debug, $mysqli, SALT);
|
||||
@ -27,13 +27,29 @@ class Share {
|
||||
return $this->sError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch archive tables name for this class
|
||||
* @param none
|
||||
* @return data string Table name
|
||||
**/
|
||||
public function getArchiveTableName() {
|
||||
return $this->tableArchive;
|
||||
}
|
||||
/**
|
||||
* Fetch normal table name for this class
|
||||
* @param none
|
||||
* @return data string Table name
|
||||
**/
|
||||
public function getTableName() {
|
||||
return $this->table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all valid shares for this round
|
||||
* @param previous_upstream int Previous found share accepted by upstream to limit results
|
||||
* @param current_upstream int Current upstream accepted share
|
||||
* @return data int Total amount of counted shares
|
||||
**/
|
||||
public function getRoundShares($previous_upstream=0, $current_upstream) {
|
||||
$stmt = $this->mysqli->prepare("SELECT
|
||||
count(id) as total
|
||||
@ -51,6 +67,12 @@ class Share {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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,
|
||||
@ -90,29 +112,41 @@ class Share {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function moveArchive($previous_upstream=0, $current_upstream,$block_id) {
|
||||
/**
|
||||
* Move accounted shares to archive table, this step is optional
|
||||
* @param previous_upstream int Previous found share accepted by upstream to limit results
|
||||
* @param current_upstream int Current upstream accepted share
|
||||
* @param block_id int Block ID to assign shares to a specific block
|
||||
* @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 ?");
|
||||
$delete_stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id BETWEEN ? AND ?");
|
||||
if ($this->checkStmt($archive_stmt) && $this->checkStmt($delete_stmt)) {
|
||||
$archive_stmt->bind_param('iii', $block_id, $previous_upstream, $current_upstream);
|
||||
$archive_stmt->execute();
|
||||
$delete_stmt->bind_param('ii', $previous_upstream, $current_upstream);
|
||||
$delete_stmt->execute();
|
||||
$delete_stmt->close();
|
||||
if ($this->checkStmt($archive_stmt) && $archive_stmt->bind_param('iii', $block_id, $previous_upstream, $current_upstream) && $archive_stmt->execute()) {
|
||||
$archive_stmt->close();
|
||||
return true;
|
||||
}
|
||||
// Catchall
|
||||
return false;
|
||||
}
|
||||
|
||||
public function deleteAccountedShares($current_upstream, $previous_upstream=0) {
|
||||
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id BETWEEN ? AND ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute())
|
||||
return true;
|
||||
// Catchall
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Set/get last found share accepted by upstream: id and accounts
|
||||
**/
|
||||
public function setLastUpstreamId() {
|
||||
$this->iLastUpstreamId = @$this->oUpstream->id ? $this->oUpstream->id : 0;
|
||||
}
|
||||
public function getLastUpstreamId() {
|
||||
return @$this->iLastUpstreamId;
|
||||
return @$this->iLastUpstreamId ? @$this->iLastUpstreamId : 0;
|
||||
}
|
||||
public function getUpstreamFinder() {
|
||||
return @$this->oUpstream->account;
|
||||
@ -120,27 +154,33 @@ class Share {
|
||||
public function getUpstreamId() {
|
||||
return @$this->oUpstream->id;
|
||||
}
|
||||
public function setUpstream($time='') {
|
||||
$stmt = $this->mysqli->prepare("SELECT
|
||||
/**
|
||||
* Find upstream accepted share that should be valid for a specific block
|
||||
* Assumptions:
|
||||
* * Shares are matching blocks in ASC order
|
||||
* * We can skip all upstream shares of previously found ones used in a block
|
||||
* @param last int Skips all shares up to last to find new share
|
||||
* @return bool
|
||||
**/
|
||||
public function setUpstream($last=0) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id
|
||||
FROM $this->table
|
||||
WHERE upstream_result = 'Y'
|
||||
AND UNIX_TIMESTAMP(time) BETWEEN ? AND (? + 1)
|
||||
AND id > ?
|
||||
ORDER BY id ASC LIMIT 1");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param('ii', $time, $time);
|
||||
$stmt->execute();
|
||||
if (! $result = $stmt->get_result()) {
|
||||
$this->setErrorMessage("No result returned from query");
|
||||
$stmt->close();
|
||||
}
|
||||
$stmt->close();
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $last) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
$this->oUpstream = $result->fetch_object();
|
||||
return true;
|
||||
}
|
||||
// Catchall
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function
|
||||
**/
|
||||
private function checkStmt($bState) {
|
||||
if ($bState ===! true) {
|
||||
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error);
|
||||
|
||||
@ -43,25 +43,13 @@ 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) {
|
||||
$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
|
||||
@ -70,7 +58,7 @@ 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 $this->setCache(__FUNCTION__ . $limit, $result->fetch_all(MYSQLI_ASSOC), 5);
|
||||
return $this->memcache->setCache(__FUNCTION__ . $limit, $result->fetch_all(MYSQLI_ASSOC), 5);
|
||||
// Catchall
|
||||
$this->debug->append("Failed to find blocks:" . $this->mysqli->error);
|
||||
return false;
|
||||
@ -84,6 +72,7 @@ class Statistics {
|
||||
* @return bool
|
||||
**/
|
||||
public function updateShareStatistics($aStats, $iBlockId) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, valid, invalid, block_id) VALUES (?, ?, ?, ?)");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $aStats['id'], $aStats['valid'], $aStats['invalid'], $iBlockId) && $stmt->execute()) return true;
|
||||
// Catchall
|
||||
@ -98,6 +87,7 @@ class Statistics {
|
||||
* @return data object Return our hashrateas an object
|
||||
**/
|
||||
public function getCurrentHashrate() {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT SUM(hashrate) AS hashrate FROM
|
||||
@ -107,7 +97,7 @@ 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 $this->setCache(__FUNCTION__, $result->fetch_object()->hashrate);
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->hashrate);
|
||||
$this->debug->append("Failed to get hashrate: " . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
@ -118,6 +108,7 @@ class Statistics {
|
||||
* @return data object Our share rate in shares per second
|
||||
**/
|
||||
public function getCurrentShareRate() {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT ROUND(SUM(sharerate) / 600, 2) AS sharerate FROM
|
||||
@ -126,7 +117,7 @@ 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 $this->setCache(__FUNCTION__, $result->fetch_object()->sharerate);
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->sharerate);
|
||||
// Catchall
|
||||
$this->debug->append("Failed to fetch share rate: " . $this->mysqli->error);
|
||||
return false;
|
||||
@ -138,6 +129,7 @@ class Statistics {
|
||||
* @return data array invalid and valid shares
|
||||
**/
|
||||
public function getRoundShares() {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($data = $this->memcache->get(__FUNCTION__)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
@ -150,7 +142,7 @@ class Statistics {
|
||||
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 $this->setCache(__FUNCTION__, $result->fetch_assoc());
|
||||
return $this->memcache->setCache(__FUNCTION__, $result->fetch_assoc());
|
||||
// Catchall
|
||||
$this->debug->append("Failed to fetch round shares: " . $this->mysqli->error);
|
||||
return false;
|
||||
@ -162,6 +154,7 @@ class Statistics {
|
||||
* @return data array invalid and valid share counts
|
||||
**/
|
||||
public function getUserShares($account_id) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
@ -184,7 +177,7 @@ class Statistics {
|
||||
AND u.id = ?
|
||||
) AS invalid");
|
||||
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());
|
||||
return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_assoc());
|
||||
// Catchall
|
||||
$this->debug->append("Unable to fetch user round shares: " . $this->mysqli->error);
|
||||
return false;
|
||||
@ -205,7 +198,7 @@ class Statistics {
|
||||
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 $this->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->hashrate);
|
||||
return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->hashrate);
|
||||
// Catchall
|
||||
$this->debug->append("Failed to fetch hashrate: " . $this->mysqli->error);
|
||||
return false;
|
||||
@ -217,6 +210,7 @@ class Statistics {
|
||||
* @return data int Current hashrate in khash/s
|
||||
**/
|
||||
public function getWorkerHashrate($worker_id) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
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
|
||||
@ -226,7 +220,7 @@ class Statistics {
|
||||
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 $this->setCache(__FUNCTION__ . $worker_id, $result->fetch_object()->hashrate);
|
||||
return $this->memcache->setCache(__FUNCTION__ . $worker_id, $result->fetch_object()->hashrate);
|
||||
// Catchall
|
||||
$this->debug->append("Failed to fetch hashrate: " . $this->mysqli->error);
|
||||
return false;
|
||||
@ -239,6 +233,7 @@ class Statistics {
|
||||
* @return data array Users with shares, account or hashrate, account
|
||||
**/
|
||||
public function getTopContributors($type='shares', $limit=15) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($data = $this->memcache->get(__FUNCTION__ . $type . $limit)) return $data;
|
||||
switch ($type) {
|
||||
case 'shares':
|
||||
@ -251,7 +246,7 @@ class Statistics {
|
||||
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));
|
||||
return $this->memcache->setCache(__FUNCTION__ . $type . $limit, $result->fetch_all(MYSQLI_ASSOC));
|
||||
$this->debug->append("Fetching shares failed: ");
|
||||
return false;
|
||||
break;
|
||||
@ -266,7 +261,7 @@ class Statistics {
|
||||
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));
|
||||
return $this->memcache->setCache(__FUNCTION__ . $type . $limit, $result->fetch_all(MYSQLI_ASSOC));
|
||||
$this->debug->append("Fetching shares failed: ");
|
||||
return false;
|
||||
break;
|
||||
@ -280,6 +275,7 @@ class Statistics {
|
||||
* @return data array NOT FINISHED YET
|
||||
**/
|
||||
public function getHourlyHashrateByAccount($account_id) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
@ -300,7 +296,7 @@ class Statistics {
|
||||
AND a.id = ?
|
||||
GROUP BY HOUR(time)");
|
||||
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);
|
||||
return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_all(MYSQLI_ASSOC), 3600);
|
||||
// Catchall
|
||||
$this->debug->append("Failed to fetch hourly hashrate: " . $this->mysqli->error);
|
||||
return false;
|
||||
|
||||
@ -43,6 +43,19 @@ class StatsCache extends Memcached {
|
||||
$this->debug->append("Key not found", 3);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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->set($key, $data, $expiration);
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$memcache = new StatsCache($config, $debug);
|
||||
|
||||
46
public/include/classes/tools.class.php
Normal file
46
public/include/classes/tools.class.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
/**
|
||||
* Helper class for our cronjobs
|
||||
* Implements some common cron tasks outside
|
||||
* the scope of our web application
|
||||
**/
|
||||
class Tools {
|
||||
public function __construct($debug) {
|
||||
$this->debug = $debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch JSON data from an API
|
||||
* @param url string API URL
|
||||
* @param target string API method
|
||||
* @param auth array Optional authentication data to be sent with
|
||||
* @return dec array JSON decoded PHP array
|
||||
**/
|
||||
public function getApi($url, $target, $auth=NULL) {
|
||||
static $ch = null;
|
||||
static $ch = null;
|
||||
if (is_null($ch)) {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; BTCE PHP client; '.php_uname('s').'; PHP/'.phpversion().')');
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_URL, $url . $target);
|
||||
// curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
|
||||
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
|
||||
|
||||
// run the query
|
||||
$res = curl_exec($ch);
|
||||
if ($res === false) throw new Exception('Could not get reply: '.curl_error($ch));
|
||||
$dec = json_decode($res, true);
|
||||
if (!$dec) throw new Exception('Invalid data received, please make sure connection is working and requested API exists');
|
||||
return $dec;
|
||||
}
|
||||
}
|
||||
|
||||
$tools = new Tools($debug);
|
||||
@ -25,6 +25,15 @@ class Transaction {
|
||||
return $this->sError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new transaction to our class table
|
||||
* @param account_id int Account ID to book transaction for
|
||||
* @param amount float Coin amount
|
||||
* @param type string Transaction type [Credit, Debit_AP, Debit_MP, Fee, Donation, Orphan_Credit, Orphan_Fee, Orphan_Donation]
|
||||
* @param block_id int Block ID to link transaction to [optional]
|
||||
* @param coin_address string Coin address for this transaction [optional]
|
||||
* @return bool
|
||||
**/
|
||||
public function addTransaction($account_id, $amount, $type='Credit', $block_id=NULL, $coin_address=NULL) {
|
||||
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, amount, block_id, type, coin_address) VALUES (?, ?, ?, ?, ?)");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
@ -38,10 +47,55 @@ class Transaction {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addDebit($account_id, $amount, $type='AP') {
|
||||
/**
|
||||
* 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);
|
||||
$stmt = $this->mysqli->prepare("
|
||||
UPDATE $this->table
|
||||
SET type = 'Orphan_Credit'
|
||||
WHERE type = 'Credit'
|
||||
AND block_id = ?
|
||||
");
|
||||
if (!($this->checkStmt($stmt) && $stmt->bind_param('i', $block_id) && $stmt->execute())) {
|
||||
$this->debug->append("Failed to set orphan credit transactions for $block_id");
|
||||
return false;
|
||||
}
|
||||
$stmt = $this->mysqli->prepare("
|
||||
UPDATE $this->table
|
||||
SET type = 'Orphan_Fee'
|
||||
WHERE type = 'Fee'
|
||||
AND block_id = ?
|
||||
");
|
||||
if (!($this->checkStmt($stmt) && $stmt->bind_param('i', $block_id) && $stmt->execute())) {
|
||||
$this->debug->append("Failed to set orphan fee transactions for $block_id");
|
||||
return false;
|
||||
}
|
||||
$stmt = $this->mysqli->prepare("
|
||||
UPDATE $this->table
|
||||
SET type = 'Orphan_Donation'
|
||||
WHERE type = 'Donation'
|
||||
AND block_id = ?
|
||||
");
|
||||
if (!($this->checkStmt($stmt) && $stmt->bind_param('i', $block_id) && $stmt->execute())) {
|
||||
$this->debug->append("Failed to set orphan donation transactions for $block_id");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all transactions from start for account_id
|
||||
* @param account_id int Account ID
|
||||
* @param start int Starting point, id of transaction
|
||||
* @return data array Database fields as defined in SELECT
|
||||
**/
|
||||
public function getTransactions($account_id, $start=0) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
t.id AS id,
|
||||
@ -74,7 +128,50 @@ class Transaction {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total balance for all users locked in wallet
|
||||
* @param none
|
||||
* @return data double Amount locked for users
|
||||
**/
|
||||
public function getLockedBalance() {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("
|
||||
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->block->getTableName() . " AS b ON t.block_id = b.id
|
||||
WHERE t.type = 'Credit'
|
||||
AND b.confirmations >= ?
|
||||
) AS t1,
|
||||
(
|
||||
SELECT sum(t.amount) AS debit
|
||||
FROM $this->table AS t
|
||||
WHERE t.type IN ('Debit_MP', 'Debit_AP')
|
||||
) AS t2,
|
||||
(
|
||||
SELECT sum(t.amount) AS other
|
||||
FROM transactions AS t
|
||||
LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
|
||||
WHERE t.type IN ('Donation','Fee')
|
||||
AND b.confirmations >= ?
|
||||
) AS t3");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $this->config['confirmations'], $this->config['confirmations']) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch())
|
||||
return $dBalance;
|
||||
// Catchall
|
||||
$this->setErrorMessage('Unable to find locked credits for all users');
|
||||
$this->debug->append('MySQL query failed : ' . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an accounts total balance
|
||||
* @param account_id int Account ID
|
||||
* @return data float Credit - Debit - Fees - Donation
|
||||
**/
|
||||
public function getBalance($account_id) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT ROUND(IFNULL(t1.credit, 0) - IFNULL(t2.debit, 0) - IFNULL(t3.other, 0), 8) AS balance
|
||||
FROM
|
||||
|
||||
@ -11,10 +11,11 @@ class User {
|
||||
private $user = array();
|
||||
private $tableAccountBalance = 'accountBalance';
|
||||
|
||||
public function __construct($debug, $mysqli, $salt) {
|
||||
public function __construct($debug, $mysqli, $salt, $config) {
|
||||
$this->debug = $debug;
|
||||
$this->mysqli = $mysqli;
|
||||
$this->salt = $salt;
|
||||
$this->config = $config;
|
||||
$this->debug->append("Instantiated User class", 2);
|
||||
}
|
||||
|
||||
@ -34,6 +35,27 @@ class User {
|
||||
return $this->getSingle($username, 'id', 'username', 's');
|
||||
}
|
||||
|
||||
public function getUserEmail($username) {
|
||||
return $this->getSingle($username, 'email', 'username', 's');
|
||||
}
|
||||
|
||||
public function getUserToken($id) {
|
||||
return $this->getSingle($id, 'token', 'id');
|
||||
}
|
||||
|
||||
public function getIdFromToken($token) {
|
||||
return $this->getSingle($token, 'id', 'token', 's');
|
||||
}
|
||||
|
||||
public function setUserToken($id) {
|
||||
$field = array(
|
||||
'name' => 'token',
|
||||
'type' => 's',
|
||||
'value' => hash('sha256', $id.time().$this->salt)
|
||||
);
|
||||
return $this->updateSingle($id, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check user login
|
||||
* @param username string Username
|
||||
@ -41,6 +63,7 @@ class User {
|
||||
* @return bool
|
||||
**/
|
||||
public function checkLogin($username, $password) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$this->debug->append("Checking login for $username with password $password", 2);
|
||||
if ( $this->checkUserPassword($username, $password) ) {
|
||||
$this->createSession($username);
|
||||
@ -56,6 +79,7 @@ class User {
|
||||
* @return bool
|
||||
**/
|
||||
public function checkPin($userId, $pin=false) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$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");
|
||||
$pin_hash = hash('sha256', $pin.$this->salt);
|
||||
@ -76,6 +100,7 @@ class User {
|
||||
* @return array Return result
|
||||
**/
|
||||
private function getSingle($value, $search='id', $field='id', $type="i") {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("SELECT $search FROM $this->table WHERE $field = ? LIMIT 1");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param($type, $value);
|
||||
@ -94,6 +119,7 @@ class User {
|
||||
* @return data array All users with payout setup
|
||||
**/
|
||||
public function getAllAutoPayout() {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
id, username, coin_address, ap_threshold
|
||||
@ -115,6 +141,7 @@ class User {
|
||||
* @return data string Coin Address
|
||||
**/
|
||||
public function getCoinAddress($userID) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
return $this->getSingle($userID, 'coin_address', 'id');
|
||||
}
|
||||
|
||||
@ -124,6 +151,7 @@ class User {
|
||||
* @return data string Coin Address
|
||||
**/
|
||||
public function getDonatePercent($userID) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$dPercent = $this->getSingle($userID, 'donate_percent', 'id');
|
||||
if ($dPercent > 100) $dPercent = 100;
|
||||
if ($dPercent < 0) $dPercent = 0;
|
||||
@ -136,18 +164,17 @@ class User {
|
||||
* @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();
|
||||
$stmt->close();
|
||||
private function updateSingle($id, $field) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("UPDATE $this->table SET " . $field['name'] . " = ? WHERE id = ? LIMIT 1");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param($field['type'].'i', $field['value'], $id) && $stmt->execute())
|
||||
return true;
|
||||
}
|
||||
$this->debug->append("Unable to update " . $field['name'] . " with " . $field['value'] . " for ID $id");
|
||||
return false;
|
||||
}
|
||||
|
||||
private function checkStmt($bState) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($bState ===! true) {
|
||||
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error);
|
||||
$this->setErrorMessage('Internal application Error');
|
||||
@ -156,7 +183,16 @@ class User {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the accounts password
|
||||
* @param userID int User ID
|
||||
* @param current string Current password
|
||||
* @param new1 string New password
|
||||
* @param new2 string New password confirmation
|
||||
* @return bool
|
||||
**/
|
||||
public function updatePassword($userID, $current, $new1, $new2) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($new1 !== $new2) {
|
||||
$this->setErrorMessage( 'New passwords do not match' );
|
||||
return false;
|
||||
@ -180,7 +216,16 @@ class User {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update account information from the edit account page
|
||||
* @param userID int User ID
|
||||
* @param address string new coin address
|
||||
* @param threshold float auto payout threshold
|
||||
* @param donat float donation % of income
|
||||
* @return bool
|
||||
**/
|
||||
public function updateAccount($userID, $address, $threshold, $donate) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$bUser = false;
|
||||
$threshold = min(250, max(0, floatval($threshold)));
|
||||
if ($threshold < 1) $threshold = 0.0;
|
||||
@ -196,7 +241,30 @@ class User {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check API key for authentication
|
||||
* @param key string API key hash
|
||||
* @return bool
|
||||
**/
|
||||
public function checkApiKey($key) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("SELECT api_key, id FROM $this->table WHERE api_key = ? LIMIT 1");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("s", $key) && $stmt->execute() && $stmt->bind_result($api_key, $id) && $stmt->fetch()) {
|
||||
if ($api_key === $key)
|
||||
return $id;
|
||||
}
|
||||
header("HTTP/1.1 401 Unauthorized");
|
||||
die('Access denied');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a password for a user
|
||||
* @param username string Username
|
||||
* @param password string Password
|
||||
* @return bool
|
||||
**/
|
||||
private function checkUserPassword($username, $password) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$user = array();
|
||||
$stmt = $this->mysqli->prepare("SELECT username, id FROM $this->table WHERE username=? AND pass=? LIMIT 1");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
@ -212,7 +280,13 @@ class User {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a PHP session for a user
|
||||
* @param username string Username to create session for
|
||||
* @return none
|
||||
**/
|
||||
private function createSession($username) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$this->debug->append("Log in user to _SESSION", 2);
|
||||
session_regenerate_id(true);
|
||||
$_SESSION['AUTHENTICATED'] = '1';
|
||||
@ -220,25 +294,41 @@ class User {
|
||||
$_SESSION['USERDATA'] = $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log out current user, destroy the session
|
||||
* @param none
|
||||
* @return true
|
||||
**/
|
||||
public function logoutUser() {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
session_destroy();
|
||||
session_regenerate_id(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch this classes table name
|
||||
* @return table string This classes table name
|
||||
**/
|
||||
public function getTableName() {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
return $this->table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch some basic user information to store for later user
|
||||
* @param userID int User ID
|
||||
* return data array Database fields as used in SELECT
|
||||
**/
|
||||
public function getUserData($userID) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$this->debug->append("Fetching user information for user id: $userID");
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
id, username, pin, pass, admin,
|
||||
id, username, pin, api_key, admin,
|
||||
IFNULL(donate_percent, '0') as donate_percent, coin_address, ap_threshold
|
||||
FROM $this->table
|
||||
WHERE id = ? LIMIT 0,1");
|
||||
echo $this->mysqli->error;
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param('i', $userID);
|
||||
if (!$stmt->execute()) {
|
||||
@ -253,7 +343,18 @@ class User {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new user in the system
|
||||
* @param username string Username
|
||||
* @param password1 string Password
|
||||
* @param password2 string Password verification
|
||||
* @param pin int 4 digit PIN code
|
||||
* @param email1 string Email address
|
||||
* @param email2 string Email confirmation
|
||||
* @return bool
|
||||
**/
|
||||
public function register($username, $password1, $password2, $pin, $email1='', $email2='') {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if (strlen($password1) < 8) {
|
||||
$this->setErrorMessage( 'Password is too short, minimum of 8 characters required' );
|
||||
return false;
|
||||
@ -275,15 +376,23 @@ class User {
|
||||
return false;
|
||||
}
|
||||
$apikey = hash("sha256",$username.$salt);
|
||||
$stmt = $this->mysqli->prepare("
|
||||
INSERT INTO $this->table (username, pass, email, pin, api_key)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
");
|
||||
if ($this->mysqli->query("SELECT id FROM $this->table LIMIT 1")->num_rows > 0) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
INSERT INTO $this->table (username, pass, email, pin, api_key)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
");
|
||||
} else {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
INSERT INTO $this->table (username, pass, email, pin, api_key, admin)
|
||||
VALUES (?, ?, ?, ?, ?, 1)
|
||||
");
|
||||
}
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param('sssss', $username, hash("sha256", $password1.$this->salt), $email1, hash("sha256", $pin.$this->salt), $apikey);
|
||||
if (!$stmt->execute()) {
|
||||
$this->setErrorMessage( 'Unable to register' );
|
||||
if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username already exists' );
|
||||
echo $this->mysqli->error;
|
||||
return false;
|
||||
}
|
||||
$stmt->close();
|
||||
@ -291,6 +400,77 @@ class User {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* User a one time token to reset a password
|
||||
* @param token string one time token
|
||||
* @param new1 string New password
|
||||
* @param new2 string New password verification
|
||||
* @return bool
|
||||
**/
|
||||
public function useToken($token, $new1, $new2) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
if ($id = $this->getIdFromToken($token)) {
|
||||
if ($new1 !== $new2) {
|
||||
$this->setErrorMessage( 'New passwords do not match' );
|
||||
return false;
|
||||
}
|
||||
if ( strlen($new1) < 8 ) {
|
||||
$this->setErrorMessage( 'New password is too short, please use more than 8 chars' );
|
||||
return false;
|
||||
}
|
||||
$new = hash('sha256', $new1.$this->salt);
|
||||
$stmt = $this->mysqli->prepare("UPDATE $this->table SET pass = ?, token = NULL WHERE id = ? AND token = ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('sis', $new, $id, $token) && $stmt->execute() && $stmt->affected_rows === 1) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
$this->setErrorMessage("Unable find user for your token");
|
||||
return false;
|
||||
}
|
||||
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) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
// Fetch the users mail address
|
||||
if (!$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");
|
||||
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('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)) {
|
||||
return true;
|
||||
} else {
|
||||
$this->setErrorMessage("Unable to send mail to your address");
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$user = new User($debug, $mysqli, SALT);
|
||||
// Make our class available automatically
|
||||
$user = new User($debug, $mysqli, SALT, $config);
|
||||
|
||||
@ -6,7 +6,7 @@ if (!defined('SECURITY'))
|
||||
|
||||
class Worker {
|
||||
private $sError = '';
|
||||
private $table = 'workers';
|
||||
private $table = 'pool_worker';
|
||||
|
||||
public function __construct($debug, $mysqli, $user, $share, $config) {
|
||||
$this->debug = $debug;
|
||||
@ -41,6 +41,7 @@ class Worker {
|
||||
* @return bool
|
||||
**/
|
||||
public function updateWorkers($account_id, $data) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$username = $this->user->getUserName($account_id);
|
||||
foreach ($data as $key => $value) {
|
||||
// Prefix the WebUser to Worker name
|
||||
@ -61,6 +62,7 @@ class Worker {
|
||||
* @return mixed array Workers and their settings or false
|
||||
**/
|
||||
public function getWorkers($account_id) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("
|
||||
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,
|
||||
@ -81,6 +83,7 @@ class Worker {
|
||||
* @return data mixed int count if any workers are active, false otherwise
|
||||
**/
|
||||
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()) {
|
||||
@ -103,6 +106,7 @@ class Worker {
|
||||
* @return bool
|
||||
**/
|
||||
public function addWorker($account_id, $workerName, $workerPassword) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$username = $this->user->getUserName($account_id);
|
||||
$workerName = "$username.$workerName";
|
||||
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, username, password) VALUES(?, ?, ?)");
|
||||
@ -125,6 +129,7 @@ class Worker {
|
||||
* @return bool
|
||||
**/
|
||||
public function deleteWorker($account_id, $id) {
|
||||
$this->debug->append("STA " . __METHOD__, 4);
|
||||
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE account_id = ? AND id = ?");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param('ii', $account_id, $id);
|
||||
|
||||
@ -21,12 +21,20 @@ define('DEBUG', 0);
|
||||
define('SALT', 'PLEASEMAKEMESOMETHINGRANDOM');
|
||||
|
||||
$config = array(
|
||||
'price' => array(
|
||||
'url' => 'https://btc-e.com/api/2',
|
||||
'target' => '/ltc_usd/ticker'
|
||||
),
|
||||
'website' => array(
|
||||
'name' => 'The Pool',
|
||||
'slogan' => 'Resistance is futile',
|
||||
'email' => 'test@example.com', // Mail address used for notifications
|
||||
),
|
||||
'archive_shares' => true, // Store accounted shares in archive table?
|
||||
'blockexplorer' => 'http://explorer.litecoin.net/search?q=', // URL for block searches, prefixed to each block number
|
||||
'chaininfo' => 'http://allchains.info', // Link to Allchains for Difficulty information
|
||||
'fees' => 0,
|
||||
'difficulty' => '31', // Target difficulty for this pool as set in pushpoold json
|
||||
'difficulty' => '20', // 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(
|
||||
|
||||
@ -37,7 +37,7 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST
|
||||
if ($continue == true && $transaction->addTransaction($_SESSION['USERDATA']['id'], $dBalance, 'Debit_MP', NULL, $sCoinAddress))
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Transaction completed', 'TYPE' => 'success');
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to pushpool service', 'TYPE' => 'errormsg');
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service', 'TYPE' => 'errormsg');
|
||||
}
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than 0.1 LTC to cover transaction fees', 'TYPE' => 'errormsg');
|
||||
@ -61,6 +61,7 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Tempalte specifics
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
?>
|
||||
|
||||
12
public/include/pages/api.inc.php
Normal file
12
public/include/pages/api.inc.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
// Check for valid API key
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
|
||||
header('HTTP/1.1 400 Bad Request');
|
||||
die('400 Bad Request');
|
||||
?>
|
||||
24
public/include/pages/api/getblockcount.inc.php
Normal file
24
public/include/pages/api/getblockcount.inc.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
|
||||
if ($bitcoin->can_connect() === true){
|
||||
if (!$iBlock = $memcache->get('iBlock')) {
|
||||
$iBlock = $bitcoin->query('getblockcount');
|
||||
$memcache->set('iBlock', $iBlock);
|
||||
}
|
||||
} else {
|
||||
$iBlock = 0;
|
||||
}
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('getblockcount' => $iBlock));
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
?>
|
||||
20
public/include/pages/api/getblocksfound.inc.php
Normal file
20
public/include/pages/api/getblocksfound.inc.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
|
||||
// Set a sane limit, overwrite with URL parameter
|
||||
$iLimit = 10;
|
||||
if (@$_REQUEST['limit'])
|
||||
$iLimit = $_REQUEST['limit'];
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('getblocksfound' => $statistics->getBlocksFound($iLimit)));
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
?>
|
||||
15
public/include/pages/api/getcurrentworkers.inc.php
Normal file
15
public/include/pages/api/getcurrentworkers.inc.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('getcurrentworkers' => $worker->getCountAllActiveWorkers()));
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
?>
|
||||
25
public/include/pages/api/getdifficulty.inc.php
Normal file
25
public/include/pages/api/getdifficulty.inc.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
|
||||
// Fetch data from litecoind
|
||||
if ($bitcoin->can_connect() === true){
|
||||
if (!$dDifficulty = $memcache->get('dDifficulty')) {
|
||||
$memcache->set('dDifficulty', $dDifficulty);
|
||||
$dDifficulty = $bitcoin->query('getdifficulty');
|
||||
}
|
||||
} else {
|
||||
$iDifficulty = 1;
|
||||
}
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('getdifficulty' => $dDifficulty));
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
?>
|
||||
18
public/include/pages/api/getestimatedtime.inc.php
Normal file
18
public/include/pages/api/getestimatedtime.inc.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
|
||||
// Estimated time to find the next block
|
||||
$iCurrentPoolHashrate = $statistics->getCurrentHashrate() * 1000;
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('getestimatedtime' => $bitcoin->getestimatedtime($iCurrentPoolHashrate)));
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
?>
|
||||
15
public/include/pages/api/getpoolhashrate.inc.php
Normal file
15
public/include/pages/api/getpoolhashrate.inc.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('getpoolhashrate' => $statistics->getCurrentHashrate()));
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
?>
|
||||
15
public/include/pages/api/getpoolsharerate.inc.php
Normal file
15
public/include/pages/api/getpoolsharerate.inc.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('getpoolsharerate' => $statistics->getCurrentShareRate()));
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
?>
|
||||
26
public/include/pages/api/gettimesincelastblock.inc.php
Normal file
26
public/include/pages/api/gettimesincelastblock.inc.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
// Check user token
|
||||
$id = $user->checkApiKey($_REQUEST['api_key']);
|
||||
|
||||
// Fetch our last block found
|
||||
$aBlocksFoundData = $statistics->getBlocksFound(1);
|
||||
|
||||
// Time since last block
|
||||
$now = new DateTime( "now" );
|
||||
if (!empty($aBlocksFoundData)) {
|
||||
$dTimeSinceLast = ($now->getTimestamp() - $aBlocksFoundData[0]['time']);
|
||||
} else {
|
||||
$dTimeSinceLast = 0;
|
||||
}
|
||||
|
||||
// Output JSON format
|
||||
echo json_encode(array('gettimesincelastblock' => $dTimeSinceLast));
|
||||
|
||||
// Supress master template
|
||||
$supress_master = 1;
|
||||
?>
|
||||
9
public/include/pages/password.inc.php
Normal file
9
public/include/pages/password.inc.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
// Tempalte specifics
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
?>
|
||||
17
public/include/pages/password/change.inc.php
Normal file
17
public/include/pages/password/change.inc.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
if ($_POST['do'] == 'useToken') {
|
||||
if ($user->useToken($_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");
|
||||
?>
|
||||
16
public/include/pages/password/reset.inc.php
Normal file
16
public/include/pages/password/reset.inc.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
// Process password reset request
|
||||
if ($user->resetPassword($_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');
|
||||
}
|
||||
|
||||
// Tempalte specifics, user default template by parent page
|
||||
$smarty->assign("CONTENT", "../default.tpl");
|
||||
?>
|
||||
@ -10,7 +10,7 @@ if ($bitcoin->can_connect() === true){
|
||||
} else {
|
||||
$iDifficulty = 1;
|
||||
$iBlock = 0;
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to pushpool service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
|
||||
$smarty->assign("CURRENTBLOCK", $iBlock);
|
||||
|
||||
22
public/include/pages/statistics/blocks.inc.php
Normal file
22
public/include/pages/statistics/blocks.inc.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY'))
|
||||
die('Hacking attempt');
|
||||
|
||||
|
||||
// Grab the last blocks found
|
||||
$iLimit = 30;
|
||||
$aBlocksFoundData = $statistics->getBlocksFound($iLimit);
|
||||
$aBlockData = $aBlocksFoundData[0];
|
||||
|
||||
// Propagate content our template
|
||||
$smarty->assign("BLOCKSFOUND", $aBlocksFoundData);
|
||||
$smarty->assign("BLOCKLIMIT", $iLimit);
|
||||
|
||||
if ($_SESSION['AUTHENTICATED']) {
|
||||
$smarty->assign("CONTENT", "blocks_found.tpl");
|
||||
} else {
|
||||
$smarty->assign("CONTENT", "default.tpl");
|
||||
}
|
||||
?>
|
||||
@ -6,28 +6,23 @@ if (!defined('SECURITY'))
|
||||
|
||||
// Fetch data from litecoind
|
||||
if ($bitcoin->can_connect() === true){
|
||||
if (!$dDifficulty = $memcache->get('dDifficulty')) {
|
||||
$dDifficulty = $bitcoin->query('getdifficulty');
|
||||
$memcache->set('dDifficulty', $dDifficulty);
|
||||
}
|
||||
if (!$iBlock = $memcache->get('iBlock')) {
|
||||
$iBlock = $bitcoin->query('getblockcount');
|
||||
$memcache->set('iBlock', $iBlock);
|
||||
}
|
||||
$dDifficulty = $bitcoin->getdifficulty();
|
||||
$iBlock = $bitcoin->getblockcount();
|
||||
} else {
|
||||
$iDifficulty = 1;
|
||||
$dDifficulty = 1;
|
||||
$iBlock = 0;
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to pushpool service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
|
||||
// Top share contributors
|
||||
$aContributorsShares = $statistics->getTopContributors('shares', 15);
|
||||
|
||||
// Top hash contributors
|
||||
$aContributorsHashes = $statistics->getTopContributors('hashes', 15);
|
||||
$aContributorsHashes = $statistics->getTopContributors('hashes', 15);
|
||||
|
||||
// Grab the last 10 blocks found
|
||||
$aBlocksFoundData = $statistics->getBlocksFound(10);
|
||||
$iLimit = 10;
|
||||
$aBlocksFoundData = $statistics->getBlocksFound($iLimit);
|
||||
$aBlockData = $aBlocksFoundData[0];
|
||||
|
||||
// Estimated time to find the next block
|
||||
@ -47,6 +42,7 @@ if (!empty($aBlockData)) {
|
||||
$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);
|
||||
|
||||
@ -6,18 +6,12 @@ if (!defined('SECURITY'))
|
||||
|
||||
// Fetch data from litecoind
|
||||
if ($bitcoin->can_connect() === true){
|
||||
if (!$dDifficulty = $memcache->get('dDifficulty')) {
|
||||
$dDifficulty = $bitcoin->query('getdifficulty');
|
||||
$memcache->set('dDifficulty', $dDifficulty);
|
||||
}
|
||||
if (!$iBlock = $memcache->get('iBlock')) {
|
||||
$iBlock = $bitcoin->query('getblockcount');
|
||||
$memcache->set('iBlock', $iBlock);
|
||||
}
|
||||
$dDifficulty = $bitcoin->getdifficulty();
|
||||
$iBlock = $bitcoin->getblockcount();
|
||||
} else {
|
||||
$iDifficulty = 1;
|
||||
$iBlock = 0;
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to pushpool service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
|
||||
}
|
||||
|
||||
$aHourlyHashRates = $statistics->getHourlyHashrateByAccount($_SESSION['USERDATA']['id']);
|
||||
|
||||
@ -22,22 +22,27 @@ $aGlobal = array(
|
||||
'roundshares' => $aRoundShares,
|
||||
'fees' => $config['fees'],
|
||||
'confirmations' => $config['confirmations'],
|
||||
'reward' => $config['reward']
|
||||
'reward' => $config['reward'],
|
||||
'price' => $setting->getValue('price'),
|
||||
'blockexplorer' => $config['blockexplorer'],
|
||||
'chaininfo' => $config['chaininfo']
|
||||
);
|
||||
|
||||
// We don't want these session infos cached
|
||||
$aGlobal['userdata'] = $_SESSION['USERDATA']['id'] ? $user->getUserData($_SESSION['USERDATA']['id']) : array();
|
||||
$aGlobal['userdata']['balance'] = $transaction->getBalance($_SESSION['USERDATA']['id']);
|
||||
if (@$_SESSION['USERDATA']['id']) {
|
||||
$aGlobal['userdata'] = $_SESSION['USERDATA']['id'] ? $user->getUserData($_SESSION['USERDATA']['id']) : array();
|
||||
$aGlobal['userdata']['balance'] = $transaction->getBalance($_SESSION['USERDATA']['id']);
|
||||
|
||||
// Other userdata that we can cache savely
|
||||
$aGlobal['userdata']['shares'] = $statistics->getUserShares($_SESSION['USERDATA']['id']);
|
||||
$aGlobal['userdata']['hashrate'] = $statistics->getUserHashrate($_SESSION['USERDATA']['id']);
|
||||
// Other userdata that we can cache savely
|
||||
$aGlobal['userdata']['shares'] = $statistics->getUserShares($_SESSION['USERDATA']['id']);
|
||||
$aGlobal['userdata']['hashrate'] = $statistics->getUserHashrate($_SESSION['USERDATA']['id']);
|
||||
|
||||
// Some estimations
|
||||
$aGlobal['userdata']['est_block'] = round(( (int)$aGlobal['userdata']['shares']['valid'] / (int)$aRoundShares['valid'] ) * (int)$config['reward'], 3);
|
||||
$aGlobal['userdata']['est_fee'] = round(($config['fees'] / 100) * $aGlobal['userdata']['est_block'], 3);
|
||||
$aGlobal['userdata']['est_donation'] = round((( $aGlobal['userdata']['donate_percent'] / 100) * ($aGlobal['userdata']['est_block'] - $aGlobal['userdata']['est_fee'])), 3);
|
||||
$aGlobal['userdata']['est_payout'] = round($aGlobal['userdata']['est_block'] - $aGlobal['userdata']['est_donation'] - $aGlobal['userdata']['est_fee'], 3);
|
||||
// Some estimations
|
||||
$aGlobal['userdata']['est_block'] = round(( (int)$aGlobal['userdata']['shares']['valid'] / (int)$aRoundShares['valid'] ) * (int)$config['reward'], 3);
|
||||
$aGlobal['userdata']['est_fee'] = round(($config['fees'] / 100) * $aGlobal['userdata']['est_block'], 3);
|
||||
$aGlobal['userdata']['est_donation'] = round((( $aGlobal['userdata']['donate_percent'] / 100) * ($aGlobal['userdata']['est_block'] - $aGlobal['userdata']['est_fee'])), 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);
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
* { padding:0; margin:0; }
|
||||
|
||||
body {
|
||||
@ -460,6 +459,14 @@ a:hover {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.block table tr td.right{
|
||||
text-align: right;
|
||||
}
|
||||
.block table tr th.right{
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
.block table tr td.delete a { color: #666; }
|
||||
.block table tr td.delete a:hover { color: #dd0000; }
|
||||
|
||||
@ -854,7 +861,7 @@ a:hover {
|
||||
/* Block sidebar */
|
||||
|
||||
.block.withsidebar .bendl {
|
||||
/* width: 171px; */
|
||||
width: 191px;
|
||||
background: url(../images/bendsb.gif) bottom left no-repeat;
|
||||
}
|
||||
|
||||
@ -865,7 +872,7 @@ a:hover {
|
||||
}
|
||||
|
||||
.block.withsidebar .block_content .sidebar {
|
||||
width: 190px;
|
||||
width: 210px;
|
||||
float: left;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
10
public/templates/mail/body.tpl
Normal file
10
public/templates/mail/body.tpl
Normal file
@ -0,0 +1,10 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>Hello {$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>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>
|
||||
</body>
|
||||
</html>
|
||||
1
public/templates/mail/subject.tpl
Normal file
1
public/templates/mail/subject.tpl
Normal file
@ -0,0 +1 @@
|
||||
[ {$WEBSITENAME} ] Password Reset Request
|
||||
@ -6,6 +6,7 @@
|
||||
<table>
|
||||
<tbody><tr><td>Username: </td><td>{$GLOBAL.userdata.username}</td></tr>
|
||||
<tr><td>User Id: </td><td>{$GLOBAL.userdata.id}</td></tr>
|
||||
<tr><td>API Key: </td><td>{$GLOBAL.userdata.api_key}</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>
|
||||
<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">[1-250 LTC. Set to '0' for no auto payout]</font></td></tr>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{include file="global/block_header.tpl" BLOCK_HEADER="Transaction Log" BUTTONS=array(Confirmed,Unconfirmed)}
|
||||
{include file="global/block_header.tpl" BLOCK_HEADER="Transaction Log" BUTTONS=array(Confirmed,Unconfirmed,Orphan)}
|
||||
<div class="block_content tab_content" id="Confirmed" style="clear:;">
|
||||
<center>
|
||||
<table cellpadding="1" cellspacing="1" width="98%" class="sortable">
|
||||
@ -77,11 +77,55 @@
|
||||
{/section}
|
||||
<tr>
|
||||
<td colspan="5"><b>Unconfirmed Totals:</b></td>
|
||||
<td><b>{$credits - $debits}</b></td>
|
||||
<td><b>{$credits|default - $debits|default}</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>
|
||||
<div class="block_content tab_content" id="Orphan" style="">
|
||||
<center>
|
||||
<table cellpadding="1" cellspacing="1" width="98%" class="sortable">
|
||||
<thead style="font-size:13px;">
|
||||
<tr>
|
||||
<th class="header" style="cursor: pointer;">TX #</th>
|
||||
<th class="header" style="cursor: pointer;">Date</th>
|
||||
<th class="header" style="cursor: pointer;">TX Type</th>
|
||||
<th class="header" style="cursor: pointer;">Payment Address</th>
|
||||
<th class="header" style="cursor: pointer;">Block #</th>
|
||||
<th class="header" style="cursor: pointer;">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody style="font-size:12px;">
|
||||
{section transaction $TRANSACTIONS}
|
||||
{if (
|
||||
$TRANSACTIONS[transaction].type == 'Orphan_Credit'
|
||||
or $TRANSACTIONS[transaction].type == 'Orphan_Donation'
|
||||
or $TRANSACTIONS[transaction].type == 'Orphan_Fee'
|
||||
)}
|
||||
<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}green{else}red{/if}">{$TRANSACTIONS[transaction].amount}</td>
|
||||
</tr>
|
||||
{if $TRANSACTIONS[transaction].type == Orphan_Credit}
|
||||
{assign var="orphan_credits" value="`$orphan_credits+$TRANSACTIONS[transaction].amount`"}
|
||||
{else}
|
||||
{assign var="orphan_debits" value="`$orphan_debits+$TRANSACTIONS[transaction].amount`"}
|
||||
{/if}
|
||||
{/if}
|
||||
{/section}
|
||||
<tr>
|
||||
<td colspan="5"><b>Orphaned Totals:</b></td>
|
||||
<td><b>{$orphan_credits|default - $orphan_debits|default}</b></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><font color="" sizeze="1">Listed are your orphaned transactions for blocks not part of the main blockchain.</font></p>
|
||||
</center>
|
||||
</div>
|
||||
{include file="global/block_footer.tpl"}
|
||||
|
||||
@ -11,8 +11,8 @@
|
||||
<li><strong>Create account.</strong>
|
||||
<br>
|
||||
<ul>
|
||||
<li>Register <a href="/register">here</a>, or login if you already have account</li>
|
||||
<li>Create a <a href="/accountworkers">worker</a> that will be used by the miner to login</li>
|
||||
<li>Register <a href="{$smarty.server.PHP_SELF}?page=register">here</a>, or login if you already have account</li>
|
||||
<li>Create a <a href="{$smarty.server.PHP_SELF}?page=account&action=workers">worker</a> that will be used by the miner to login</li>
|
||||
</ul>
|
||||
|
||||
</li><li><strong>Download a miner.</strong>
|
||||
@ -49,7 +49,7 @@ Start your miner using the following parameters:
|
||||
If you use a command-line miner, type:
|
||||
<pre> ./minerd --url http://pool.grewe.ca:8337/ --userpass <em>Weblogin</em>.<em>Worker</em>:<em>Worker password</em>
|
||||
</pre>
|
||||
If you want, you can create additional slaves with usernames and passwords of your choice <a href="accountworkers">Here<br></a><br>
|
||||
If you want, you can create additional workers with usernames and passwords of your choice <a href="{$smarty.server.PHP_SELF}?page=account&action=workers">Here<br></a><br>
|
||||
</li>
|
||||
<li><strong>Create a Litecoin address to recieve payments.</strong>
|
||||
<ul>
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<div class="block{if $ALIGN} small {$ALIGN}{/if}" style="{if $BLOCK_STYLE}{$BLOCK_STYLE}{else}clear:none;{/if}">
|
||||
<div class="block{if $ALIGN|default} small {$ALIGN}{/if}" style="{if $BLOCK_STYLE|default}{$BLOCK_STYLE}{else}clear:none;{/if}">
|
||||
<div class="block_head">
|
||||
<div class="bheadl"></div>
|
||||
<div class="bheadr"></div>
|
||||
<h2>{$BLOCK_HEADER|default:"UNKNOWN BLOCK"}</h2>
|
||||
{if $BUTTONS}
|
||||
{if $BUTTONS|default}
|
||||
<ul class="tabs">
|
||||
{foreach from=$BUTTONS item=name}
|
||||
<li style="font-size:9px;"><a href="#{$name}">{$name}</a></li>
|
||||
@ -11,4 +11,4 @@
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="block_content" style="{if $STYLE}{$STYLE}{else}padding:10px;{/if}">
|
||||
<div class="block_content" style="{if $STYLE|default}{$STYLE}{else}padding:10px;{/if}">
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
<div id="ministats">
|
||||
<table border="0">
|
||||
<tr>
|
||||
<td><li>LTC/usd: {$GLOBAL.price|default:"n/a"} </li></td>
|
||||
<td><li>Pool Hashrate: {$GLOBAL.hashrate / 1000} MH/s </li></td>
|
||||
<td><li>Pool Sharerate: {$GLOBAL.sharerate} Shares/s </li></td>
|
||||
<td><li>Pool Workers: {$GLOBAL.workers} </li></td>
|
||||
|
||||
@ -4,5 +4,5 @@
|
||||
<p><input type="password" name="password" value="" id="passForm" maxlength="20"></p>
|
||||
<center><p><input type="submit" class="submit small" value="Login"></p></center>
|
||||
</form>
|
||||
<center><p><a href="/lostPass"><font size="1">Forgot your password?</font></a></p></center>
|
||||
<center><p><a href="{$smarty.server.PHP_SELF}?page=password"><font size="1">Forgot your password?</font></a></p></center>
|
||||
{include file="global/block_footer.tpl"}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{if $GLOBAL.motd}
|
||||
{if $GLOBAL.motd|default}
|
||||
<div id="generic_infobox" style="margin-left:3px;">
|
||||
<font color="orange" size="1">***Message of the Day***<br></font>
|
||||
<font size="1">{$GLOBAL.motd|escape:'html'}</font>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<ul id="nav">
|
||||
<li><a href="{$smarty.server.PHP_SELF}">Home</a></li>
|
||||
{if $smarty.session.AUTHENTICATED == 1}
|
||||
{if $smarty.session.AUTHENTICATED|default:"0" == 1}
|
||||
<li><a href="">My Account</a>
|
||||
<ul>
|
||||
<li><a href="{$smarty.server.PHP_SELF}?page=account&action=edit">Edit Account</a></li>
|
||||
@ -10,21 +10,20 @@
|
||||
</ul>
|
||||
</li>
|
||||
{/if}
|
||||
{if $smarty.session.AUTHENTICATED == 1 && $GLOBAL.userdata.admin == 1}<li><a href="#">Admin Panel</a></li>{/if}
|
||||
{if $smarty.session.AUTHENTICATED|default:"0" == 1 && $GLOBAL.userdata.admin == 1}<li><a href="#">Admin Panel</a></li>{/if}
|
||||
<li><a href="{$smarty.server.PHP_SELF}?page=statistics">Statistics</a>
|
||||
<ul>
|
||||
<li><a href="{$smarty.server.PHP_SELF}?page=statistics&action=pool">Pool Stats</a></li>
|
||||
{if $smarty.session.AUTHENTICATED}<li><a href="{$smarty.server.PHP_SELF}?page=statistics&action=block">Block Stats</a></li>{/if}
|
||||
{if $smarty.session.AUTHENTICATED|default}<li><a href="{$smarty.server.PHP_SELF}?page=statistics&action=blocks">Block Stats</a></li>{/if}
|
||||
</ul>
|
||||
</li>
|
||||
<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=pplns">About</a>
|
||||
<li><a href="{$smarty.server.PHP_SELF}?page=about&action=pool">About</a>
|
||||
<ul>
|
||||
<li><a href="{$smarty.server.PHP_SELF}?page=about&action=pplns">PPLNS Payout</a></li>
|
||||
<li><a href="{$smarty.server.PHP_SELF}?page=about&action=pool">This Pool</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="{$smarty.server.PHP_SELF}?page=news">News</a></li>
|
||||
{if $smarty.session.AUTHENTICATED == 1}<li><a href="{$smarty.server.PHP_SELF}?page=logout">Logout</a></li>{else}<li><a href="{$smarty.server.PHP_SELF}?page=register">Register</a></li>{/if}
|
||||
{if $smarty.session.AUTHENTICATED|default == 1}<li><a href="{$smarty.server.PHP_SELF}?page=logout">Logout</a></li>{else}<li><a href="{$smarty.server.PHP_SELF}?page=register">Register</a></li>{/if}
|
||||
</ul>
|
||||
|
||||
@ -5,57 +5,57 @@
|
||||
<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>
|
||||
<table class="sidebar" style="width: 196px">
|
||||
<tr><td colspan="2"><b>Your Hashrate</b></td></tr>
|
||||
<tr><td colspan="2" 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>
|
||||
<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>
|
||||
<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><i>{$GLOBAL.roundshares.valid}</i> <font size='1px'></font></b></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><i>{$GLOBAL.roundshares.valid}</i></td>
|
||||
<td class="right"><i>{$GLOBAL.roundshares.valid|number_format}</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Pool Invalid</b></td>
|
||||
<td><i>{$GLOBAL.roundshares.invalid}</i></td>
|
||||
<td class="right"><i>{$GLOBAL.roundshares.invalid|number_format}</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Your Invalid</b></td>
|
||||
<td><i>{$GLOBAL.userdata.shares.invalid}</i><font size='1px'></font></td>
|
||||
<td class="right"><i>{$GLOBAL.userdata.shares.invalid|number_format}</i><font size='1px'></font></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>Round Estimate</u></b></td>
|
||||
<td colspan="2"><b><u>LTC Round Estimate</u></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Block</b></td>
|
||||
<td>{$GLOBAL.userdata.est_block} LTC</td>
|
||||
<td class="right">{$GLOBAL.userdata.est_block|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Fees</b></td>
|
||||
<td>{$GLOBAL.userdata.est_fee} LTC</td>
|
||||
<td class="right">{$GLOBAL.userdata.est_fee|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Donation</b></td>
|
||||
<td>{$GLOBAL.userdata.est_donation} LTC</td>
|
||||
<td class="right">{$GLOBAL.userdata.est_donation|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Payout</b></td>
|
||||
<td>{$GLOBAL.userdata.est_payout} LTC</td>
|
||||
<td class="right">{$GLOBAL.userdata.est_payout|number_format:"3"}</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>
|
||||
<tr><td colspan="2" class="right"><b>{$GLOBAL.userdata.balance|default:"0"} LTC</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="bendl"></div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{if $GLOBAL.userdata.username}
|
||||
<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='/osList'>donating</a> <b></i>{$GLOBAL.userdata.donate_percent}%</b> <i>of your earnings)</i></font></h2>
|
||||
{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>
|
||||
{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}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<title>ThePool</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=7" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
|
||||
<link rel="shortcut icon" href="#" />
|
||||
<link rel="stylesheet" href="{$PATH}/css/mainstyle.css" type="text/css" />
|
||||
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
|
||||
<script src="{$PATH}/js/jquery.browser.js"></script>
|
||||
@ -45,14 +45,14 @@
|
||||
</div>
|
||||
<div class="block_content">
|
||||
<div class="sidebar">
|
||||
{if $smarty.session.AUTHENTICATED}
|
||||
{if $smarty.session.AUTHENTICATED|default}
|
||||
{include file="global/sidebar.tpl"}
|
||||
{else}
|
||||
{include file="global/login.tpl"}
|
||||
{/if}
|
||||
</div>
|
||||
<div class="sidebar_content" id="sb1">
|
||||
{if is_array($smarty.session.POPUP)}
|
||||
<div class="sidebar_content" id="sb1" style="margin-left: 13px">
|
||||
{if is_array($smarty.session.POPUP|default)}
|
||||
{section popup $smarty.session.POPUP}
|
||||
<div class="message {$smarty.session.POPUP[popup].TYPE|default:"success"}"><p>{$smarty.session.POPUP[popup].CONTENT}</p></div>
|
||||
{/section}
|
||||
|
||||
12
public/templates/mmcFE/password/change/default.tpl
Normal file
12
public/templates/mmcFE/password/change/default.tpl
Normal file
@ -0,0 +1,12 @@
|
||||
{include file="global/block_header.tpl" BLOCK_HEADER="Change Password"}
|
||||
<form action="{$smarty.server.PHP_SELF}" method="post">
|
||||
<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">
|
||||
<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>
|
||||
</tbody></table>
|
||||
<input type="submit" class="submit long" value="Change Password"></form>
|
||||
{include file="global/block_footer.tpl"}
|
||||
8
public/templates/mmcFE/password/default.tpl
Normal file
8
public/templates/mmcFE/password/default.tpl
Normal file
@ -0,0 +1,8 @@
|
||||
{include file="global/block_header.tpl" BLOCK_HEADER="Reset Password" BLOCK_STYLE="clear:none;"}
|
||||
<form action="" method="POST">
|
||||
<input type="hidden" name="page" value="password">
|
||||
<input type="hidden" name="action" value="reset">
|
||||
<p>If you have an email set for your account, enter your username to get your password reset</p>
|
||||
<p><input type="text" value="{$smarty.post.username}" name="username"><input class="submit small" type="submit" value="Reset"></p>
|
||||
</form>
|
||||
{include file="global/block_footer.tpl"}
|
||||
@ -1,4 +1,4 @@
|
||||
{include file="global/block_header.tpl" BLOCK_HEADER="Last 10 Blocks Found" BLOCK_STYLE="clear:none;" BUTTONS=array(More)}
|
||||
{include file="global/block_header.tpl" BLOCK_HEADER="Last $BLOCKLIMIT Blocks Found" BLOCK_STYLE="clear:none;"}
|
||||
<center>
|
||||
<table class="stats_lastblocks" width="100%" style="font-size:13px;">
|
||||
<thead>
|
||||
@ -7,20 +7,25 @@
|
||||
<th scope="col" align="left">Validity</th>
|
||||
<th scope="col" align="left">Finder</th>
|
||||
<th scope="col" align="left">Date / Time</th>
|
||||
<th scope="col" align="left">Difficulty</th>
|
||||
<th scope="col" align="left">Shares</th>
|
||||
<th class="right" scope="col">Difficulty</th>
|
||||
<th class="right" scope="col">Shares</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{assign var=rank value=1}
|
||||
{section block $BLOCKSFOUND}
|
||||
<tr class="{cycle values="odd,even"}">
|
||||
<td>{$BLOCKSFOUND[block].height}</td>
|
||||
<td>{if $BLOCKSFOUND[block].confirmations >= $GLOBAL.confirmations}<font color="green">Confirmed</font>{else}{$GLOBAL.confirmations - $BLOCKSFOUND[block].confirmations} left{/if}</td>
|
||||
<td><a href="{$GLOBAL.blockexplorer}{$BLOCKSFOUND[block].height}" target="_blank">{$BLOCKSFOUND[block].height}</a></td>
|
||||
<td>
|
||||
{if $BLOCKSFOUND[block].confirmations >= $GLOBAL.confirmations}
|
||||
<font color="green">Confirmed</font>
|
||||
{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>{$BLOCKSFOUND[block].time|date_format:"%d/%m/%Y %H:%M:%S"}</td>
|
||||
<td>{$BLOCKSFOUND[block].difficulty|number_format:"8"}</td>
|
||||
<td>{$BLOCKSFOUND[block].shares|number_format}</td>
|
||||
<td class="right">{$BLOCKSFOUND[block].difficulty|number_format:"8"}</td>
|
||||
<td class="right">{$BLOCKSFOUND[block].shares|number_format}</td>
|
||||
</tr>
|
||||
{/section}
|
||||
</tbody>
|
||||
|
||||
@ -17,29 +17,30 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="leftheader">Next Network Block</td>
|
||||
<td><a href="http://explorer.litecoin.net/search?q={$CURRENTBLOCK + 1}" target="_new">{$CURRENTBLOCK + 1}</a> <font size="1"> (Current: <a href="http://explorer.litecoin.net/search?q={$CURRENTBLOCK}" target="_new">{$CURRENTBLOCK})</a></font></td>
|
||||
<td><a href="{$GLOBAL.blockexplorer}{$CURRENTBLOCK + 1}" target="_new">{$CURRENTBLOCK + 1}</a> <font size="1"> (Current: <a href="{$GLOBAL.blockexplorer}{$CURRENTBLOCK}" target="_new">{$CURRENTBLOCK})</a></font></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="leftheader">Last Block Found</td>
|
||||
<td><a href="http://explorer.litecoin.net/search?q={$LASTBLOCK}" target="_new">{$LASTBLOCK|default:"0"}</a></td>
|
||||
<td><a href="{$GLOBAL.blockexplorer}{$LASTBLOCK}" target="_new">{$LASTBLOCK|default:"0"}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="leftheader">Current Difficulty</td>
|
||||
<td><a href="http://allchains.info" target="_new"><font size="2">{$DIFFICULTY}</font></a></td>
|
||||
<td><a href="{$GLOBAL.chaininfo}" target="_new"><font size="2">{$DIFFICULTY}</font></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="leftheader">Est. Avg. Time per Round</td>
|
||||
<td>{$ESTTIME|seconds_to_words}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="leftheader">Est. Avg. Shares per Round</td>
|
||||
<td>{($ESTTIME * $GLOBAL.sharerate)|number_format:"0"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="leftheader">Time Since Last Block</td>
|
||||
<td>{$TIMESINCELAST|seconds_to_words}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<ul>
|
||||
<li><font color="orange">Server stats are also available in JSON format <a href="/api" target="_api">HERE</a></font></li>
|
||||
</ul>
|
||||
{include file="global/block_footer.tpl"}
|
||||
|
||||
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
<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>
|
||||
<th class="right" scope="col">KH/s</th>
|
||||
<th class="right">Ł/Day<font size="1"> (est)</font></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -16,16 +16,16 @@
|
||||
<tr{if $GLOBAL.userdata.username == $CONTRIBHASHES[contrib].account}{assign var=listed value=1} style="background-color:#99EB99;"{else} class="{cycle values="odd,even"}"{/if}>
|
||||
<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>
|
||||
<td class="right">{$CONTRIBHASHES[contrib].hashrate|number_format}</td>
|
||||
<td class="right">{math equation="round(reward / ( diff * pow(2,32) / ( hashrate * 1000 ) / 3600 / 24), 3)" diff=$DIFFICULTY reward=$REWARD hashrate=$CONTRIBHASHES[contrib].hashrate}</td>
|
||||
</tr>
|
||||
{/section}
|
||||
{if $listed != 1}
|
||||
<tr style="background-color:#99EB99;">
|
||||
<td>n/a</td>
|
||||
<td>{$GLOBAL.userdata.username}</td>
|
||||
<td>{$GLOBAL.userdata.hashrate}</td>
|
||||
<td>{math equation="round(reward / ( diff * pow(2,32) / ( hashrate * 1000 ) / 3600 / 24), 3)" diff=$DIFFICULTY reward=$REWARD hashrate=$GLOBAL.userdata.hashrate}</td>
|
||||
<td class="right">{$GLOBAL.userdata.hashrate}</td>
|
||||
<td class="right">{math equation="round(reward / ( diff * pow(2,32) / ( hashrate * 1000 ) / 3600 / 24), 3)" diff=$DIFFICULTY reward=$REWARD hashrate=$GLOBAL.userdata.hashrate}</td>
|
||||
</tr>
|
||||
{/if}
|
||||
</tbody>
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
<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>
|
||||
<th scope="col">User Name</th>
|
||||
<th class="right" scope="col">Shares</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -15,14 +15,14 @@
|
||||
<tr{if $GLOBAL.userdata.username == $CONTRIBSHARES[hashrate].account}{assign var=listed value=1} style="background-color:#99EB99;"{else} class="{cycle values="odd,even"}"{/if}>
|
||||
<td>{$rank++}</td>
|
||||
<td>{$CONTRIBSHARES[hashrate].account}</td>
|
||||
<td>{$CONTRIBSHARES[hashrate].shares|number_format}</td>
|
||||
<td class="right">{$CONTRIBSHARES[hashrate].shares|number_format}</td>
|
||||
</tr>
|
||||
{/section}
|
||||
{if $listed != 1}
|
||||
<tr style="background-color:#99EB99;">
|
||||
<td>n/a</td>
|
||||
<td>{$GLOBAL.userdata.username}</td>
|
||||
<td>{$GLOBAL.userdata.shares.valid}</td>
|
||||
<td class="right">{$GLOBAL.userdata.shares.valid}</td>
|
||||
</tr>
|
||||
{/if}
|
||||
</tbody>
|
||||
|
||||
@ -6,10 +6,8 @@
|
||||
</div>
|
||||
|
||||
<div class="block_content" style="padding:10px;">
|
||||
<p>
|
||||
|
||||
</p><p>As mentioned in various places this site is experimental and does not come with any support nor warranty.</p>
|
||||
<p>Nevertheless, you can find contact information on my main website: <a href="http://www.grewe.ca">http://www.grewe.ca</a></p>
|
||||
<p>This product comes 'as-is' without any warranty. Please check the Apache License, Version 2.0, for details.</p>
|
||||
<p>If you would like to get in touch you can find me on Freenode IRC @ #mmcfe-ng.</p>
|
||||
|
||||
</div> <!-- nested block ends -->
|
||||
<div class="bendl"></div>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
-- http://www.phpmyadmin.net
|
||||
--
|
||||
-- Host: localhost
|
||||
-- Generation Time: May 20, 2013 at 07:35 PM
|
||||
-- Generation Time: May 31, 2013 at 02:31 PM
|
||||
-- Server version: 5.5.31-0ubuntu0.13.04.1
|
||||
-- PHP Version: 5.4.9-4ubuntu2
|
||||
|
||||
@ -36,6 +36,7 @@ CREATE TABLE IF NOT EXISTS `accounts` (
|
||||
`sessionTimeoutStamp` int(255) DEFAULT NULL,
|
||||
`pin` varchar(255) NOT NULL COMMENT 'four digit pin to allow account changes',
|
||||
`api_key` varchar(255) DEFAULT NULL,
|
||||
`token` varchar(65) DEFAULT NULL,
|
||||
`donate_percent` float DEFAULT '0',
|
||||
`ap_threshold` float DEFAULT '0',
|
||||
`coin_address` varchar(255) DEFAULT NULL,
|
||||
@ -53,17 +54,18 @@ CREATE TABLE IF NOT EXISTS `blocks` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`height` int(10) unsigned NOT NULL,
|
||||
`blockhash` char(65) NOT NULL,
|
||||
`confirmations` int(10) unsigned NOT NULL,
|
||||
`confirmations` int(10) NOT NULL,
|
||||
`amount` double NOT NULL,
|
||||
`difficulty` double NOT NULL,
|
||||
`time` int(11) NOT NULL,
|
||||
`accounted` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`account_id` int(255) unsigned DEFAULT NULL,
|
||||
`shares` int(255) unsigned DEFAULT NULL,
|
||||
`share_id` int(255) DEFAULT NULL,
|
||||
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';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
@ -72,9 +74,10 @@ CREATE TABLE IF NOT EXISTS `blocks` (
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `settings` (
|
||||
`setting` varchar(255) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`value` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`setting`)
|
||||
PRIMARY KEY (`name`),
|
||||
UNIQUE KEY `setting` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
@ -97,7 +100,7 @@ CREATE TABLE IF NOT EXISTS `shares` (
|
||||
KEY `upstream_result` (`upstream_result`),
|
||||
KEY `our_result` (`our_result`),
|
||||
KEY `username` (`username`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
@ -112,10 +115,11 @@ CREATE TABLE IF NOT EXISTS `shares_archive` (
|
||||
`our_result` enum('Y','N') DEFAULT NULL,
|
||||
`upstream_result` enum('Y','N') DEFAULT NULL,
|
||||
`block_id` int(10) unsigned NOT NULL,
|
||||
`time` datetime DEFAULT NULL,
|
||||
`time` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `share_id` (`share_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes';
|
||||
UNIQUE KEY `share_id` (`share_id`),
|
||||
KEY `time` (`time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
@ -132,7 +136,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,7 +147,7 @@ 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','Fee','Donation') DEFAULT NULL,
|
||||
`type` enum('Credit','Debit_MP','Debit_AP','Donation','Fee','Orphan_Credit','Orphan_Fee','Orphan_Donation') DEFAULT NULL,
|
||||
`coin_address` varchar(255) DEFAULT NULL,
|
||||
`amount` double DEFAULT '0',
|
||||
`block_id` int(255) DEFAULT NULL,
|
||||
@ -152,15 +156,15 @@ CREATE TABLE IF NOT EXISTS `transactions` (
|
||||
KEY `block_id` (`block_id`),
|
||||
KEY `account_id` (`account_id`),
|
||||
KEY `type` (`type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `workers`
|
||||
-- Table structure for table `pool_worker`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `workers` (
|
||||
CREATE TABLE IF NOT EXISTS `pool_worker` (
|
||||
`id` int(255) NOT NULL AUTO_INCREMENT,
|
||||
`account_id` int(255) NOT NULL,
|
||||
`username` char(50) DEFAULT NULL,
|
||||
@ -169,7 +173,7 @@ CREATE TABLE IF NOT EXISTS `workers` (
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `username` (`username`),
|
||||
KEY `account_id` (`account_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user