commit
a841880fe7
@ -51,17 +51,17 @@ if (empty($aTransactions['transactions'])) {
|
|||||||
// Let us add those blocks as unaccounted
|
// Let us add those blocks as unaccounted
|
||||||
foreach ($aTransactions['transactions'] as $iIndex => $aData) {
|
foreach ($aTransactions['transactions'] as $iIndex => $aData) {
|
||||||
if ( $aData['category'] == 'generate' || $aData['category'] == 'immature' ) {
|
if ( $aData['category'] == 'generate' || $aData['category'] == 'immature' ) {
|
||||||
$aBlockInfo = $bitcoin->query('getblock', $aData['blockhash']);
|
$aBlockRPCInfo = $bitcoin->query('getblock', $aData['blockhash']);
|
||||||
$config['reward_type'] == 'block' ? $aData['amount'] = $aData['amount'] : $aData['amount'] = $config['reward'];
|
$config['reward_type'] == 'block' ? $aData['amount'] = $aData['amount'] : $aData['amount'] = $config['reward'];
|
||||||
$aData['height'] = $aBlockInfo['height'];
|
$aData['height'] = $aBlockRPCInfo['height'];
|
||||||
$aData['difficulty'] = $aBlockInfo['difficulty'];
|
$aData['difficulty'] = $aBlockRPCInfo['difficulty'];
|
||||||
$log->logInfo(substr($aData['blockhash'], 0, 15) . "...\t" .
|
$log->logInfo(substr($aData['blockhash'], 0, 15) . "...\t" .
|
||||||
$aData['height'] . "\t" .
|
$aData['height'] . "\t" .
|
||||||
$aData['amount'] . "\t" .
|
$aData['amount'] . "\t" .
|
||||||
$aData['confirmations'] . "\t\t" .
|
$aData['confirmations'] . "\t\t" .
|
||||||
$aData['difficulty'] . "\t" .
|
$aData['difficulty'] . "\t" .
|
||||||
strftime("%Y-%m-%d %H:%M:%S", $aData['time']));
|
strftime("%Y-%m-%d %H:%M:%S", $aData['time']));
|
||||||
if ( ! empty($aBlockInfo['flags']) && preg_match('/proof-of-stake/', $aBlockInfo['flags']) ) {
|
if ( ! empty($aBlockRPCInfo['flags']) && preg_match('/proof-of-stake/', $aBlockRPCInfo['flags']) ) {
|
||||||
$log->logInfo("Block above with height " . $aData['height'] . " not added to database, proof-of-stake block!");
|
$log->logInfo("Block above with height " . $aData['height'] . " not added to database, proof-of-stake block!");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -78,38 +78,65 @@ if (empty($aAllBlocks)) {
|
|||||||
$log->logDebug('No new blocks without share_id found in database');
|
$log->logDebug('No new blocks without share_id found in database');
|
||||||
} else {
|
} else {
|
||||||
// Loop through our unaccounted blocks
|
// Loop through our unaccounted blocks
|
||||||
$log->logInfo("Block ID\t\tHeight\tAmount\tShare ID\tShares\tFinder\tType");
|
$log->logInfo("Block ID\tHeight\t\tAmount\tShare ID\tShares\tFinder\t\tType");
|
||||||
foreach ($aAllBlocks as $iIndex => $aBlock) {
|
foreach ($aAllBlocks as $iIndex => $aBlock) {
|
||||||
if (empty($aBlock['share_id'])) {
|
if (empty($aBlock['share_id'])) {
|
||||||
|
// Fetch share information
|
||||||
|
$iPreviousShareId = $block->getLastShareId();
|
||||||
|
if ( !$iPreviousShareId && $block->getBlockCount() > 1) {
|
||||||
|
$iPreviousShareId = 0;
|
||||||
|
// $log->logError('Unable to find highest share ID found so far, assuming share ID 0 as previous found upstream share.');
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch this blocks upstream ID
|
// Fetch this blocks upstream ID
|
||||||
$aBlockInfo = $bitcoin->query('getblock', $aBlock['blockhash']);
|
$aBlockRPCInfo = $bitcoin->query('getblock', $aBlock['blockhash']);
|
||||||
if ($share->setUpstream($aBlockInfo, $block->getLastUpstreamId())) {
|
if ($share->findUpstreamShare($aBlockRPCInfo, $iPreviousShareId)) {
|
||||||
$iCurrentUpstreamId = $share->getUpstreamId();
|
$iCurrentUpstreamId = $share->getUpstreamShareId();
|
||||||
$iAccountId = $user->getUserId($share->getUpstreamFinder());
|
// Out of order share detection
|
||||||
|
if ($iCurrentUpstreamId < $iPreviousShareId) {
|
||||||
|
// Fetch our offending block
|
||||||
|
$aBlockError = $block->getBlockByShareId($iPreviousShareId);
|
||||||
|
$log->logError('E0001: The block with height ' . $aBlock['height'] . ' found share ' . $iCurrentUpstreamId . ' which is < than ' . $iPreviousShareId . ' of block ' . $aBlockError['height'] . '.');
|
||||||
|
if ( !$aShareError = $share->getShareById($aBlockError['share_id']) || !$aShareCurrent = $share->getShareById($iCurrentUpstreamId)) {
|
||||||
|
// We were not able to fetch all shares that were causing this detection to trigger, bail out
|
||||||
|
$log->logFatal('E0002: Failed to fetch both offending shares ' . $iCurrentUpstreamId . ' and ' . $iPreviousShareId . '. Block height: ' . $aBlock['height']);
|
||||||
|
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||||
|
$monitoring->setStatus($cron_name . "_message", "message", "E0002: Upstream shares not found");
|
||||||
|
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
// Shares seem to be out of order, so lets change them
|
||||||
|
if ( !$share->updateShareById($iCurrentUpstreamId, $aShareError) || !$share->updateShareById($iPreviousShareId, $aShareCurrent)) {
|
||||||
|
// We couldn't update one of the shares! That might mean they have been deleted already
|
||||||
|
$log->logFatal('E0003: Failed to change shares order!');
|
||||||
|
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||||
|
$monitoring->setStatus($cron_name . "_message", "message", "E0003: Failed share update");
|
||||||
|
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
// Reset our offending block so the next run re-checks the shares
|
||||||
|
if (!$block->setShareId($aBlockError['id'], NULL) && !$block->setFinder($aBlockError['id'], NULL) || !$block->setShares($aBlockError['id'], NULL)) {
|
||||||
|
$log->logFatal('E0004: Failed to reset previous block: ' . $aBlockError['height']);
|
||||||
|
$log->logError('Failed to reset block in database: ' . $aBlockError['height']);
|
||||||
|
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||||
|
$monitoring->setStatus($cron_name . "_message", "message", "E0004: Failed to reset block");
|
||||||
|
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||||
|
$monitoring->setStatus($cron_name . "_message", "message", "Out of Order Share detected, autofixed");
|
||||||
|
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||||
|
exit(0);
|
||||||
|
} else {
|
||||||
|
$iRoundShares = $share->getRoundShares($iPreviousShareId, $iCurrentUpstreamId);
|
||||||
|
$iAccountId = $user->getUserId($share->getUpstreamFinder());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$log->logFatal('Unable to fetch blocks upstream share, aborted:' . $share->getError());
|
$log->logFatal('E0005: Unable to fetch blocks upstream share, aborted:' . $share->getError());
|
||||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||||
$monitoring->setStatus($cron_name . "_message", "message", "Unable to fetch blocks " . $aBlock['height'] . " upstream share: " . $share->getError());
|
$monitoring->setStatus($cron_name . "_message", "message", "Unable to fetch blocks " . $aBlock['height'] . " upstream share: " . $share->getError());
|
||||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||||
exit;
|
exit(1);
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch share information
|
|
||||||
if (!$iPreviousShareId = $block->getLastShareId()) {
|
|
||||||
$iPreviousShareId = 0;
|
|
||||||
$log->logInfo('Unable to find highest share ID found so far, if this is your first block, this is normal.');
|
|
||||||
}
|
|
||||||
$iRoundShares = $share->getRoundShares($iPreviousShareId, $iCurrentUpstreamId);
|
|
||||||
|
|
||||||
// Store new information
|
|
||||||
if (!$block->setShareId($aBlock['id'], $iCurrentUpstreamId))
|
|
||||||
$log->logError('Failed to update share ID in database for block ' . $aBlock['height']);
|
|
||||||
if (!$block->setFinder($aBlock['id'], $iAccountId))
|
|
||||||
$log->logError('Failed to update finder account ID in database for block ' . $aBlock['height']);
|
|
||||||
if (!$block->setShares($aBlock['id'], $iRoundShares))
|
|
||||||
$log->logError('Failed to update share count in database for block ' . $aBlock['height']);
|
|
||||||
if ($config['block_bonus'] > 0 && !$transaction->addTransaction($iAccountId, $config['block_bonus'], 'Bonus', $aBlock['id'])) {
|
|
||||||
$log->logError('Failed to create Bonus transaction in database for user ' . $user->getUserName($iAccountId) . ' for block ' . $aBlock['height']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$log->logInfo(
|
$log->logInfo(
|
||||||
@ -122,6 +149,17 @@ if (empty($aAllBlocks)) {
|
|||||||
. $share->share_type
|
. $share->share_type
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Store new information
|
||||||
|
if (!$block->setShareId($aBlock['id'], $iCurrentUpstreamId))
|
||||||
|
$log->logError('Failed to update share ID in database for block ' . $aBlock['height']);
|
||||||
|
if (!$block->setFinder($aBlock['id'], $iAccountId))
|
||||||
|
$log->logError('Failed to update finder account ID in database for block ' . $aBlock['height']);
|
||||||
|
if (!$block->setShares($aBlock['id'], $iRoundShares))
|
||||||
|
$log->logError('Failed to update share count in database for block ' . $aBlock['height']);
|
||||||
|
if ($config['block_bonus'] > 0 && !$transaction->addTransaction($iAccountId, $config['block_bonus'], 'Bonus', $aBlock['id'])) {
|
||||||
|
$log->logError('Failed to create Bonus transaction in database for user ' . $user->getUserName($iAccountId) . ' for block ' . $aBlock['height']);
|
||||||
|
}
|
||||||
|
|
||||||
if ($setting->getValue('disable_notifications') != 1) {
|
if ($setting->getValue('disable_notifications') != 1) {
|
||||||
// Notify users
|
// Notify users
|
||||||
$aAccounts = $notification->getNotificationAccountIdByType('new_block');
|
$aAccounts = $notification->getNotificationAccountIdByType('new_block');
|
||||||
|
|||||||
@ -8,8 +8,12 @@ if (!defined('SECURITY'))
|
|||||||
// some cross-class functions.
|
// some cross-class functions.
|
||||||
class Base {
|
class Base {
|
||||||
private $sError = '';
|
private $sError = '';
|
||||||
|
protected $table = '';
|
||||||
private $values = array(), $types = '';
|
private $values = array(), $types = '';
|
||||||
|
|
||||||
|
public function getTableName() {
|
||||||
|
return $this->table;
|
||||||
|
}
|
||||||
public function setDebug($debug) {
|
public function setDebug($debug) {
|
||||||
$this->debug = $debug;
|
$this->debug = $debug;
|
||||||
}
|
}
|
||||||
@ -50,6 +54,13 @@ class Base {
|
|||||||
return $this->sError;
|
return $this->sError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getAllAssoc($value, $field='id', $type='i') {
|
||||||
|
$this->debug->append("STA " . __METHOD__, 4);
|
||||||
|
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE $field = ? LIMIT 1");
|
||||||
|
if ($this->checkStmt($stmt) && $stmt->bind_param($type, $value) && $stmt->execute() && $result = $stmt->get_result())
|
||||||
|
return $result->fetch_assoc();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Get a single row from the table
|
* Get a single row from the table
|
||||||
* @param value string Value to search for
|
* @param value string Value to search for
|
||||||
|
|||||||
@ -56,6 +56,30 @@ class Block {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a specific block, by share_id
|
||||||
|
* @param share_id int Blocks share_id
|
||||||
|
* @return data array Block information from DB
|
||||||
|
**/
|
||||||
|
public function getBlockByShareId($share_id) {
|
||||||
|
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE share_id = ? LIMIT 1");
|
||||||
|
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $share_id) && $stmt->execute() && $result = $stmt->get_result())
|
||||||
|
return $result->fetch_assoc();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a specific block, by id
|
||||||
|
* @param share_id int Blocks share_id
|
||||||
|
* @return data array Block information from DB
|
||||||
|
**/
|
||||||
|
public function getBlockById($id) {
|
||||||
|
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE id = ? LIMIT 1");
|
||||||
|
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $id) && $stmt->execute() && $result = $stmt->get_result())
|
||||||
|
return $result->fetch_assoc();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get our last, highest share ID inserted for a block
|
* Get our last, highest share ID inserted for a block
|
||||||
* @param none
|
* @param none
|
||||||
|
|||||||
@ -4,9 +4,8 @@
|
|||||||
if (!defined('SECURITY'))
|
if (!defined('SECURITY'))
|
||||||
die('Hacking attempt');
|
die('Hacking attempt');
|
||||||
|
|
||||||
class Share {
|
class Share Extends Base {
|
||||||
private $sError = '';
|
protected $table = 'shares';
|
||||||
private $table = 'shares';
|
|
||||||
private $tableArchive = 'shares_archive';
|
private $tableArchive = 'shares_archive';
|
||||||
private $oUpstream;
|
private $oUpstream;
|
||||||
private $iLastUpstreamId;
|
private $iLastUpstreamId;
|
||||||
@ -22,14 +21,6 @@ class Share {
|
|||||||
$this->debug->append("Instantiated Share class", 2);
|
$this->debug->append("Instantiated Share class", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get and set methods
|
|
||||||
private function setErrorMessage($msg) {
|
|
||||||
$this->sError = $msg;
|
|
||||||
}
|
|
||||||
public function getError() {
|
|
||||||
return $this->sError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch archive tables name for this class
|
* Fetch archive tables name for this class
|
||||||
* @param none
|
* @param none
|
||||||
@ -38,13 +29,43 @@ class Share {
|
|||||||
public function getArchiveTableName() {
|
public function getArchiveTableName() {
|
||||||
return $this->tableArchive;
|
return $this->tableArchive;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch normal table name for this class
|
* Fetch a single share by ID
|
||||||
* @param none
|
* @param id int Share ID
|
||||||
* @return data string Table name
|
* @return array Share data
|
||||||
**/
|
**/
|
||||||
public function getTableName() {
|
public function getShareById($id) {
|
||||||
return $this->table;
|
return $this->getAllAssoc($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an entire shares data
|
||||||
|
**/
|
||||||
|
public function updateShareById($id, $data) {
|
||||||
|
$this->debug->append("STA " . __METHOD__, 4);
|
||||||
|
$sql = "UPDATE $this->table SET";
|
||||||
|
$start = true;
|
||||||
|
// Remove ID column
|
||||||
|
unset($data['id']);
|
||||||
|
foreach ($data as $column => $value) {
|
||||||
|
$start == true ? $sql .= " $column = ? " : $sql .= ", $column = ?";
|
||||||
|
$start = false;
|
||||||
|
switch($column) {
|
||||||
|
case 'difficulty':
|
||||||
|
$this->addParam('d', $value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$this->addParam('s', $value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$sql .= " WHERE id = ? LIMIT 1";
|
||||||
|
$this->addParam('i', $id);
|
||||||
|
$stmt = $this->mysqli->prepare($sql);
|
||||||
|
if ($this->checkStmt($stmt) && call_user_func_array( array($stmt, 'bind_param'), $this->getParam()) && $stmt->execute())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -229,7 +250,7 @@ class Share {
|
|||||||
public function getUpstreamFinder() {
|
public function getUpstreamFinder() {
|
||||||
return @$this->oUpstream->account;
|
return @$this->oUpstream->account;
|
||||||
}
|
}
|
||||||
public function getUpstreamId() {
|
public function getUpstreamShareId() {
|
||||||
return @$this->oUpstream->id;
|
return @$this->oUpstream->id;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -240,7 +261,7 @@ class Share {
|
|||||||
* @param last int Skips all shares up to last to find new share
|
* @param last int Skips all shares up to last to find new share
|
||||||
* @return bool
|
* @return bool
|
||||||
**/
|
**/
|
||||||
public function setUpstream($aBlock, $last=0) {
|
public function findUpstreamShare($aBlock, $last=0) {
|
||||||
// Many use stratum, so we create our stratum check first
|
// Many use stratum, so we create our stratum check first
|
||||||
$version = pack("I*", sprintf('%08d', $aBlock['version']));
|
$version = pack("I*", sprintf('%08d', $aBlock['version']));
|
||||||
$previousblockhash = pack("H*", swapEndian($aBlock['previousblockhash']));
|
$previousblockhash = pack("H*", swapEndian($aBlock['previousblockhash']));
|
||||||
@ -358,18 +379,10 @@ class Share {
|
|||||||
return $result->fetch_object()->share_id;
|
return $result->fetch_object()->share_id;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function
|
|
||||||
**/
|
|
||||||
private function checkStmt($bState) {
|
|
||||||
if ($bState ===! true) {
|
|
||||||
$this->debug->append("Failed to prepare statement: " . $this->mysqli->error);
|
|
||||||
$this->setErrorMessage('Internal application Error');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$share = new Share($debug, $mysqli, $user, $block, $config);
|
$share = new Share($debug, $mysqli, $user, $block, $config);
|
||||||
|
$share->setMysql($mysqli);
|
||||||
|
$share->setConfig($config);
|
||||||
|
$share->setUser($user);
|
||||||
|
$share->setBlock($block);
|
||||||
|
|||||||
@ -5,7 +5,8 @@ if (!defined('SECURITY'))
|
|||||||
die('Hacking attempt');
|
die('Hacking attempt');
|
||||||
|
|
||||||
class Transaction extends Base {
|
class Transaction extends Base {
|
||||||
private $sError = '', $table = 'transactions';
|
private $sError = '';
|
||||||
|
protected $table = 'transactions';
|
||||||
public $num_rows = 0, $insert_id = 0;
|
public $num_rows = 0, $insert_id = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user