ghost
3 years ago
6 changed files with 403 additions and 202 deletions
@ -1,98 +1,189 @@ |
|||||||
<?php |
<?php |
||||||
|
|
||||||
require_once('config.php'); |
$semaphore = sem_get(1); |
||||||
require_once('library/mysql.php'); |
|
||||||
require_once('library/keva.php'); |
|
||||||
require_once('library/hash.php'); |
|
||||||
require_once('library/base58.php'); |
|
||||||
require_once('library/base58check.php'); |
|
||||||
require_once('library/crypto.php'); |
|
||||||
|
|
||||||
$db = new MySQL(); |
if (false !== sem_acquire($semaphore, 1)) { |
||||||
$node = new Keva(); |
|
||||||
|
|
||||||
$node->host = KEVA_HOST; |
require_once('config.php'); |
||||||
$node->username = KEVA_USERNAME; |
require_once('library/mysql.php'); |
||||||
$node->password = KEVA_PASSWORD; |
require_once('library/kevacoin.php'); |
||||||
$node->port = KEVA_PORT; |
require_once('library/hash.php'); |
||||||
|
require_once('library/base58.php'); |
||||||
|
require_once('library/base58check.php'); |
||||||
|
require_once('library/crypto.php'); |
||||||
|
require_once('library/helper.php'); |
||||||
|
|
||||||
$blockLast = $db->getLastBlock(); |
$db = new MySQL(DB_HOST, DB_PORT, DB_NAME, DB_USERNAME, DB_PASSWORD); |
||||||
$blockTotal = $node->getblockcount(); |
$kevaCoin = new KevaCoin(KEVA_PROTOCOL, KEVA_HOST, KEVA_PORT, KEVA_USERNAME, KEVA_PASSWORD); |
||||||
|
|
||||||
$response = []; |
$blockLast = $db->getLastBlock(); |
||||||
|
$blockTotal = $kevaCoin->getblockcount(); |
||||||
|
|
||||||
if ($blockTotal > $blockLast) { |
if (!$blockTotal) { |
||||||
|
echo "API connection error.\n"; |
||||||
|
exit; |
||||||
|
} |
||||||
|
|
||||||
|
$response = []; |
||||||
|
|
||||||
for ($blockCurrent = $blockLast; $blockCurrent <= $blockLast + STEP_BLOCK_LIMIT; $blockCurrent++) { |
if (CRAWLER_DEBUG) { |
||||||
|
echo "scanning blockhain...\n"; |
||||||
|
} |
||||||
|
|
||||||
if ($blockHash = $node->getblockhash($blockCurrent)) { |
for ($blockCurrent = ($blockLast + 1); $blockCurrent <= $blockLast + STEP_BLOCK_LIMIT; $blockCurrent++) { |
||||||
|
|
||||||
$blockData = $node->getblock($blockHash); |
if ($blockCurrent > $blockTotal) { |
||||||
|
|
||||||
if (!$blockId = $db->getBlock($blockCurrent)) { |
if (CRAWLER_DEBUG) { |
||||||
$blockId = $db->addBlock($blockCurrent); |
echo "database is up to date.\n"; |
||||||
} |
} |
||||||
|
|
||||||
foreach ($blockData['tx'] as $transaction) { |
break; |
||||||
|
} |
||||||
|
|
||||||
$transactionRaw = $node->getrawtransaction($transaction, 1); |
if (CRAWLER_DEBUG) { |
||||||
|
echo sprintf("reading block %s\n", $blockCurrent); |
||||||
|
} |
||||||
|
|
||||||
foreach($transactionRaw['vout'] as $vout) { |
if (!$blockHash = $kevaCoin->getblockhash($blockCurrent)) { |
||||||
|
|
||||||
$asmArray = explode(' ', $vout['scriptPubKey']['asm']); |
if (CRAWLER_DEBUG) { |
||||||
|
echo "could not read the block hash. waiting for reconnect.\n"; |
||||||
|
} |
||||||
|
|
||||||
if($asmArray[0] == 'OP_KEVA_NAMESPACE' || $asmArray[0] == 'OP_KEVA_PUT') { // OP_KEVA_DELETE |
break; |
||||||
|
} |
||||||
|
|
||||||
$hash = Base58Check::encode($asmArray[1], false , 0 , false); |
if (!$blockData = $kevaCoin->getblock($blockHash)) { |
||||||
$nameSpace = $node->keva_get($hash, '_KEVA_NS_'); |
|
||||||
|
|
||||||
$nameSpaceValue = strip_tags(html_entity_decode($nameSpace['value'], ENT_QUOTES, 'UTF-8')); |
if (CRAWLER_DEBUG) { |
||||||
|
echo "could not read the block data. waiting for reconnect.\n"; |
||||||
|
} |
||||||
|
|
||||||
if ((empty(KEVA_NS) || (!empty(KEVA_NS) && KEVA_NS == $nameSpaceValue))) { |
break; |
||||||
|
} |
||||||
|
|
||||||
if (!$nameSpaceId = $db->getNameSpace($hash)) { |
if (!$blockId = $db->getBlock($blockCurrent)) { |
||||||
$nameSpaceId = $db->addNameSpace($hash, $nameSpaceValue); |
$blockId = $db->addBlock($blockCurrent); |
||||||
} |
|
||||||
|
|
||||||
if (!$db->getData($blockId, $nameSpaceId)) { |
if (CRAWLER_DEBUG) { |
||||||
$db->addData($blockId, |
echo sprintf("add block %s\n", $blockCurrent); |
||||||
$nameSpaceId, |
} |
||||||
$transactionRaw['time'], |
} |
||||||
$transactionRaw['size'], |
|
||||||
$transactionRaw['txid'], |
$lostTransactions = 0; |
||||||
strip_tags(html_entity_decode(@hex2bin($asmArray[2]), ENT_QUOTES, 'UTF-8')), |
|
||||||
strip_tags(html_entity_decode(@hex2bin($asmArray[3]), ENT_QUOTES, 'UTF-8'))); |
foreach ($blockData['tx'] as $transaction) { |
||||||
} |
|
||||||
|
if (!$transactionRaw = $kevaCoin->getrawtransaction($transaction)) { |
||||||
|
|
||||||
|
$lostTransactions++; |
||||||
|
|
||||||
|
$db->setLostTransactions($blockId, $lostTransactions); |
||||||
|
|
||||||
|
if (CRAWLER_DEBUG) { |
||||||
|
echo sprintf("could not read the transaction %s. skipped.\n", $transaction); |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
foreach($transactionRaw['vout'] as $vout) { |
||||||
|
|
||||||
|
$asmArray = explode(' ', $vout['scriptPubKey']['asm']); |
||||||
|
|
||||||
|
if (in_array($asmArray[0], ['OP_KEVA_NAMESPACE', 'OP_KEVA_PUT', 'OP_KEVA_DELETE'])) { |
||||||
|
|
||||||
|
$hash = Base58Check::encode($asmArray[1], false , 0 , false); |
||||||
|
|
||||||
|
switch ($asmArray[0]) { |
||||||
|
|
||||||
|
case 'OP_KEVA_DELETE': |
||||||
|
|
||||||
|
$key = filterString(decodeString($asmArray[2])); |
||||||
|
$value = ''; |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 'OP_KEVA_NAMESPACE': |
||||||
|
|
||||||
|
$key = '_KEVA_NS_'; |
||||||
|
$value = filterString(decodeString($asmArray[2])); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
|
||||||
|
$key = filterString(decodeString($asmArray[2])); |
||||||
|
$value = filterString(decodeString($asmArray[3])); |
||||||
|
} |
||||||
|
|
||||||
|
if (!$nameSpaceId = $db->getNameSpace($hash)) { |
||||||
|
$nameSpaceId = $db->addNameSpace($hash); |
||||||
|
|
||||||
if (CRAWLER_DEBUG) { |
if (CRAWLER_DEBUG) { |
||||||
$response[] = [ |
echo sprintf("add namespace %s\n", $hash); |
||||||
'blocktotal'=> $blockTotal, |
|
||||||
'block' => $blockCurrent, |
|
||||||
'blockhash' => $transactionRaw['blockhash'], |
|
||||||
'txid' => $transactionRaw['txid'], |
|
||||||
'version' => $transactionRaw['version'], |
|
||||||
'size' => $transactionRaw['size'], |
|
||||||
'time' => $transactionRaw['time'], |
|
||||||
'blocktime' => $transactionRaw['blocktime'], |
|
||||||
'namehash' => $hash, |
|
||||||
'title' => $nameSpaceValue, |
|
||||||
'key' => strip_tags(html_entity_decode(@hex2bin($asmArray[2]), ENT_QUOTES, 'UTF-8')), |
|
||||||
'vale' => strip_tags(html_entity_decode(@hex2bin($asmArray[3]), ENT_QUOTES, 'UTF-8')) |
|
||||||
]; |
|
||||||
} |
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!$dataId = $db->getData($transactionRaw['txid'])) { |
||||||
|
$dataId = $db->addData($blockId, |
||||||
|
$nameSpaceId, |
||||||
|
$transactionRaw['time'], |
||||||
|
$transactionRaw['size'], |
||||||
|
$transactionRaw['txid'], |
||||||
|
$key, |
||||||
|
$value, |
||||||
|
($key == '_KEVA_NS_'), |
||||||
|
empty($value)); |
||||||
|
|
||||||
|
if ($value) { |
||||||
|
|
||||||
|
$db->setDataKeyDeleted($nameSpaceId, $key, false); |
||||||
|
|
||||||
|
if (CRAWLER_DEBUG) { |
||||||
|
echo sprintf("add new key/value %s\n", $transactionRaw['txid']); |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
$db->setDataKeyDeleted($nameSpaceId, $key, true); |
||||||
|
|
||||||
|
if (CRAWLER_DEBUG) { |
||||||
|
echo sprintf("delete key %s from namespace %s\n", $key, $hash); |
||||||
|
} |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
|
if (CRAWLER_DEBUG) { |
||||||
|
$response[] = [ |
||||||
|
'blocktotal'=> $blockTotal, |
||||||
|
'block' => $blockCurrent, |
||||||
|
'blockhash' => $transactionRaw['blockhash'], |
||||||
|
'txid' => $transactionRaw['txid'], |
||||||
|
'version' => $transactionRaw['version'], |
||||||
|
'size' => $transactionRaw['size'], |
||||||
|
'time' => $transactionRaw['time'], |
||||||
|
'blocktime' => $transactionRaw['blocktime'], |
||||||
|
'namehash' => $hash, |
||||||
|
'key' => $key, |
||||||
|
'value' => $value |
||||||
|
]; |
||||||
|
} |
||||||
} |
} |
||||||
} |
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
} else { |
|
||||||
|
|
||||||
// @TODO block not found |
// Debug |
||||||
} |
if (CRAWLER_DEBUG) { |
||||||
|
echo "scanning completed.\n"; |
||||||
|
# print_r($response); |
||||||
} |
} |
||||||
} |
|
||||||
|
|
||||||
// Debug |
sem_release($semaphore); |
||||||
if (CRAWLER_DEBUG) { |
|
||||||
print_r($response); |
} else { |
||||||
|
echo "database locked by the another process...\n"; |
||||||
} |
} |
||||||
|
@ -0,0 +1,15 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
function decodeString($string) { |
||||||
|
|
||||||
|
if (is_numeric($string) && $string < 0xFFFFFFFF) { |
||||||
|
return mb_chr($string, 'ASCII'); |
||||||
|
} else { |
||||||
|
return hex2bin($string); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function filterString($string) { |
||||||
|
|
||||||
|
return strip_tags(html_entity_decode($string, ENT_QUOTES, 'UTF-8')); |
||||||
|
} |
@ -1,110 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class Keva { |
|
||||||
|
|
||||||
public $username; |
|
||||||
public $password; |
|
||||||
|
|
||||||
public $host; |
|
||||||
public $port; |
|
||||||
public $url; |
|
||||||
|
|
||||||
public $proto = 'http'; |
|
||||||
public $CACertificate = null; |
|
||||||
|
|
||||||
public $status; |
|
||||||
public $error; |
|
||||||
public $rawResponse; |
|
||||||
public $response; |
|
||||||
|
|
||||||
private $id = 0; |
|
||||||
|
|
||||||
public function setSSL($certificate = null) { |
|
||||||
$this->proto = 'https'; |
|
||||||
$this->CACertificate = $certificate; |
|
||||||
} |
|
||||||
|
|
||||||
public function __call($method, $params) { |
|
||||||
|
|
||||||
$this->status = null; |
|
||||||
$this->error = null; |
|
||||||
$this->rawResponse = null; |
|
||||||
$this->response = null; |
|
||||||
|
|
||||||
$params = array_values($params); |
|
||||||
|
|
||||||
$this->id++; |
|
||||||
|
|
||||||
$request = json_encode(array( |
|
||||||
'method' => $method, |
|
||||||
'params' => $params, |
|
||||||
'id' => $this->id |
|
||||||
)); |
|
||||||
|
|
||||||
$curl = curl_init("{$this->proto}://{$this->host}:{$this->port}/{$this->url}"); |
|
||||||
$options = array( |
|
||||||
CURLOPT_HTTPAUTH => CURLAUTH_BASIC, |
|
||||||
CURLOPT_USERPWD => $this->username . ':' . $this->password, |
|
||||||
CURLOPT_RETURNTRANSFER => true, |
|
||||||
CURLOPT_FOLLOWLOCATION => true, |
|
||||||
CURLOPT_MAXREDIRS => 10, |
|
||||||
CURLOPT_HTTPHEADER => array('Content-type: text/plain'), |
|
||||||
CURLOPT_POST => true, |
|
||||||
CURLOPT_POSTFIELDS => $request |
|
||||||
); |
|
||||||
|
|
||||||
if (ini_get('open_basedir')) { |
|
||||||
unset($options[CURLOPT_FOLLOWLOCATION]); |
|
||||||
} |
|
||||||
|
|
||||||
if ($this->proto == 'https') { |
|
||||||
if (!empty($this->CACertificate)) { |
|
||||||
$options[CURLOPT_CAINFO] = $this->CACertificate; |
|
||||||
$options[CURLOPT_CAPATH] = DIRNAME($this->CACertificate); |
|
||||||
} else { |
|
||||||
$options[CURLOPT_SSL_VERIFYPEER] = false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
curl_setopt_array($curl, $options); |
|
||||||
|
|
||||||
$this->rawResponse = curl_exec($curl); |
|
||||||
$this->response = json_decode($this->rawResponse, true); |
|
||||||
|
|
||||||
$this->status = curl_getinfo($curl, CURLINFO_HTTP_CODE); |
|
||||||
|
|
||||||
$curl_error = curl_error($curl); |
|
||||||
|
|
||||||
curl_close($curl); |
|
||||||
|
|
||||||
if (!empty($curl_error)) { |
|
||||||
$this->error = $curl_error; |
|
||||||
} |
|
||||||
|
|
||||||
if ($this->response['error']) { |
|
||||||
$this->error = $this->response['error']['message']; |
|
||||||
} elseif ($this->status != 200) { |
|
||||||
|
|
||||||
switch ($this->status) { |
|
||||||
case 400: |
|
||||||
$this->error = 'HTTP_BAD_REQUEST'; |
|
||||||
break; |
|
||||||
case 401: |
|
||||||
$this->error = 'HTTP_UNAUTHORIZED'; |
|
||||||
break; |
|
||||||
case 403: |
|
||||||
$this->error = 'HTTP_FORBIDDEN'; |
|
||||||
break; |
|
||||||
case 404: |
|
||||||
$this->error = 'HTTP_NOT_FOUND'; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if ($this->error) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
return $this->response['result']; |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,156 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
class KevaCoin { |
||||||
|
|
||||||
|
private $_id = 0; |
||||||
|
|
||||||
|
private $_curl; |
||||||
|
private $_protocol; |
||||||
|
private $_host; |
||||||
|
private $_port; |
||||||
|
|
||||||
|
public function __construct($protocol, $host, $port, $username, $password) { |
||||||
|
|
||||||
|
$this->_protocol = $protocol; |
||||||
|
$this->_host = $host; |
||||||
|
$this->_port = $port; |
||||||
|
|
||||||
|
$this->_curl = curl_init(); |
||||||
|
|
||||||
|
curl_setopt_array($this->_curl, [CURLOPT_RETURNTRANSFER => true, |
||||||
|
CURLOPT_FOLLOWLOCATION => true, |
||||||
|
CURLOPT_FRESH_CONNECT => true, |
||||||
|
CURLOPT_HTTPAUTH => CURLAUTH_BASIC, |
||||||
|
CURLOPT_USERPWD => $username . ':' . $password, |
||||||
|
CURLOPT_RETURNTRANSFER => true, |
||||||
|
CURLOPT_FOLLOWLOCATION => true, |
||||||
|
//CURLOPT_VERBOSE => true, |
||||||
|
CURLOPT_HTTPHEADER => [ |
||||||
|
'Content-Type: application/plain', |
||||||
|
], |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
public function __destruct() { |
||||||
|
curl_close($this->_curl); |
||||||
|
} |
||||||
|
|
||||||
|
protected function prepare($url, $method, array $postfields = []) { |
||||||
|
|
||||||
|
curl_setopt($this->_curl, CURLOPT_URL, $this->_protocol . '://' . $this->_host . ':' . $this->_port . $url); |
||||||
|
curl_setopt($this->_curl, CURLOPT_CUSTOMREQUEST, $method); |
||||||
|
|
||||||
|
if ($method == 'POST' && $postfields) { |
||||||
|
curl_setopt($this->_curl, CURLOPT_POSTFIELDS, json_encode($postfields)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected function execute($json = true) { |
||||||
|
|
||||||
|
$response = curl_exec($this->_curl); |
||||||
|
$errorNumber = curl_errno($this->_curl); |
||||||
|
$errorText = curl_error($this->_curl); |
||||||
|
|
||||||
|
if ($errorNumber > 0) { |
||||||
|
//return false; |
||||||
|
} |
||||||
|
|
||||||
|
if ($response) { |
||||||
|
if ($json) { |
||||||
|
return json_decode($response, true); |
||||||
|
} else { |
||||||
|
return $response; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public function getblockcount() { |
||||||
|
|
||||||
|
$this->_id++; |
||||||
|
|
||||||
|
$this->prepare('', 'POST', [ |
||||||
|
'method' => 'getblockcount', |
||||||
|
'params' => [], |
||||||
|
'id' => $this->_id |
||||||
|
]); |
||||||
|
|
||||||
|
$response = $this->execute(); |
||||||
|
|
||||||
|
if (isset($response['result']) && is_int($response['result'])) { |
||||||
|
|
||||||
|
return $response['result']; |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function getblockhash($block) { |
||||||
|
|
||||||
|
$this->_id++; |
||||||
|
|
||||||
|
$this->prepare('', 'POST', [ |
||||||
|
'method' => 'getblockhash', |
||||||
|
'params' => [$block], |
||||||
|
'id' => $this->_id |
||||||
|
]); |
||||||
|
|
||||||
|
$response = $this->execute(); |
||||||
|
|
||||||
|
if (isset($response['result']) && 64 == strlen($response['result'])) { |
||||||
|
|
||||||
|
return $response['result']; |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function getblock($hash) { |
||||||
|
|
||||||
|
$this->_id++; |
||||||
|
|
||||||
|
$this->prepare('', 'POST', [ |
||||||
|
'method' => 'getblock', |
||||||
|
'params' => [$hash], |
||||||
|
'id' => $this->_id |
||||||
|
]); |
||||||
|
|
||||||
|
$response = $this->execute(); |
||||||
|
|
||||||
|
if (isset($response['result']) && is_array($response['result'])) { |
||||||
|
|
||||||
|
return $response['result']; |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function getrawtransaction($txid, $decode = true) { |
||||||
|
|
||||||
|
$this->_id++; |
||||||
|
|
||||||
|
$this->prepare('', 'POST', [ |
||||||
|
'method' => 'getrawtransaction', |
||||||
|
'params' => [$txid, $decode], |
||||||
|
'id' => $this->_id |
||||||
|
]); |
||||||
|
|
||||||
|
$response = $this->execute(); |
||||||
|
|
||||||
|
if (isset($response['result']) && is_array($response['result'])) { |
||||||
|
|
||||||
|
return $response['result']; |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue