diff --git a/.gitignore b/.gitignore index 703984b..a82918b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ /composer.lock /config.json -/.block \ No newline at end of file +/.state \ No newline at end of file diff --git a/README.md b/README.md index 9e03750..98196e1 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,5 @@ * `cd crawler` * `cp example/config.json config.json` * `crontab -e`:`* * * * * php kvazar/crawler/src/index.php` - * drop index: `php kvazar/crawler/src/index.php drop` \ No newline at end of file + * drop index: `php kvazar/crawler/src/index.php drop` + * optimize index: `php kvazar/crawler/src/index.php optimize` \ No newline at end of file diff --git a/src/index.php b/src/index.php index f3a0f7d..e76c21a 100644 --- a/src/index.php +++ b/src/index.php @@ -2,7 +2,7 @@ // Prevent multi-thread execution $semaphore = sem_get( - crc32(__DIR__), 1 + crc32(__FILE__), 1 ); if (false === sem_acquire($semaphore, true)) @@ -26,21 +26,21 @@ $config = json_decode( ) ); -// Init block -$block = 0; +// Init current block state +$state = 1; -if (file_exists(__DIR__ . '/../.block')) +if (file_exists(__DIR__ . '/../.state')) { - $block = (int) file_get_contents( - __DIR__ . '/../.block' + $state = (int) file_get_contents( + __DIR__ . '/../.state' ); } else { file_put_contents( - __DIR__ . '/../.block', - $block + __DIR__ . '/../.state', + $state ); } @@ -115,11 +115,27 @@ if (isset($argv[1])) true ); + file_put_contents( + __DIR__ . '/../.state', + 1 + ); + exit( _('Index dropped!') ); break; + + // Index optimization request + case 'optimize': + + $index->optimize(); + + exit( + _('Index optimized!') + ); + + break; } } @@ -131,16 +147,262 @@ if (false === $blocks = $kevacoin->getBlockCount()) ); } -for ($i = $block; $i <= $blocks; $i++) +for ($block = $state; $block <= $blocks; $block++) { + // Debug progress echo sprintf( "%d/%d\r", - $i, + $block, $blocks ); + // Get block hash + if (!$hash = $kevacoin->getBlockHash($block)) + { + exit( + sprintf( + _('Could not receive "%d" block hash!'), + $block + ) + ); + } + + // Get block data + if (!$data = $kevacoin->getBlock($hash)) + { + exit( + sprintf( + _('Could not receive "%d" block data by hash "%s"!'), + $block, + $hash + ) + ); + } + + if (!isset($data['tx'])) + { + exit( + sprintf( + _('Could not receive tx data in block "%d"!'), + $transaction, + $block + ) + ); + } + + // Process each transaction in block + foreach ((array) $data['tx'] as $transaction) + { + // Validate each transaction + if (!$raw = $kevacoin->getRawTransaction($transaction)) + { + exit( + sprintf( + _('Could not receive raw transaction "%s" in block "%d"!'), + $transaction, + $block + ) + ); + } + + if (empty($raw['txid'])) + { + exit( + sprintf( + _('Could not receive txid of transaction "%s" in block "%d"!'), + $transaction, + $block + ) + ); + } + + if (!isset($raw['vout'])) + { + exit( + sprintf( + _('Could not receive vout of transaction "%s" in block "%d"!'), + $transaction, + $block + ) + ); + } + + if (empty($raw['time'])) + { + exit( + sprintf( + _('Could not receive time of transaction "%s" in block "%d"!'), + $transaction, + $block + ) + ); + } + + if (empty($raw['size'])) + { + exit( + sprintf( + _('Could not receive size of transaction "%s" in block "%d"!'), + $transaction, + $block + ) + ); + } + + // Parse transaction data + foreach((array) $raw['vout'] as $vout) { + + if (!$vout || empty($vout['scriptPubKey']) || empty($vout['scriptPubKey']['asm'])) + { + exit( + sprintf( + _('Invalid vout transaction "%s" in block "%d"!'), + $transaction, + $block + ) + ); + } + + // Parse space-separated fragments to array + $asm = explode( + ' ', + $vout['scriptPubKey']['asm'] + ); + + // Get operation ID required to continue + if (empty($asm[0])) + { + exit( + sprintf( + _('Undefined operation of transaction "%s" in block "%d"!'), + $transaction, + $block + ) + ); + } + + // Detect key / value + switch ($asm[0]) { + + case 'OP_KEVA_PUT': + + if (empty($asm[1]) || empty($asm[2]) || empty($asm[3])) + { + exit( + sprintf( + _('Undefined namespace or key or value of transaction "%s" in block "%d"!'), + $transaction, + $block + ) + ); + } + + $namespace = \Kvazar\Crypto\Base58::encode( + $asm[1], false, 0, false + ); + + $key = \Kvazar\Crypto\Kevacoin::decode( + $asm[2] + ); + + $value = \Kvazar\Crypto\Kevacoin::decode( + $asm[3] + ); + + break; + + case 'OP_KEVA_DELETE': + + if (empty($asm[1]) || empty($asm[2])) + { + exit( + sprintf( + _('Undefined namespace or key of transaction "%s" in block "%d"!'), + $transaction, + $block + ) + ); + } + + $namespace = \Kvazar\Crypto\Base58::encode( + $asm[1], false, 0, false + ); + + $key = \Kvazar\Crypto\Kevacoin::decode( + $asm[2] + ); + + $value = ''; + + break; + + case 'OP_KEVA_NAMESPACE': + + if (empty($asm[1]) || empty($asm[2])) + { + exit( + sprintf( + _('Undefined namespace or value of transaction "%s" in block "%d"!'), + $transaction, + $block + ) + ); + } + + $namespace = \Kvazar\Crypto\Base58::encode( + $asm[1], false, 0, false + ); + + $key = '_KEVA_NS_'; + + $value = \Kvazar\Crypto\Kevacoin::decode( + $asm[2] + ); + + break; + + case 'OP_HASH160': // @TODO not in use at this moment + + /* + [0] => OP_HASH160 + [1] => d03b0c06f8db322a2365e41385f2c8f6f89eebe3 + [2] => OP_EQUAL + */ + + continue 2; + + break; + + case 'OP_RETURN': // @TODO not in use at this moment + + /* + [0] => OP_RETURN + [1] => aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf9 + */ + + continue 2; + + break; + + default: + + exit( + sprintf( + _('Undefined operation "%s" of transaction "%s" in block "%d"!'), + $asm[0], + $transaction, + $block + ) + ); + } + + // Add index record @TODO + } + } + + // Update current block indexed file_put_contents( - __DIR__ . '/../.block', - $i + __DIR__ . '/../.state', + $block ); } \ No newline at end of file