diff --git a/app/Console/Commands/AuxCommand.php b/app/Console/Commands/AuxCommand.php deleted file mode 100644 index 725de99..0000000 --- a/app/Console/Commands/AuxCommand.php +++ /dev/null @@ -1,258 +0,0 @@ -argument('function'); - if($function){ - $this->$function(); - }else{ - $this->warn('No arguments specified'); - } - } - - /** - * @throws \Throwable - */ - public function verifytags(): void{ - self::lock('auxverify'); - - $conn = DB::connection(); - - $requests = TagAddressRequest::query()->where('IsVerified','<>',1)->get(); - foreach ($requests AS $req) { - echo "Verifying tag for $req->Address, amount: $req->VerificationAmount... "; - - $req_date = $req->Created; - $src_addr = $req->Address; - $dst_addr = self::tagrcptaddress; - - // find a transaction with the corresponding inputs created on or after the date - // look for the address ids - $address = Address::query()->select(['Id'])->where('Address',$src_addr)->first(); - $veri_address = Address::query()->select(['Id'])->where('Address',$dst_addr)->first(); // TODO: Redis cache? - if (!$address || !$veri_address) { - echo "could not find source nor verification addresses. Skipping.\n"; - continue; - } - - $src_addr_id = $address->Id; - $dst_addr_id = $veri_address->Id; - - // find the inputs for the source address that were created after $req->Created - 1 hour - $req_date->sub(new DateInterval('PT1H')); - $stmt = $conn->getPdo()->query('SELECT DISTINCT I.TransactionId FROM Inputs I ' . - 'RIGHT JOIN (SELECT IIA.InputId FROM InputsAddresses IIA WHERE IIA.AddressId = ?) IA ON IA.InputId = I.Id ' . - 'JOIN Transactions T ON T.Id = I.TransactionId ' . - 'LEFT JOIN Blocks B ON B.Hash = T.BlockHash ' . - 'WHERE B.Confirmations > 0 AND DATE(I.Created) >= ?', [$src_addr_id, $req_date->format('Y-m-d')]); - $tx_inputs = $stmt->fetchAll(PDO::FETCH_OBJ); - - - $param_values = [$dst_addr_id]; - $params = []; - foreach ($tx_inputs as $in) { - $params[] = '?'; - $param_values[] = $in->TransactionId; - } - - $num_inputs = count($tx_inputs); - echo "***found $num_inputs inputs from address $src_addr.\n"; - - if ($num_inputs == 0) { - continue; - } - - try { - // check the outputs with the dst address - $total_amount = 0; - $stmt = $conn->getPdo()->query(sprintf('SELECT O.Value FROM Outputs O ' . - 'RIGHT JOIN (SELECT IOA.OutputId, IOA.AddressId FROM OutputsAddresses IOA WHERE IOA.AddressId = ?) OA ON OA.OutputId = O.Id ' . - 'WHERE O.TransactionId IN (%s)', implode(', ', $params)), $param_values); - $tx_outputs = $stmt->fetchAll(PDO::FETCH_OBJ); - foreach ($tx_outputs as $out) { - echo "***found output to verification address with value " . $out->Value . "\n"; - $total_amount = bcadd($total_amount, $out->Value, 8); - } - - if ($total_amount >= $req->VerificationAmount) { - $conn->beginTransaction(); - echo "***$total_amount is gte verification amount: $req->VerificationAmount.\n"; - - // Update the tag in the DB - $conn->statement('UPDATE Addresses SET Tag = ?, TagUrl = ? WHERE Address = ?', [$req->Tag, $req->TagUrl, $src_addr]); - - // Set the request as verified - $conn->statement('UPDATE TagAddressRequests SET IsVerified = 1 WHERE Id = ?', [$req->Id]); - - $conn->commit(); - echo "Data committed.\n"; - } else { - echo "***$total_amount is NOT up to verification amount: $req->VerificationAmount.\n"; - } - } catch (Exception $e) { - print_r($e); - echo "Rolling back.\n"; - $conn->rollback(); - } - } - - self::unlock('auxverify'); - } - - public function pricehistory(): void{ - self::lock('pricehistory'); - - $conn = DB::connection('localdb'); - $redis = Redis::connection(); - - try { - - // Only allow 5-minute update intervals - $stmt = $conn->getPdo()->query('SELECT MAX(Created) AS LastUpdate FROM PriceHistory'); - $res = $stmt->fetch(PDO::FETCH_OBJ); - - $now = new DateTime('now', new DateTimeZone('UTC')); - if ($res && strlen(trim($res->LastUpdate)) > 0) { - $dt = new DateTime($res->LastUpdate, new DateTimeZone('UTC')); - $diff = $now->diff($dt); - $diffMinutes = $diff->i; - if ($diffMinutes < 5) { - echo "Last update is less than 5 minutes ago. Quitting.\n"; - self::unlock('pricehistory'); - return; - } - } - - $coingeckoJSON = Cache::remember('coingecko',60,static function(){ - return json_decode(self::curl_get(self::coingeckoURL)); - }); - $blckjson = json_decode(self::curl_get(self::blockchainticker)); - - if ($coingeckoJSON) { - $btc = $coingeckoJSON->{'lbry-credits'}->btc; - $usd = 0; - if (isset($blckjson->USD)) { - $usd = $btc * $blckjson->USD->buy; - $priceInfo = [ - 'price' => number_format($usd, 3, '.', ''), - 'time' => $now->format('c'), - ]; - if ($redis) { - $redis->client()->set(self::lbcpricekey, json_encode($priceInfo)); - } - - // save the price history if both prices are > 0 - if ($usd > 0 && $btc > 0) { - $conn->statement('INSERT INTO PriceHistory (USD, BTC, Created) VALUES (?, ?, UTC_TIMESTAMP())', [$usd, $btc]); - echo "Inserted price history item. USD: $usd, BTC: $btc.\n"; - } else { - echo "Could not insert price history item. USD: $usd, BTC: $btc.\n"; - } - } - } else { - echo "CoinGecko requrest returned an invalid result.\n"; - } - } catch (Exception $e) { - print_r($e); - } - - self::unlock('pricehistory'); - } - - /** - * Lock - * @param $process_name - * @return void - */ - public static function lock($process_name): void{ - $lock = Cache::lock($process_name); - if(!$lock->get()){ - echo $process_name." is already running.\n"; - exit(0); - } - } - - /** - * Unlock - * @param $process_name - * @return bool - */ - public static function unlock($process_name): bool{ - return Cache::lock($process_name)->release(); - } - - /** - * @param $url - * @return string - * @throws Exception - */ - public static function curl_get($url): string{ - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - $response = curl_exec($ch); - if ($response === false) { - $error = curl_error($ch); - $errno = curl_errno($ch); - curl_close($ch); - - throw new Exception(sprintf('The request failed: %s', $error), $errno); - } else { - curl_close($ch); - } - - return $response; - } - -} diff --git a/app/Console/Commands/BlockCommand.php b/app/Console/Commands/BlockCommand.php deleted file mode 100644 index ff34288..0000000 --- a/app/Console/Commands/BlockCommand.php +++ /dev/null @@ -1,2249 +0,0 @@ -client(); - self::$rpcurl = config('lbry.rpc_url'); - - $function = $this->argument('function'); - if($function){ - $this->$function(); - }else{ - $this->warn('No arguments specified'); - } - } - - /** - * @return void - * @throws Throwable - */ - public function fixscripthashtx(): void{ - $conn = DB::connection(); - $otxs = Output::query()->select(['TransactionId'])->distinct(['TransactionId'])->where('Type','scripthash')->get(); - foreach ($otxs as $otx) { - $txid = $otx->TransactionId; - $tx = Transaction::query()->select(['Hash'])->where('Id',$txid)->first(); - $req = ['method' => 'getrawtransaction', 'params' => [$tx->Hash],'id'=>rand()]; - $response = self::curl_json_post(self::$rpcurl, json_encode($req)); - $json = json_decode($response); - $tx_result = $json->result; - $raw_tx = $tx_result; - $tx_data = self::decode_tx($raw_tx); - $all_tx_data = $this->txdb_data_from_decoded($tx_data); - - foreach ($all_tx_data['outputs'] as $out) { - if ($out['Type'] != 'scripthash') { - continue; - } - - // get the old address - $old_output = Output::query()->select(['Id', 'Addresses'])->where([ - ['TransactionId',$txid], - ['Vout',$out['Vout']], - ])->first(); - if ($old_output) { - $old_addresses = json_decode($old_output->Addresses); - $old_address = $old_addresses[0]; - $new_addresses = json_decode($out['Addresses']); - $new_address = $new_addresses[0]; - - // update the output with new addresses array - $conn->beginTransaction(); - $conn->statement('UPDATE Outputs SET Addresses = ? WHERE Id = ?', [$out['Addresses'], $old_output->Id]); - - // update the old address with the new one - $conn->statement('UPDATE Addresses SET Address = ? WHERE Address = ?', [$new_address, $old_address]); - $conn->commit(); - - echo "$old_address => $new_address\n"; - } - } - } - } - - public static function hex2str(string $hex): string{ - $string = ''; - for ($i = 0; $i < strlen($hex)-1; $i+=2){ - $string .= chr(hexdec($hex[$i].$hex[$i+1])); - } - return $string; - } - - public function updateclaimfees(): void{ - self::lock('claimfees'); - - $conn = DB::connection(); - try { - $stmt = $conn->getPdo()->query('SELECT CS.Id, CS.Stream FROM ClaimStreams CS JOIN Claims C ON C.Id = CS.Id WHERE C.Fee = 0 AND C.Id <= 11462 ORDER BY Id ASC'); - $claims = $stmt->fetchAll(PDO::FETCH_OBJ); - foreach ($claims as $claim) { - $stream = json_decode($claim->Stream); - if (isset($stream->metadata->fee) && $stream->metadata->fee->amount > 0) { - $fee = $stream->metadata->fee->amount; - $currency = $stream->metadata->fee->currency; - - $conn->statement('UPDATE Claims SET Fee = ?, FeeCurrency = ? WHERE Id = ?', [$fee, $currency, $claim->Id]); - echo "Updated fee for claim ID: $claim->Id. Fee: $fee, Currency: $currency\n"; - } - } - } catch (Exception $e) { - print_r($e); - } - - self::unlock('claimfees'); - } - - /** - * @return void - * @throws RedisException - * @throws Throwable - */ - public function buildclaimindex(): void{ - self::lock('buildindex'); - - // start with all txs - $decoder_url = 'http://127.0.0.1:5000'; - $conn = DB::connection(); - $redis_key = 'claim.oid'; - $last_claim_oid = self::$redis->exists($redis_key) ? self::$redis->get($redis_key) : 0; - try { - $stmt = $conn->getPdo()->query('SELECT COUNT(Id) AS RecordCount FROM Outputs WHERE Id > ?', [$last_claim_oid]); - $count = min(500000, $stmt->fetch(PDO::FETCH_OBJ)->RecordCount); - - $idx = 0; - $stmt = $conn->getPdo()->query('SELECT O.Id, O.TransactionId, O.Vout, O.ScriptPubKeyAsm, T.Hash, IFNULL(T.TransactionTime, T.CreatedTime) AS TxTime FROM Outputs O ' . - 'JOIN Transactions T ON T.Id = O.TransactionId WHERE O.Id > ? ORDER BY O.Id ASC LIMIT 500000', - [$last_claim_oid]); - while ($out = $stmt->fetch(PDO::FETCH_OBJ)) { - $idx++; - $idx_str = str_pad($idx, strlen($count), '0', STR_PAD_LEFT); - - $txid = $out->TransactionId; - $vout = $out->Vout; - - if (strpos($out->ScriptPubKeyAsm, 'OP_CLAIM_NAME') !== false) { - // check if the claim already exists in the claims table - $stmt2 = $conn->getPdo()->query('SELECT Id FROM Claims WHERE TransactionHash = ? AND Vout = ?', [$out->Hash, $out->Vout]); - $exist_claim = $stmt2->fetch(PDO::FETCH_OBJ); - if ($exist_claim) { - echo "[$idx_str/$count] claim already exists for [$out->Hash:$vout]. Skipping.\n"; - continue; - } - - $asm_parts = explode(' ', $out->ScriptPubKeyAsm, 4); - $name_hex = $asm_parts[1]; - $claim_name = @pack('H*', $name_hex); - - // decode claim - $url = sprintf("%s/claim_decode/%s", $decoder_url, $claim_name); - $json = null; - try { - $json = self::curl_json_get($url); - } catch (Exception $e) { - echo "[$idx_str/$count] claimdecode failed for [$out->Hash:$vout]. Skipping.\n"; - continue; - } - $claim = json_decode($json); - - if ($claim) { - $req = ['method' => 'getvalueforname', 'params' => [$claim_name],'id'=>rand()]; - $json = null; - try { - $json = json_decode(self::curl_json_post(self::$rpcurl, json_encode($req))); - if (!$json) { - echo "[$idx_str/$count] getvalueforname failed for [$out->Hash:$vout]. Skipping.\n"; - continue; - } - } catch (Exception $e) { - echo "[$idx_str/$count] getvalueforname failed for [$out->Hash:$vout]. Skipping.\n"; - continue; - } - - echo "[$idx_str/$count] claim found for [$out->Hash:$vout]. Processing claim... \n"; - $claim_data = []; - - $claim_id = $json->result->claimId; - $tx_dt = DateTime::createFromFormat('U', $out->TxTime); - - $claim_stream_data = null; - if ($claim->claimType === 'streamType') { - // Build claim object to save - $claim_data = [ - 'ClaimId' => $claim_id, - 'TransactionHash' => $out->Hash, - 'Vout' => $out->Vout, - 'Name' => $claim_name, - 'Version' => $claim->version, - 'ClaimType' => 2, // streamType - 'ContentType' => isset($claim->stream->source->contentType) ? $claim->stream->source->contentType : null, - 'Title' => isset($claim->stream->metadata->title) ? $claim->stream->metadata->title : null, - 'Description' => isset($claim->stream->metadata->description) ? $claim->stream->metadata->description : null, - 'Language' => isset($claim->stream->metadata->language) ? $claim->stream->metadata->language : null, - 'Author' => isset($claim->stream->metadata->author) ? $claim->stream->metadata->author : null, - 'ThumbnailUrl' => isset($claim->stream->metadata->thumbnail) ? $claim->stream->metadata->thumbnail : null, - 'IsNSFW' => isset($claim->stream->metadata->nsfw) ? $claim->stream->metadata->nsfw : 0, - 'Fee' => isset($claim->stream->metadata->fee) ? $claim->stream->metadata->fee->amount : 0, - 'FeeCurrency' => isset($claim->stream->metadata->fee) ? $claim->stream->metadata->fee->currency : 0, - 'Created' => $tx_dt->format('Y-m-d H:i:s'), - 'Modified' => $tx_dt->format('Y-m-d H:i:s') - ]; - - $claim_stream_data = [ - 'Stream' => json_encode($claim->stream) - ]; - - if (isset($claim->publisherSignature)) { - $sig_claim = Claim::query()->select(['Id', 'ClaimId', 'Name'])->where('ClaimId',$claim->publisherSignature->certificateId)->first(); - if ($sig_claim) { - $claim_data['PublisherId'] = $sig_claim->ClaimId; - $claim_data['PublisherName'] = $sig_claim->Name; - } - } - } else { - $claim_data = [ - 'ClaimId' => $claim_id, - 'TransactionHash' => $out->Hash, - 'Vout' => $out->Vout, - 'Name' => $claim_name, - 'Version' => $claim->version, - 'ClaimType' => 1, - 'Certificate' => json_encode($claim->certificate), - 'Created' => $tx_dt->format('Y-m-d H:i:s'), - 'Modified' => $tx_dt->format('Y-m-d H:i:s') - ]; - } - - $conn->beginTransaction(); - $data_error = false; - - $claim_entity = new Claim($claim_data); - $res = $claim_entity->save(); - - if (!$res) { - $data_error = true; - echo "[$idx_str/$count] claim for [$out->Hash:$vout] FAILED to save.\n"; - } - - if (!$data_error) { - if ($claim_stream_data) { - $claim_stream_data['Id'] = $claim_entity->Id; - $claim_stream_entity = new ClaimStream($claim_stream_data); - - $res = $claim_stream_entity->save(); - if (!$res) { - $data_error = true; - } - } - } - - if (!$data_error) { - $conn->commit(); - echo "[$idx_str/$count] claim for [$out->Hash:$vout] indexed.\n"; - } else { - $conn->rollback(); - echo "[$idx_str/$count] claim for [$out->Hash:$vout] NOT indexed. Rolled back.\n"; - } - } else { - echo "[$idx_str/$count] claim for [$out->Hash:$vout] could not be decoded. Skipping.\n"; - } - } else { - echo "[$idx_str/$count] no claim found for [$out->Hash:$vout]. Skipping.\n"; - } - - self::$redis->set($redis_key, $out->Id); - } - } catch (Exception $e) { - // continue - print_r($e); - } - - self::unlock('buildindex'); - } - - // TODO: Refactor for unique claim identification by claim_id instead of using the claim name. - protected function _getclaimfortxout($pubkeyasm, $tx_hash, $vout, $tx_time = null): array{ - $claim_data = null; - $claim_stream_data = null; - - $asm_parts = explode(' ', $pubkeyasm, 4); - $name_hex = $asm_parts[1]; - $claim_name = @pack('H*', $name_hex); - - // decode claim - $decoder_url = 'http://127.0.0.1:5000'; - $url = sprintf("%s/claim_decode/%s", $decoder_url, $claim_name); - $json = null; - try { - $json = self::curl_json_get($url); - } catch (Exception $e) { - echo "***claimdecode failed for [$tx_hash:$vout]. Skipping.\n"; - } - - if ($json) { - $claim = json_decode($json); - if ($claim) { - if (strpos($claim_name, '#') !== false) { - $claim_name = substr($claim_name, 0, strpos($claim_name, '#')); - } - - $req = ['method' => 'getvalueforname', 'params' => [$claim_name],'id'=>rand()]; - $json = null; - try { - $json = json_decode(self::curl_json_post(self::$rpcurl, json_encode($req))); - if ($json) { - $claim_data = []; - $claim_id = $json->result->claimId; - $now = new DateTime('now', new DateTimeZone('UTC')); - $tx_dt = ($tx_time != null) ? $tx_time : $now; - if ($claim->claimType === 'streamType') { - // Build claim object to save - $claim_data = [ - 'ClaimId' => $claim_id, - 'TransactionHash' => $tx_hash, - 'Vout' => $vout, - 'Name' => $claim_name, - 'Version' => $claim->version, - 'ClaimType' => 2, // streamType - 'ContentType' => isset($claim->stream->source->contentType) ? $claim->stream->source->contentType : null, - 'Title' => isset($claim->stream->metadata->title) ? $claim->stream->metadata->title : null, - 'Description' => isset($claim->stream->metadata->description) ? $claim->stream->metadata->description : null, - 'Language' => isset($claim->stream->metadata->language) ? $claim->stream->metadata->language : null, - 'Author' => isset($claim->stream->metadata->author) ? $claim->stream->metadata->author : null, - 'ThumbnailUrl' => isset($claim->stream->metadata->thumbnail) ? $claim->stream->metadata->thumbnail : null, - 'IsNSFW' => isset($claim->stream->metadata->nsfw) ? $claim->stream->metadata->nsfw : 0, - 'Fee' => isset($claim->stream->metadata->fee) ? $claim->stream->metadata->fee->amount : 0, - 'FeeCurrency' => isset($claim->stream->metadata->fee) ? $claim->stream->metadata->fee->currency : 0, - 'Created' => $tx_dt->format('Y-m-d H:i:s'), - 'Modified' => $tx_dt->format('Y-m-d H:i:s') - ]; - - $claim_stream_data = [ - 'Stream' => json_encode($claim->stream) - ]; - - if (isset($claim->publisherSignature)) { - $sig_claim = Claim::query()->select(['Id', 'ClaimId', 'Name'])->where('ClaimId',$claim->publisherSignature->certificateId)->first(); - if ($sig_claim) { - $claim_data['PublisherId'] = $sig_claim->ClaimId; - $claim_data['PublisherName'] = $sig_claim->Name; - } - } - } else { - $claim_data = [ - 'ClaimId' => $claim_id, - 'TransactionHash' => $tx_hash, - 'Vout' => $vout, - 'Name' => $claim_name, - 'Version' => $claim->version, - 'ClaimType' => 1, - 'Certificate' => json_encode($claim->certificate), - 'Created' => $tx_dt->format('Y-m-d H:i:s'), - 'Modified' => $tx_dt->format('Y-m-d H:i:s') - ]; - } - } - } catch (Exception $e) { - echo "***getvalueforname failed for [$out->Hash:$vout]. Skipping.\n"; - } - } - } - - return ['claim_data' => $claim_data, 'claim_stream_data' => $claim_stream_data]; - } - - public function fixzerooutputs(): void{ - self::lock('zerooutputs'); - - $conn = DB::connection(); - - /** 2017-06-12 21:38:07 **/ - //$last_fixed_txid = self::$redis->exists('fix.txid') ? self::$redis->get('fix.txid') : 0; - try { - $stmt = $conn->getPdo()->query('SELECT Id FROM Transactions WHERE Created >= ? AND Created <= ? LIMIT 1000000', ['2017-06-15 20:44:50', '2017-06-16 08:02:09']); - $txids = $stmt->fetchAll(PDO::FETCH_OBJ); - - $count = count($txids); - $idx = 0; - foreach ($txids as $distincttx) { - $idx++; - $idx_str = str_pad($idx, strlen($count), '0', STR_PAD_LEFT); - $txid = $distincttx->Id; - echo "[$idx_str/$count] Processing txid: $txid... "; - $total_diff = 0; - - // findtx - $start_ms = round(microtime(true) * 1000); - $tx = Transaction::query()->select(['Hash'])->where('Id',$txid)->first(); - $diff_ms = (round(microtime(true) * 1000)) - $start_ms; - $total_diff += $diff_ms; - echo "findtx took {$diff_ms}ms. "; - - // Get the inputs and outputs - // Get the raw transaction (Use getrawtx daemon instead (for speed!!!) - - // getraw - $req = ['method' => 'getrawtransaction', 'params' => [$tx->Hash],'id'=>rand()]; - $start_ms = round(microtime(true) * 1000); - $response = self::curl_json_post(self::$rpcurl, json_encode($req)); - $diff_ms = (round(microtime(true) * 1000)) - $start_ms; - $total_diff += $diff_ms; - echo "getrawtx took {$diff_ms}ms. "; - - $start_ms = round(microtime(true) * 1000); - $json = json_decode($response); - $tx_result = $json->result; - $raw_tx = $tx_result; - $tx_data = self::decode_tx($raw_tx); - - $all_tx_data = $this->txdb_data_from_decoded($tx_data); - - $inputs = $all_tx_data['inputs']; - $outputs = $all_tx_data['outputs']; - - $addr_id_map = []; - $addr_id_drcr = []; // debits and credits grouped by address - $total_tx_value = 0; - - $diff_ms = (round(microtime(true) * 1000)) - $start_ms; - $total_diff += $diff_ms; - echo "decodetx took {$diff_ms}ms. "; - - // Create / update addresses - $addr_id_map = []; - $new_addr_map = []; - foreach($all_tx_data['addresses'] as $address => $addro) { - $prev_addr = Address::query()->select(['Id'])->where('Address',$address)->first(); - if (!$prev_addr) { - $new_addr = [ - 'Address' => $address, - 'FirstSeen' => $block_ts->format('Y-m-d H:i:s') - ]; - $entity = new Address($new_addr); - $res = $entity->save(); - if (!$res) { - $data_error = true; - } else { - $addr_id_map[$address] = $entity->Id; - } - $new_addr_map[$address] = 1; - } else { - $addr_id_map[$address] = $prev_addr->Id; - } - } - - $start_ms = round(microtime(true) * 1000); - $num_outputs = count($outputs); - foreach ($outputs as $out) { - $vout = $out['Vout']; - $total_tx_value = bcadd($total_tx_value, $out['Value'], 8); - - // check if the output exists - $stmt = $conn->getPdo()->query('SELECT Id FROM Outputs WHERE TransactionId = ? AND Vout = ?', [$txid, $vout]); - $exist_output = $stmt->fetch(PDO::FETCH_OBJ); - - if (!$exist_output) { - $out['TransactionId'] = $txid; - $out_entity = new Output($out); - - //$stmt->execute('INSERT INTO Outputs') - $conn->statement('INSERT INTO Outputs (TransactionId, Vout, Value, Type, ScriptPubKeyAsm, ScriptPubKeyHex, RequiredSignatures, Hash160, Addresses, Created, Modified) '. - 'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, UTC_TIMESTAMP(), UTC_TIMESTAMP())', - [$out['TransactionId'], - $out['Vout'], - $out['Value'], - $out['Type'], - $out['ScriptPubKeyAsm'], - $out['ScriptPubKeyHex'], - $out['RequiredSignatures'], - $out['Hash160'], - $out['Addresses'] - ]); - - // get the last insert id - $stmt = $conn->getPdo()->query('SELECT LAST_INSERT_ID() AS outputId'); - $linsert = $stmt->fetch(PDO::FETCH_OBJ); - $out_entity->Id = $linsert->outputId; - - if ($out_entity->Id === 0) { - $data_error = true; - break; - } - - $json_addr = json_decode($out['Addresses']); - $address = $json_addr[0]; - - // Get the address ID - $addr_id = -1; - if (isset($addr_id_map[$address])) { - $addr_id = $addr_id_map[$address]; - } else { - $src_addr = Address::query()->select(['Id'])->where('Address',$address)->first(); - if ($src_addr) { - $addr_id = $src_addr->Id; - $addr_id_map[$address] = $addr_id; - } - } - - if ($addr_id > -1) { - $conn->statement('INSERT INTO OutputsAddresses (OutputId, AddressId) VALUES (?, ?) ON DUPLICATE KEY UPDATE OutputId = OutputId', [$out_entity->Id, $addr_id]); - $conn->statement('INSERT INTO TransactionsAddresses (TransactionId, AddressId) VALUES (?, ?) ON DUPLICATE KEY UPDATE TransactionId = TransactionId', [$txid, $addr_id]); - } - - if ($addr_id > -1 && isset($new_addr_map[$address])) { - if (!isset($addr_id_drcr[$addr_id])) { - $addr_id_drcr[$addr_id] = ['debit' => 0, 'credit' => 0]; - } - $addr_id_drcr[$addr_id]['credit'] = bcadd($addr_id_drcr[$addr_id]['credit'], $out['Value'], 8); - - // Update the Received amount for the address based on the output - $conn->statement('UPDATE Addresses SET TotalReceived = TotalReceived + ? WHERE Id = ?', [$out['Value'], $addr_id]); - } - } - } - $diff_ms = (round(microtime(true) * 1000)) - $start_ms; - $total_diff += $diff_ms; - echo "$num_outputs output(s) took {$diff_ms}ms. "; - - // Fix the input values - $start_ms = round(microtime(true) * 1000); - $num_inputs = count($inputs); - foreach ($inputs as $in) { - if (isset($in['PrevoutHash'])) { - $prevout_hash = $in['PrevoutHash']; - $in_prevout = $in['PrevoutN']; - $prevout_tx_id = -1; - $prevout_tx = Transaction::query()->select(['Id'])->where('Hash',$prevout_hash)->first(); - if (!$prevout_tx) { - continue; - } - - $prevout_tx_id = $prevout_tx->Id; - $stmt = $conn->getPdo()->query('SELECT Value, Addresses FROM Outputs WHERE TransactionId = ? AND Vout = ?', [$prevout_tx_id, $in_prevout]); - $src_output = $stmt->fetch(PDO::FETCH_OBJ); - if ($src_output) { - $in['Value'] = $src_output->Value; - //$conn->execute('UPDATE Inputs SET Value = ? WHERE TransactionId = ? AND PrevoutHash = ? AND PrevoutN = ?', [$in['Value'], $txid, $prevout_hash, $in_prevout]); - - // Check if the input exists - $stmt = $conn->getPdo()->query('SELECT Id FROM Inputs WHERE TransactionId = ? AND PrevoutHash = ? AND PrevoutN = ?', [$txid, $prevout_hash, $in_prevout]); - $exist_input = $stmt->fetch(PDO::FETCH_OBJ); - - if (!$exist_input) { - $json_addr = json_decode($src_output->Addresses); - $address = $json_addr[0]; - - // Get the address ID - $addr_id = -1; - if (isset($addr_id_map[$address])) { - $addr_id = $addr_id_map[$address]; - } else { - $src_addr = Address::query()->select(['Id'])->where('Address',$address)->first(); - if ($src_addr) { - $addr_id = $src_addr->Id; - $addr_id_map[$address] = $addr_id; - } - } - - $in_entity = new Input($in); - $in['TransactionId'] = $txid; - if ($addr_id > -1) { - $in['AddressId'] = $addr_id; - } - $conn->statement('INSERT INTO Inputs (TransactionId, TransactionHash, AddressId, PrevoutHash, PrevoutN, Sequence, Value, ScriptSigAsm, ScriptSigHex, Created, Modified) ' . - 'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, UTC_TIMESTAMP(), UTC_TIMESTAMP())', - [$in['TransactionId'], - $in['TransactionHash'], - isset($in['AddressId']) ? $in['AddressId'] : null, - $in['PrevoutHash'], - $in['PrevoutN'], - $in['Sequence'], - isset($in['Value']) ? $in['Value'] : 0, - $in['ScriptSigAsm'], - $in['ScriptSigHex'] - ]); - - // get last insert id - $stmt = $conn->getPdo()->query('SELECT LAST_INSERT_ID() AS inputId'); - $linsert = $stmt->fetch(PDO::FETCH_OBJ); - $in_entity->Id = $linsert->inputId; - - if ($in_entity->Id === 0) { - $data_error = true; - break; - } - - if ($addr_id > -1) { - $conn->statement('INSERT INTO InputsAddresses (InputId, AddressId) VALUES (?, ?) ON DUPLICATE KEY UPDATE InputId = InputId', [$in_entity->Id, $addr_id]); - $conn->statement('INSERT INTO TransactionsAddresses (TransactionId, AddressId) VALUES (?, ?) ON DUPLICATE KEY UPDATE TransactionId = TransactionId', [$txid, $addr_id]); - } - - if ($addr_id > -1 && isset($new_addr_map[$address])) { - if (!isset($addr_id_drcr[$addr_id])) { - $addr_id_drcr[$addr_id] = ['debit' => 0, 'credit' => 0]; - } - $addr_id_drcr[$addr_id]['debit'] = bcadd($addr_id_drcr[$addr_id]['debit'], $in['Value'], 8); - - // Update total sent - $conn->statement('UPDATE Addresses SET TotalSent = TotalSent + ? WHERE Id = ?', [$in['Value'], $addr_id]); - } - } - } - } - } - $diff_ms = (round(microtime(true) * 1000)) - $start_ms; - $total_diff += $diff_ms; - echo "$num_inputs input(s) took {$diff_ms}ms. "; - - // update tx time - $start_ms = round(microtime(true) * 1000); - $upd_addr_ids = []; - //$conn->execute('UPDATE Transactions SET Value = ? WHERE Id = ?', [$total_tx_value, $txid]); - foreach ($addr_id_drcr as $addr_id => $drcr) { - try { - $conn->statement('UPDATE TransactionsAddresses SET DebitAmount = ?, CreditAmount = ? WHERE TransactionId = ? AND AddressId = ?', - [$drcr['debit'], $drcr['credit'], $txid, $addr_id]); - } catch (Exception $e) { - print_r($e); - $data_error = true; - break; - } - } - - //self::$redis->set('fix.txid', $txid); - $diff_ms = (round(microtime(true) * 1000)) - $start_ms; - $total_diff += $diff_ms; - echo "update took {$diff_ms}ms. Total {$total_diff} ms.\n"; - } - } catch (Exception $e) { - print_r($e); - } - - self::unlock('zerooutputs'); - } - - public function addrtxamounts(): void{ - set_time_limit(0); - - self::lock('addrtxamounts'); - - try { - $conn = DB::connection(); - $stmt = $conn->getPdo()->query('SELECT TransactionId, AddressId FROM TransactionsAddresses WHERE DebitAmount = 0 AND CreditAmount = 0 LIMIT 1000000'); - $txaddresses = $stmt->fetchAll(PDO::FETCH_OBJ); - - $count = count($txaddresses); - $idx = 0; - - echo "Processing $count tx address combos...\n"; - foreach ($txaddresses as $txaddr) { - $idx++; - $idx_str = str_pad($idx, strlen($count), '0', STR_PAD_LEFT); - - // Check the inputs - $stmt = $conn->getPdo()->query('SELECT SUM(I.Value) AS DebitAmount FROM Inputs I JOIN InputsAddresses IA ON IA.InputId = I.Id WHERE I.TransactionId = ? AND IA.AddressId = ?', - [$txaddr->TransactionId, $txaddr->AddressId]); - $res = $stmt->fetch(PDO::FETCH_OBJ); - $debitamount = $res->DebitAmount ? $res->DebitAmount : 0; - - $stmt = $conn->getPdo()->query('SELECT SUM(O.Value) AS CreditAmount FROM Outputs O JOIN OutputsAddresses OA ON OA.OutputId = O.Id WHERE O.TransactionId = ? AND OA.AddressId = ?', - [$txaddr->TransactionId, $txaddr->AddressId]); - $res = $stmt->fetch(PDO::FETCH_OBJ); - $creditamount = $res->CreditAmount ? $res->CreditAmount : 0; - - echo "[$idx_str/$count] Updating tx $txaddr->TransactionId, address id $txaddr->AddressId with debit amount: $debitamount, credit amount: $creditamount... "; - $conn->statement('UPDATE TransactionsAddresses SET DebitAmount = ?, CreditAmount = ? WHERE TransactionId = ? AND AddressId = ?', - [$debitamount, $creditamount, $txaddr->TransactionId, $txaddr->AddressId]); - echo "Done.\n"; - } - } catch (Exception $e) { - // failed - print_r($e); - } - - self::unlock('addrtxamounts'); - } - - /** - * @param $tx_hash - * @param $block_ts - * @param $block_data - * @param $data_error - * @return void - * @throws Exception - */ - private function processtx($tx_hash, $block_ts, $block_data, &$data_error): void{ - // Get the raw transaction (Use getrawtx daemon instead (for speed!!!) - $req = ['method' => 'getrawtransaction', 'params' => [$tx_hash],'id'=>rand()]; - $response = self::curl_json_post(self::$rpcurl, json_encode($req)); - $json = json_decode($response); - $tx_result = $json->result; - $raw_tx = $tx_result; - $tx_data = self::decode_tx($raw_tx); - - $all_tx_data = $this->txdb_data_from_decoded($tx_data); - $conn = DB::connection(); - - // Create / update addresses - $addr_id_map = []; - foreach($all_tx_data['addresses'] as $address => $addrss) { - $prev_addr = Address::query()->select(['Id'])->where('Address',$address)->first(); - if (!$prev_addr) { - $new_addr = [ - 'Address' => $address, - 'FirstSeen' => $block_ts->format('Y-m-d H:i:s'), - ]; - $entity = new Address($new_addr); - $res = $entity->save(); - if (!$res) { - $data_error = true; - } else { - $addr_id_map[$address] = $entity->Id; - } - } else { - $addr_id_map[$address] = $prev_addr->Id; - } - } - - $addr_id_drcr = []; // debits and credits grouped by address - $numeric_tx_id = -1; - if (!$data_error) { - // Create transaction - $new_tx = $all_tx_data['tx']; - - $total_tx_value = 0; - foreach ($all_tx_data['outputs'] as $out) { - $total_tx_value = bcadd($total_tx_value, $out['Value'], 8); - } - - if ($block_data) { - $new_tx['BlockHash'] = $block_data['hash']; - $new_tx['TransactionTime'] = $block_data['time']; - } - $new_tx['TransactionSize'] = ((strlen($raw_tx)) / 2); - $new_tx['InputCount'] = count($all_tx_data['inputs']); - $new_tx['OutputCount'] = count($all_tx_data['outputs']); - $new_tx['Hash'] = $tx_hash; - $new_tx['Value'] = $total_tx_value; - $new_tx['Raw'] = $raw_tx; - - - $tx_entity = new Transaction($new_tx); - $conn->statement('INSERT INTO Transactions (Version, LockTime, BlockHash, TransactionTime, InputCount, OutputCount, TransactionSize, Hash, Value, Raw, Created, Modified) VALUES ' . - '(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, UTC_TIMESTAMP(), UTC_TIMESTAMP())', - [ - $new_tx['Version'], - $new_tx['LockTime'], - isset($new_tx['BlockHash']) ? $new_tx['BlockHash'] : null, - isset($new_tx['TransactionTime']) ? $new_tx['TransactionTime'] : null, - $new_tx['InputCount'], - $new_tx['OutputCount'], - $new_tx['TransactionSize'], - $new_tx['Hash'], - $new_tx['Value'], - $new_tx['Raw'] - ]); - $stmt = $conn->getPdo()->query('SELECT LAST_INSERT_ID() AS txnId'); - $linsert = $stmt->fetch(PDO::FETCH_OBJ); - $tx_entity->Id = $linsert->txnId; - if ($tx_entity->Id === 0) { - 3; - } else { - $numeric_tx_id = $tx_entity->Id; - } - } - - if (!$data_error && $numeric_tx_id > 0) { - // Create the inputs - $inputs = $all_tx_data['inputs']; - $outputs = $all_tx_data['outputs']; - - foreach ($inputs as $in) { - $in['TransactionId'] = $numeric_tx_id; - $in['TransactionHash'] = $tx_hash; - - if (isset($in['IsCoinbase']) && $in['IsCoinbase'] === 1) { - $in_entity = new Input($in); - $res = $in_entity->save(); - if (!$res) { - $data_error = true; - break; - } - } else { - $in_tx_hash = $in['PrevoutHash']; - $in_prevout = $in['PrevoutN']; - - // Find the corresponding previous output - $in_tx = Transaction::query()->select(['Id'])->where('Hash',$in_tx_hash)->first(); - $src_output = null; - if ($in_tx) { - $stmt = $conn->getPdo()->query('SELECT Id, Value, Addresses FROM Outputs WHERE TransactionId = ? AND Vout = ?', [$in_tx->Id, $in_prevout]); - $src_output = $stmt->fetch(PDO::FETCH_OBJ); - if ($src_output) { - $in['Value'] = $src_output->Value; - $json_addr = json_decode($src_output->Addresses); - $in_addr_id = 0; - if (isset($addr_id_map[$json_addr[0]])) { - $in['AddressId'] = $addr_id_map[$json_addr[0]]; - } else { - $in_addr = Address::query()->select(['Id'])->where('Address',$json_addr[0])->first(); - if ($in_addr) { - $addr_id_map[$json_addr[0]] = $in_addr->Id; - $in['AddressId'] = $in_addr->Id; - } - } - } - } - - $in_entity = new Input($in); - $conn->statement('INSERT INTO Inputs (TransactionId, TransactionHash, AddressId, PrevoutHash, PrevoutN, Sequence, Value, ScriptSigAsm, ScriptSigHex, Created, Modified) ' . - 'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, UTC_TIMESTAMP(), UTC_TIMESTAMP())', - [$in['TransactionId'], - $in['TransactionHash'], - isset($in['AddressId']) ? $in['AddressId'] : null, - $in['PrevoutHash'], - $in['PrevoutN'], - $in['Sequence'], - isset($in['Value']) ? $in['Value'] : 0, - $in['ScriptSigAsm'], - $in['ScriptSigHex'] - ]); - - // get last insert id - $stmt = $conn->getPdo()->query('SELECT LAST_INSERT_ID() AS inputId'); - $linsert = $stmt->fetch(PDO::FETCH_OBJ); - $in_entity->Id = $linsert->inputId; - - if ($in_entity->Id === 0) { - $data_error = true; - break; - } - - // Update the src_output spent if successful - if ($src_output) { - try { - $conn->statement('UPDATE Outputs SET IsSpent = 1, SpentByInputId = ? WHERE Id = ?', [$in_entity->Id, $src_output->Id]); - $conn->statement('UPDATE Inputs SET PrevoutSpendUpdated = 1 WHERE Id = ?', [$in_entity->Id]); - } catch (Exception $e) { - $data_error = true; - break; - } - } - - if (isset($in['AddressId']) && $in['AddressId'] > 0) { - $addr_id = $in['AddressId']; - if (!isset($addr_id_drcr[$addr_id])) { - $addr_id_drcr[$addr_id] = ['debit' => 0, 'credit' => 0]; - } - $addr_id_drcr[$addr_id]['debit'] = bcadd($addr_id_drcr[$addr_id]['debit'], $in['Value'], 8); - - try { - $conn->statement('INSERT INTO InputsAddresses (InputId, AddressId) VALUES (?, ?) ON DUPLICATE KEY UPDATE InputId = InputId', [$in_entity->Id, $in['AddressId']]); - $conn->statement('UPDATE Addresses SET TotalSent = TotalSent + ? WHERE Id = ?', [$in['Value'], $in['AddressId']]); - $conn->statement('INSERT INTO TransactionsAddresses (TransactionId, AddressId) VALUES (?, ?) ON DUPLICATE KEY UPDATE TransactionId = TransactionId', [$numeric_tx_id, $in['AddressId']]); - } catch (Exception $e) { - $data_error = true; - break; - } - } - } - } - - foreach ($outputs as $out) { - $out['TransactionId'] = $numeric_tx_id; - $out_entity = new Output($out); - - //$stmt->execute('INSERT INTO Outputs') - $conn->statement('INSERT INTO Outputs (TransactionId, Vout, Value, Type, ScriptPubKeyAsm, ScriptPubKeyHex, RequiredSignatures, Hash160, Addresses, Created, Modified) '. - 'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, UTC_TIMESTAMP(), UTC_TIMESTAMP())', - [$out['TransactionId'], - $out['Vout'], - $out['Value'], - $out['Type'], - $out['ScriptPubKeyAsm'], - $out['ScriptPubKeyHex'], - $out['RequiredSignatures'], - $out['Hash160'], - $out['Addresses'] - ]); - - // get the last insert id - $stmt = $conn->getPdo()->query('SELECT LAST_INSERT_ID() AS outputId'); - $linsert = $stmt->fetch(PDO::FETCH_OBJ); - $out_entity->Id = $linsert->outputId; - - if ($out_entity->Id === 0) { - $data_error = true; - break; - } - - $json_addr = json_decode($out['Addresses']); - $out_addr_id = 0; - if (isset($addr_id_map[$json_addr[0]])) { - $out_addr_id = $addr_id_map[$json_addr[0]]; - } else { - $out_addr = Address::query()->select(['Id'])->where('Address',$json_addr[0])->first(); - if ($out_addr) { - $addr_id_map[$json_addr[0]] = $out_addr->Id; - $out_addr_id = $out_addr->Id; - } - } - - if ($out_addr_id > 0) { - $addr_id = $out_addr_id; - if (!isset($addr_id_drcr[$addr_id])) { - $addr_id_drcr[$addr_id] = ['debit' => 0, 'credit' => 0]; - } - $addr_id_drcr[$addr_id]['credit'] = bcadd($addr_id_drcr[$addr_id]['credit'], $out['Value'], 8); - - try { - $conn->statement('INSERT INTO OutputsAddresses (OutputId, AddressId) VALUES (?, ?) ON DUPLICATE KEY UPDATE OutputId = OutputId', [$out_entity->Id, $out_addr_id]); - $conn->statement('UPDATE Addresses SET TotalReceived = TotalReceived + ? WHERE Id = ?', [$out['Value'], $out_addr_id]); - $conn->statement('INSERT INTO TransactionsAddresses (TransactionId, AddressId) VALUES (?, ?) ON DUPLICATE KEY UPDATE TransactionId = TransactionId', [$numeric_tx_id, $out_addr_id]); - } catch (Exception $e) { - print_r($e); - $data_error = true; - break; - } - } - - // create the claim if the asm pub key starts with OP_CLAIM_NAME - if (strpos($out['ScriptPubKeyAsm'], 'OP_CLAIM_NAME') !== false) { - $all_claim_data = $this->_getclaimfortxout($out['ScriptPubKeyAsm'], $tx_hash, $out['Vout'], $block_ts); - $claim = $all_claim_data['claim_data']; - $claim_stream_data = $all_claim_data['claim_stream_data']; - - if (!$claim) { - continue; - } - - if ($claim['ClaimType'] == 2 && !$claim_stream_data) { - echo "***claim stream data missing for streamType claim\n"; - continue; - } - - $claim_entity = new Claim($claim); - $res = $claim_entity->save(); - - if (!$res) { - echo "***claim could not be saved.\n"; - continue; - } - - if (!$data_error && $claim_stream_data) { - $claim_stream_data['Id'] = $claim_entity->Id; - $claim_stream_entity = new ClaimStream($claim_stream_data); - $res = $claim_stream_entity->save(); - if (!$res) { - echo "***claim stream could not be saved.\n"; - } - } - } - } - } - - // update tx amounts - if (!$data_error) { - foreach ($addr_id_drcr as $addr_id => $drcr) { - try { - $conn->statement('UPDATE TransactionsAddresses SET DebitAmount = ?, CreditAmount = ?, TransactionTime = UTC_TIMESTAMP() WHERE TransactionId = ? AND AddressId = ?', - [$drcr['debit'], $drcr['credit'], $numeric_tx_id, $addr_id]); - } catch (Exception $e) { - print_r($e); - $data_error = true; - break; - } - } - } - } - - /** - * @return void - * @throws Throwable - */ - public function parsetxs(): void{ - set_time_limit(0); - - self::lock('parsetxs'); - - // Get the minimum block with no processed transactions - echo "Parsing transactions...\n"; - - $conn = DB::connection(); - //$conn->execute('SET foreign_key_checks = 0'); - //$conn->execute('SET unique_checks = 0'); - - try { - $unproc_blocks = Block::query()->select(['Id', 'Height', 'Hash', 'TransactionHashes', 'BlockTime'])->where('TransactionsProcessed',0)->orderBy('Height')->get(); - foreach ($unproc_blocks as $min_block) { - $tx_hashes = json_decode($min_block->TransactionHashes); - if ($tx_hashes && is_array($tx_hashes)) { - $block_time = $min_block->BlockTime; - $block_ts = DateTime::createFromFormat('U', $block_time); - - $count = count($tx_hashes); - echo "Processing " . $count . " transaction(s) for block $min_block->Height ($min_block->Hash)...\n"; - - $data_error = false; - $conn->beginTransaction(); - - $idx = 0; - foreach ($tx_hashes as $tx_hash) { - $idx++; - $idx_str = str_pad($idx, strlen($count), '0', STR_PAD_LEFT); - echo "[$idx_str/$count] Processing tx hash: $tx_hash... "; - - $total_diff = 0; - $start_ms = round(microtime(true) * 1000); - $exist_tx = Transaction::query()->select(['Id'])->where('Hash', $tx_hash)->first(); - $end_ms = round(microtime(true) * 1000); - $diff_ms = $end_ms - $start_ms; - $total_diff += $diff_ms; - echo "findtx took {$diff_ms}ms. "; - - if ($exist_tx) { - echo "Exists. Skipping.\n"; - continue; - } - - $start_ms = round(microtime(true) * 1000); - $this->processtx($tx_hash, $block_ts, ['hash' => $min_block->Hash, 'time' => $min_block->BlockTime], $data_error); - $diff_ms = round(microtime(true) * 1000) - $start_ms; - $total_diff += $diff_ms; - echo "tx took {$diff_ms}ms. Total {$total_diff}ms. "; - - if (!$data_error && self::$redis && self::$redis->sismember(self::mempooltxkey, $tx_hash)) { - self::$redis->srem(self::mempooltxkey, $tx_hash); - echo "Removed $tx_hash from redis mempooltx.\n"; - } - - echo "Done.\n"; - } - - if (!$data_error) { - $conn->statement('UPDATE Blocks SET TransactionsProcessed = 1 WHERE Id = ?', [$min_block->Id]); - } - - if ($data_error) { - echo "Rolling back!\n"; - $conn->rollback(); - throw new Exception('Data save failed!'); - } else { - echo "Data committed.\n"; - $conn->commit(); - } - } - } - - // Try to update txs with null BlockHash - $mempooltx = Transaction::query()->select(['Id', 'Hash'])->where('BlockHash','IS','NULL')->orderBy('Created')->get(); - $idx = 0; - $count = count($mempooltx); - foreach ($mempooltx as $tx) { - $idx++; - $tx_hash = $tx->Hash; - $idx_str = ($count > 10 && $idx < 10) ? '0' . $idx : $idx; - echo "[$idx_str/$count] Processing tx hash: $tx_hash... "; - - $stmt = $conn->getPdo()->query("SELECT Hash, BlockTime FROM Blocks WHERE TransactionHashes LIKE CONCAT('%', ?, '%') AND Height > ((SELECT MAX(Height) FROM Blocks) - 10000) ORDER BY Height ASC LIMIT 1", [$tx_hash]); - $block = $stmt->fetch(PDO::FETCH_OBJ); - if ($block) { - $upd_tx = ['Id' => $tx->Id, 'BlockHash' => $block->Hash, 'TransactionTime' => $block->BlockTime]; - $upd_entity = new Transaction($upd_tx); - $upd_entity->save(); - echo "Done.\n"; - - if (self::$redis && self::$redis->sismember(self::mempooltxkey, $tx_hash)) { - self::$redis->srem(self::mempooltxkey, $tx_hash); - echo "Removed $tx_hash from redis mempooltx.\n"; - } - } else { - echo "Block not found.\n"; - } - } - } catch (Exception $e) { - print_r($e); - } - - //$conn->execute('SET foreign_key_checks = 1'); - //$conn->execute('SET unique_checks = 1'); - - self::unlock('parsetxs'); - } - - /** - * @throws Throwable - */ - public function parsenewblocks(): void{ - - set_time_limit(0); - self::lock('parsenewblocks'); - - echo "Parsing new blocks...\n"; - try { - // Get the best block hash - $req = ['method' => 'getbestblockhash', 'params' => [],'id'=>rand()]; - $response = self::curl_json_post(self::$rpcurl, json_encode($req)); - $json = json_decode($response); - print_r($response); print_r($json); - $best_hash = $json->result; - - $req = ['method' => 'getblock', 'params' => [$best_hash],'id'=>rand()]; - $response = self::curl_json_post(self::$rpcurl, json_encode($req)); - $json = json_decode($response); - $best_block = $json->result; - - $max_block = Block::query()->select(['Hash', 'Height'])->orderByDesc('Height')->first(); - if (!$max_block) { - self::unlock('parsenewblocks'); - return; - } - - $min_height = min($max_block->Height, $best_block->height); - $max_height = max($max_block->Height, $best_block->height); - $height_diff = $best_block->height - $max_block->Height; - - if ($height_diff <= 0) { - self::unlock('parsenewblocks'); - return; - } - - $conn = DB::connection(); - for ($curr_height = $min_height; $curr_height <= $max_height; $curr_height++) { - // get the block hash - $req = ['method' => 'getblockhash', 'params' => [$curr_height],'id'=>rand()]; - $response = self::curl_json_post(self::$rpcurl, json_encode($req)); - $json = json_decode($response); - $curr_block_hash = $json->result; - - $next_block_hash = null; - if ($curr_height < $max_height) { - $req = ['method' => 'getblockhash', 'params' => [$curr_height + 1],'id'=>rand()]; - $response = self::curl_json_post(self::$rpcurl, json_encode($req)); - $json = json_decode($response); - $next_block_hash = $json->result; - } - - $req = ['method' => 'getblock', 'params' => [$curr_block_hash],'id'=>rand()]; - $response = self::curl_json_post(self::$rpcurl, json_encode($req)); - $json = json_decode($response); - $curr_block = $json->result; - - if ($curr_block->confirmations < 0) { - continue; - } - - $next_block = null; - if ($next_block_hash != null) { - $req = ['method' => 'getblock', 'params' => [$next_block_hash],'id'=>rand()]; - $response = self::curl_json_post(self::$rpcurl, json_encode($req)); - $json = json_decode($response); - $next_block = $json->result; - } - - if ($curr_block != null) { - $curr_block_ins = $this->blockdb_data_from_json($curr_block); - if ($next_block != null && $curr_block_ins['NextBlockHash'] == null) { - $curr_block_ins['NextBlockHash'] = $next_block->hash; - } - - $block_data = $curr_block; - $block_id = -1; - // Make sure the block does not exist before inserting - $old_block = Block::query()->select(['Id'])->where('Hash',$block_data->hash)->first(); - if (!$old_block) { - echo "Inserting block $block_data->height ($block_data->hash)... "; - $curr_block_entity = new Block($curr_block_ins); - - $conn->statement('INSERT INTO Blocks (Bits, Chainwork, Confirmations, Difficulty, Hash, Height, MedianTime, MerkleRoot, NameClaimRoot, Nonce, PreviousBlockHash, NextBlockHash, BlockSize, Target, BlockTime, TransactionHashes, Version, VersionHex, Created, Modified) ' . - 'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, UTC_TIMESTAMP(), UTC_TIMESTAMP())', - [ - $curr_block_entity->Bits, - $curr_block_entity->Chainwork, - $curr_block_entity->Confirmations, - $curr_block_ins['Difficulty'], // cakephp 3 why? - $curr_block_entity->Hash, - $curr_block_entity->Height, - $curr_block_entity->MedianTime, - $curr_block_entity->MerkleRoot, - $curr_block_entity->NameClaimRoot, - $curr_block_entity->Nonce, - $curr_block_entity->PreviousBlockHash, - $curr_block_entity->NextBlockHash, - $curr_block_entity->BlockSize, - $curr_block_entity->Target, - $curr_block_entity->BlockTime, - $curr_block_entity->TransactionHashes, - $curr_block_entity->Version, - $curr_block_entity->VersionHex, - ]); - - $stmt = $conn->getPdo()->query('SELECT LAST_INSERT_ID() AS lBlockId'); - $linsert = $stmt->fetch(PDO::FETCH_OBJ); - $curr_block_entity->Id = $linsert->lBlockId; - $block_id = $curr_block_entity->Id; - - echo "Done.\n"; - } else { - echo "Updating block $block_data->height ($block_data->hash) with next block hash: " . $curr_block_ins['NextBlockHash'] . " and confirmations: " . $curr_block_ins['Confirmations'] . "... "; - $upd_block = ['Id' => $old_block->Id, 'NextBlockHash' => $curr_block_ins['NextBlockHash'], 'Confirmations' => $curr_block_ins['Confirmations']]; - $upd_entity = new Block($upd_block); - $block_id = $old_block->Id; - echo "Done.\n"; - } - - $txs = $block_data->tx; - $data_error = false; - foreach ($txs as $tx_hash) { - // Check if the transactions exist and then update the BlockHash and TxTime - $tx = Transaction::query()->select(['Id'])->where('Hash',$tx_hash)->first(); - if ($tx) { - $upd_tx_data = [ - 'Id' => $tx->Id, - 'BlockHash' => $block_data->hash, - 'TransactionTime' => $block_data->time - ]; - $upd_tx_entity = new Transaction($upd_tx_data); - $upd_tx_entity->save(); - echo "Updated tx $tx_hash with block hash and time $block_data->time.\n"; - } else { - // Doesn't exist, create a new transaction - echo "Inserting tx $tx_hash for block height $block_data->height... "; - - $conn->beginTransaction(); - $block_ts = \DateTime::createFromFormat('U', $block_data->time); - $this->processtx($tx_hash, $block_ts, ['hash' => $block_data->hash, 'time' => $block_data->time], $data_error); - - if ($data_error) { - $conn->rollback(); - echo "Insert failed.\n"; - } else { - $conn->commit(); - echo "Done.\n"; - } - } - - // Remove from redis if present - if (!$data_error && self::$redis && self::$redis->sismember(self::mempooltxkey, $tx_hash)) { - self::$redis->srem(self::mempooltxkey, $tx_hash); - echo "Removed $tx_hash from redis mempooltx.\n"; - } - } - - if (!$data_error && $block_id > -1) { - // set TransactionsProcessed to true - $conn->statement('UPDATE Blocks SET TransactionsProcessed = 1 WHERE Id = ?', [$block_id]); - } - } - } - } catch (Exception $e) { - print_r($e); - } - - self::unlock('parsenewblocks'); - } - - /** - * @throws Throwable - */ - public function forevermempool(): void{ - self::lock('forevermempool'); - - $conn = DB::connection(); - - while (true) { - try { - $data = ['method' => 'getrawmempool', 'params' => [],'id'=>rand()]; - $res = self::curl_json_post(self::$rpcurl, json_encode($data)); - $json = json_decode($res); - $txs = $json->result; - $now = new DateTime('now', new DateTimeZone('UTC')); - $data_error = false; - - if (count($txs) === 0) { - // If no transactions found, that means there's nothing in the mempool. Clear redis - if (self::$redis) { - self::$redis->del(self::mempooltxkey); - echo "Empty rawmempool. Cleared mempool txs from redis.\n"; - } - } - - foreach ($txs as $tx_hash) { - // Check redis mempool txs - if (self::$redis && self::$redis->exists(self::mempooltxkey)) { - if (self::$redis->sismember(self::mempooltxkey, $tx_hash)) { - echo "Found processed tx hash: $tx_hash. Skipping.\n"; - continue; - } - } - - echo "Processing tx hash: $tx_hash... "; - $exist_tx = Transaction::query()->select(['Id'])->where('Hash',$tx_hash)->first(); - if ($exist_tx) { - echo "Exists. Skipping.\n"; - continue; - } - - // Process the tx - $conn->beginTransaction(); - $block_ts = new DateTime('now', new DateTimeZone('UTC')); - $this->processtx($tx_hash, $block_ts, null, $data_error); - - if ($data_error) { - echo "Rolling back!\n"; - $conn->rollback(); - throw new Exception('Data save failed!'); - } else { - echo "Data committed.\n"; - $conn->commit(); - - // Save to redis to prevent the DB from behing hit again - if (self::$redis) { - self::$redis->sadd(self::mempooltxkey, $tx_hash); - } - } - } - } catch (Exception $e) { - echo "Mempool database error. Attempting to reconnect.\n"; - - // Final fix for MySQL server has gone away (hopefully) - try { - $conn->disconnect(); - } catch (\Exception $e) { - // ignore possible disconnect errors - } - - $conn->reconnect(); - } - - echo "*******************\n"; - sleep(1); - } - - self::unlock('forevermempool'); - } - - private function txdb_data_from_decoded($decoded_tx): array{ - $tx = [ - 'Version' => $decoded_tx['version'], - 'LockTime' => $decoded_tx['locktime'] - ]; - - $addresses = []; - $inputs = []; - $outputs = []; - - $vin = $decoded_tx['vin']; - $vout = $decoded_tx['vout']; - if (is_array($vin)) { - foreach ($vin as $in) { - if (isset($in['coinbase'])) { - $inputs[] = [ - 'IsCoinbase' => 1, - 'Coinbase' => $in['coinbase'] - ]; - } else { - $inputs[] = [ - 'PrevoutHash' => $in['txid'], - 'PrevoutN' => $in['vout'], - 'ScriptSigAsm' => $in['scriptSig']['asm'], - 'ScriptSigHex' => $in['scriptSig']['hex'], - 'Sequence' => $in['sequence'] - ]; - } - } - - foreach ($vout as $out) { - $outputs[] = [ - 'Vout' => $out['vout'], - 'Value' => bcdiv($out['value'], 100000000, 8), - 'Type' => isset($out['scriptPubKey']['type']) ? $out['scriptPubKey']['type'] : '', - 'ScriptPubKeyAsm' => isset($out['scriptPubKey']['asm']) ? $out['scriptPubKey']['asm'] : '', - 'ScriptPubKeyHex' => isset($out['scriptPubKey']['hex']) ? $out['scriptPubKey']['hex'] : '', - 'RequiredSignatures' => isset($out['scriptPubKey']['reqSigs']) ? $out['scriptPubKey']['reqSigs'] : '', - 'Hash160' => isset($out['scriptPubKey']['hash160']) ? $out['scriptPubKey']['hash160'] : '', - 'Addresses' => isset($out['scriptPubKey']['addresses']) ? json_encode($out['scriptPubKey']['addresses']) : null - ]; - - if (isset($out['scriptPubKey']['addresses'])) { - foreach ($out['scriptPubKey']['addresses'] as $address) { - $addresses[$address] = $address; - } - } - } - } - - return ['tx' => $tx, 'addresses' => $addresses, 'inputs' => $inputs, 'outputs' => $outputs]; - } - - private function blockdb_data_from_json($json_block): array{ - return [ - 'Bits' => $json_block->bits, - 'Chainwork' => $json_block->chainwork, - 'Confirmations' => $json_block->confirmations, - 'Difficulty' => $json_block->difficulty, - 'Hash' => $json_block->hash, - 'Height' => $json_block->height, - 'MedianTime' => $json_block->mediantime, - 'MerkleRoot' => $json_block->merkleroot, - 'NameClaimRoot' => $json_block->nameclaimroot, - 'Nonce' => $json_block->nonce, - 'PreviousBlockHash' => isset($json_block->previousblockhash) ? $json_block->previousblockhash : null, - 'NextBlockHash' => isset($json_block->nextblockhash) ? $json_block->nextblockhash : null, - 'BlockSize' => $json_block->size, - 'Target' => $json_block->target ?? null,//TODO: just $json_block->target - 'BlockTime' => $json_block->time, - 'TransactionHashes' => json_encode($json_block->tx), - 'Version' => $json_block->version, - 'VersionHex' => $json_block->versionHex - ]; - } - - /** - * @param $url - * @param $data - * @param $headers - * @return bool|string - * @throws Exception - */ - private static function curl_json_post($url, $data, $headers = []): string{ - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $data); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - $response = curl_exec($ch); - //Log::debug('Request execution completed.'); - if ($response === false) { - $error = curl_error($ch); - $errno = curl_errno($ch); - curl_close($ch); - - throw new Exception(sprintf('The request failed: %s', $error), $errno); - } else { - curl_close($ch); - } - - // Close any open file handle - return $response; - } - - private static function curl_json_get($url, $headers = []): string{ - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - $response = curl_exec($ch); - //Log::debug('Request execution completed.'); - if ($response === false) { - $error = curl_error($ch); - $errno = curl_errno($ch); - curl_close($ch); - - throw new \Exception(sprintf('The request failed: %s', $error), $errno); - } else { - curl_close($ch); - } - - // Close any open file handle - return $response; - } - - private static $base58chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - - public static $op_codes = [ - ['OP_0', 0], - ['OP_PUSHDATA', 76], - 'OP_PUSHDATA2', - 'OP_PUSHDATA4', - 'OP_1NEGATE', - 'OP_RESERVED', - 'OP_1', - 'OP_2', - 'OP_3', - 'OP_4', - 'OP_5', - 'OP_6', - 'OP_7', - 'OP_8', 'OP_9', 'OP_10', 'OP_11', 'OP_12', 'OP_13', 'OP_14', 'OP_15', 'OP_16', - 'OP_NOP', 'OP_VER', 'OP_IF', 'OP_NOTIF', 'OP_VERIF', 'OP_VERNOTIF', 'OP_ELSE', 'OP_ENDIF', 'OP_VERIFY', - 'OP_RETURN', 'OP_TOALTSTACK', 'OP_FROMALTSTACK', 'OP_2DROP', 'OP_2DUP', 'OP_3DUP', 'OP_2OVER', 'OP_2ROT', 'OP_2SWAP', - 'OP_IFDUP', 'OP_DEPTH', 'OP_DROP', 'OP_DUP', 'OP_NIP', 'OP_OVER', 'OP_PICK', 'OP_ROLL', 'OP_ROT', - 'OP_SWAP', 'OP_TUCK', 'OP_CAT', 'OP_SUBSTR', 'OP_LEFT', 'OP_RIGHT', 'OP_SIZE', 'OP_INVERT', 'OP_AND', - 'OP_OR', 'OP_XOR', 'OP_EQUAL', 'OP_EQUALVERIFY', 'OP_RESERVED1', 'OP_RESERVED2', 'OP_1ADD', 'OP_1SUB', 'OP_2MUL', - 'OP_2DIV', 'OP_NEGATE', 'OP_ABS', 'OP_NOT', 'OP_0NOTEQUAL', 'OP_ADD', 'OP_SUB', 'OP_MUL', 'OP_DIV', - 'OP_MOD', 'OP_LSHIFT', 'OP_RSHIFT', 'OP_BOOLAND', 'OP_BOOLOR', - 'OP_NUMEQUAL', 'OP_NUMEQUALVERIFY', 'OP_NUMNOTEQUAL', 'OP_LESSTHAN', - 'OP_GREATERTHAN', 'OP_LESSTHANOREQUAL', 'OP_GREATERTHANOREQUAL', 'OP_MIN', 'OP_MAX', - 'OP_WITHIN', 'OP_RIPEMD160', 'OP_SHA1', 'OP_SHA256', 'OP_HASH160', - 'OP_HASH256', 'OP_CODESEPARATOR', 'OP_CHECKSIG', 'OP_CHECKSIGVERIFY', 'OP_CHECKMULTISIG', - 'OP_CHECKMULTISIGVERIFY', 'OP_NOP1', 'OP_NOP2', 'OP_NOP3', 'OP_NOP4', 'OP_NOP5', 'OP_CLAIM_NAME', - 'OP_SUPPORT_CLAIM', 'OP_UPDATE_CLAIM', - ['OP_SINGLEBYTE_END', 0xF0], - ['OP_DOUBLEBYTE_BEGIN', 0xF000], - 'OP_PUBKEY', 'OP_PUBKEYHASH', - ['OP_INVALIDOPCODE', 0xFFFF] - ]; - - public static $op_code = [ - '00' => 'OP_0', // or OP_FALSE - '51' => 'OP_1', // or OP_TRUE - '61' => 'OP_NOP', - '6a' => 'OP_RETURN', - '6d' => 'OP_2DROP', - '75' => 'OP_DROP', - '76' => 'OP_DUP', - '87' => 'OP_EQUAL', - '88' => 'OP_EQUALVERIFY', - 'a6' => 'OP_RIPEMD160', - 'a7' => 'OP_SHA1', - 'a8' => 'OP_SHA256', - 'a9' => 'OP_HASH160', - 'aa' => 'OP_HASH256', - 'ac' => 'OP_CHECKSIG', - 'ae' => 'OP_CHECKMULTISIG', - 'b5' => 'OP_CLAIM_NAME', - 'b6' => 'OP_SUPPORT_CLAIM', - 'b7' => 'OP_UPDATE_CLAIM' - ]; - - /*protected static function hash160_to_bc_address($h160, $addrType = 0) { - $vh160 = $c . $h160; - $h = self::_dhash($vh160); - - $addr = $vh160 . substr($h, 0, 4); - return $addr; - //return self::base58_encode($addr); - }*/ - - protected static function _dhash($str, $raw = false): string{ - return hash('sha256', hash('sha256', $str, true), $raw); - } - - public static function _get_vint(&$string): int{ - // Load the next byte, convert to decimal. - $decimal = hexdec(self::_return_bytes($string, 1)); - // Less than 253: Not encoding extra bytes. - // More than 253, work out the $number of bytes using the 2^(offset) - $num_bytes = ($decimal < 253) ? 0 : 2 ^ ($decimal - 253); - // Num_bytes is 0: Just return the decimal - // Otherwise, return $num_bytes bytes (order flipped) and converted to decimal - return ($num_bytes == 0) ? $decimal : hexdec(self::_return_bytes($string, $num_bytes, true)); - } - - public static function hash160($string): string{ - $bs = @pack("H*", $string); - return hash("ripemd160", hash("sha256", $bs, true)); - } - - public static function hash256($string): string{ - $bs = @pack("H*", $string); - return hash("sha256", hash("sha256", $bs, true)); - } - - public static function hash160_to_address($hash160, $address_version = null): string{ - $c = ''; - if ($address_version == self::pubKeyAddress[0]) { - $c = dechex(self::pubKeyAddress[1]); - } else if ($address_version == self::scriptAddress[0]) { - $c = dechex(self::scriptAddress[1]); - } - - $hash160 = $c . $hash160; - $addr = $hash160; - return self::base58_encode_checksum($addr); - } - - public static function base58_encode_checksum($hex): string{ - $checksum = self::hash256($hex); - $checksum = substr($checksum, 0, 8); - $hash = $hex . $checksum; - return self::base58_encode($hash); - } - - public static function _decode_script($script): string{ - $pos = 0; - $data = array(); - while ($pos < strlen($script)) { - $code = hexdec(substr($script, $pos, 2)); // hex opcode. - $pos += 2; - if ($code < 1) { - // OP_FALSE - $push = '0'; - } elseif ($code <= 75) { - // $code bytes will be pushed to the stack. - $push = substr($script, $pos, ($code * 2)); - $pos += $code * 2; - } elseif ($code <= 78) { - // In this range, 2^($code-76) is the number of bytes to take for the *next* number onto the stack. - $szsz = pow(2, $code - 75); // decimal number of bytes. - $sz = hexdec(substr($script, $pos, ($szsz * 2))); // decimal number of bytes to load and push. - $pos += $szsz; - $push = substr($script, $pos, ($pos + $sz * 2)); // Load the data starting from the new position. - $pos += $sz * 2; - } elseif ($code <= 108/*96*/) { - // OP_x, where x = $code-80 - $push = ($code - 80); - } else { - $push = $code; - } - $data[] = $push; - } - - return implode(" ", $data); - } - - /*public static function script_getopname($bytes_hex) { - $index = hexdec($bytes_hex) - 75; - $op = self::$op_codes[$index]; - if (is_array($op)) { - if ($bytes_hex == dechex($op[1])) { - return $op[0]; - } - } - if () - - return str_replace('OP_', '', $op); - }*/ - - public static function get_opcode($opname): ?int{ - $len = count(self::$op_codes); - for ($i = 0; $i < $len; $i++) { - $op = self::$op_codes[$i]; - $op = is_array($op) ? $op[0] : $op; - if ($op === $opname) { - return $i; - } - } - - return null; - } - - public static function match_decoded($decoded, $to_match): bool{ - if (strlen($decoded) != strlen($to_match)) { - return false; - } - - for ($i = 0; $i < count($decoded); $i++) { - $pushdata4 = self::get_opcode('OP_PUSHDATA4'); - if ($to_match[$i] == $pushdata4 && ($pushdata4 >= $decoded[$i][0]) > 0) { - continue; - } - if ($to_match[$i] != $decoded[$i][0]) { - return false; - } - } - - return true; - } - - public static function base58_encode($hex): string{ - if (strlen($hex) == 0) { - return ''; - } - // Convert the hex string to a base10 integer - $num = gmp_strval(gmp_init($hex, 16), 58); - // Check that number isn't just 0 - which would be all padding. - if ($num != '0') { - $num = strtr($num, '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv', self::$base58chars); - } else { - $num = ''; - } - // Pad the leading 1's - $pad = ''; - $n = 0; - while (substr($hex, $n, 2) == '00') { - $pad .= '1'; - $n += 2; - } - return $pad . $num; - } - - public static function decode_tx($raw_transaction): array{ - $math = EccFactory::getAdapter(); - /*$magic_byte = BitcoinLib::magicByte($magic_byte); - $magic_p2sh_byte = BitcoinLib::magicP2SHByte($magic_p2sh_byte);*/ - $raw_transaction = trim($raw_transaction); - if (((bool)preg_match('/^[0-9a-fA-F]{2,}$/i', $raw_transaction) !== true) - || (strlen($raw_transaction)) % 2 !== 0 - ) { - throw new \InvalidArgumentException("Raw transaction is invalid hex"); - } - $txHash = hash('sha256', hash('sha256', pack("H*", trim($raw_transaction)), true)); - $txid = self::_flip_byte_order($txHash); - $info = array(); - $info['txid'] = $txid; - $info['version'] = $math->hexDec(self::_return_bytes($raw_transaction, 4, true)); - /*if (!in_array($info['version'], array('0', '1'))) { - throw new \InvalidArgumentException("Invalid transaction version"); - }*/ - $input_count = self::_get_vint($raw_transaction); - if (!($input_count >= 0 && $input_count <= 4294967296)) { - throw new \InvalidArgumentException("Invalid input count"); - } - $info['vin'] = self::_decode_inputs($raw_transaction, $input_count); - if ($info['vin'] == false) { - throw new \InvalidArgumentException("No inputs in transaction"); - } - $output_count = self::_get_vint($raw_transaction); - if (!($output_count >= 0 && $output_count <= 4294967296)) { - throw new \InvalidArgumentException("Invalid output count"); - } - $info['vout'] = self::_decode_outputs($raw_transaction, $output_count); - $info['locktime'] = $math->hexDec(self::_return_bytes($raw_transaction, 4)); - return $info; - } - - public static function _decode_inputs(&$raw_transaction, $input_count): array{ - $inputs = array(); - // Loop until $input count is reached, sequentially removing the - // leading data from $raw_transaction reference. - for ($i = 0; $i < $input_count; $i++) { - // Load the TxID (32bytes) and vout (4bytes) - $txid = self::_return_bytes($raw_transaction, 32, true); - $vout = self::_return_bytes($raw_transaction, 4, true); - // Script is prefixed with a varint that must be decoded. - $script_length = self::_get_vint($raw_transaction); // decimal number of bytes. - $script = self::_return_bytes($raw_transaction, $script_length); - - // Build input body depending on whether the TxIn is coinbase. - if ($txid == '0000000000000000000000000000000000000000000000000000000000000000') { - $input_body = array('coinbase' => $script); - } else { - $input_body = array('txid' => $txid, - 'vout' => hexdec($vout), - 'scriptSig' => array('asm' => self::_decode_script($script), 'hex' => $script)); - } - // Append a sequence number, and finally add the input to the array. - $input_body['sequence'] = hexdec(self::_return_bytes($raw_transaction, 4)); - $inputs[$i] = $input_body; - } - return $inputs; - } - - public static function _decode_outputs(&$tx, $output_count): array{ - $math = EccFactory::getAdapter(); - /*$magic_byte = BitcoinLib::magicByte($magic_byte); - $magic_p2sh_byte = BitcoinLib::magicP2SHByte($magic_p2sh_byte);*/ - $outputs = array(); - for ($i = 0; $i < $output_count; $i++) { - // Pop 8 bytes (flipped) from the $tx string, convert to decimal, - // and then convert to Satoshis. - $satoshis = $math->hexDec(self::_return_bytes($tx, 8, true)); - // Decode the varint for the length of the scriptPubKey - $script_length = self::_get_vint($tx); // decimal number of bytes - $script = self::_return_bytes($tx, $script_length); - - - try { - $asm = self::_decode_scriptPubKey($script); - } catch (\Exception $e) { - $asm = null; - } - // Begin building scriptPubKey - $scriptPubKey = array( - 'asm' => $asm, - 'hex' => $script - ); - - // Try to decode the scriptPubKey['asm'] to learn the transaction type. - $txn_info = self::_get_transaction_type($scriptPubKey['asm']); - if ($txn_info !== false) { - $scriptPubKey = array_merge($scriptPubKey, $txn_info); - } - $outputs[$i] = array( - 'value' => $satoshis, - 'vout' => $i, - 'scriptPubKey' => $scriptPubKey); - } - return $outputs; - } - - /** - * @throws Exception - */ - public function fixoutputs(): void{ - $sql = 'SELECT * FROM Outputs WHERE Id NOT IN (SELECT OutputId FROM OutputsAddresses)'; - - $conn = DB::connection(); - $stmt = $conn->getPdo()->query($sql); - $outs = $stmt->fetchAll(PDO::FETCH_OBJ); - - foreach ($outs as $out) { - $txn_info = self::_get_transaction_type($out->ScriptPubKeyAsm); - - $out_data = [ - 'Id' => $out->Id, - 'Type' => $txn_info['type'], - 'RequiredSignatures' => $txn_info['reqSigs'], - 'Hash160' => $txn_info['hash160'], - 'Addresses' => json_encode($txn_info['addresses']) - ]; - $out_entity = new Output($out_data); - $out_entity->save(); - - // Fix the addresses - foreach ($txn_info['addresses'] as $address) { - $prev_addr = Address::query()->where('Address',$address)->first(); - $addr_id = -1; - if ($prev_addr) { - $addr_id = $prev_addr->Id; - } else { - $dt = new DateTime($out->Created, new DateTimeZone('UTC')); - $new_addr = [ - 'Address' => $address, - 'FirstSeen' => $dt->format('Y-m-d H:i:s') - ]; - $new_addr_entity = new Address($new_addr); - if ($new_addr_entity->save()) { - $addr_id = $new_addr_entity->Id; - } - } - - if ($addr_id > -1) { - $conn->statement('INSERT INTO OutputsAddresses (OutputId, AddressId) VALUES (?, ?) ON DUPLICATE KEY UPDATE OutputId = OutputId', [$out->Id, $addr_id]); - } - } - - echo "Fixed output $out->Id with new data: " . print_r($out_data, true); - } - } - - public function fixinputs(): void{ - $sql = 'SELECT * FROM Inputs WHERE IsCoinbase <> 1 AND Id NOT IN (SELECT InputId FROM InputsAddresses)'; - - $conn = DB::connection(); - $stmt = $conn->getPdo()->query($sql); - $ins = $stmt->fetchAll(PDO::FETCH_OBJ); - - foreach ($ins as $in) { - $prev_tx_hash = $in->PrevoutHash; - $prev_n = $in->PrevoutN; - - // Get the previous transaction - $prev_tx = Transaction::query()->select(['Id'])->where('Hash',$prev_tx_hash)->first(); - if (!$prev_tx) { - echo "Previous tx for hash $prev_tx_hash not found.\n"; - continue; - } - - $prev_tx_id = $prev_tx->Id; - $src_output = Output::query()->where('TransactionId',$prev_tx_id)->where('Vout',$prev_n)->first(); //TODO: ->contain(['OutputAddresses']) - $in_data = ['Id' => $in->Id]; - if ($src_output) { - $in_data['Value'] = $src_output->Value; - $in_data['AddressId'] = $src_output->OutputAddresses[0]->Id; - - $in_entity = new Input($in_data); - if ($in_entity->save()) { - $conn->statement('INSERT INTO InputsAddresses (InputId, AddressId) VALUES (?, ?) ON DUPLICATE KEY UPDATE InputId = InputId', [$in->Id, $in_data['AddressId']]); - } - } - - echo "Fixed input $in->Id with new data: " . print_r($in_data, true); - } - } - - public static function _get_transaction_type($data): mixed{ - //$magic_byte = BitcoinLib::magicByte($magic_byte); - //$magic_p2sh_byte = BitcoinLib::magicP2SHByte($magic_p2sh_byte); - $has_claim = (strpos($data, 'CLAIM') !== false); - $has_update_claim = (strpos($data, 'UPDATE_CLAIM') !== false); - $has_op_0 = (strpos($data, 'OP_0') !== false); - $data = explode(" ", trim($data)); - // Define information about eventual transactions cases, and - // the position of the hash160 address in the stack. - $define = array(); - $rule = array(); - - // Other standard: pay to pubkey hash - $define['p2pk'] = array('type' => 'pubkeyhash', - 'reqSigs' => 1, - 'data_index_for_hash' => 0); - $rule['p2pk'] = [ - '0' => '/^[0-9a-f]+$/i', - '1' => '/^OP_CHECKSIG/' - ]; - - // Pay to script hash - $define['p2sh'] = array('type' => 'scripthash', - 'reqSigs' => 1, - 'data_index_for_hash' => 1); - $rule['p2sh'] = array( - '0' => '/^OP_HASH160/', - '1' => '/^[0-9a-f]{40}$/i', // pos 1 - '2' => '/^OP_EQUAL/'); - - // Non-standard (claim_name and support_claim) - $define['p2c'] = array('type' => 'nonstandard', - 'reqSigs' => 1, - 'data_index_for_hash' => 7); - $rule['p2c'] = [ - '0' => '/^OP_CLAIM_NAME|OP_SUPPORT_CLAIM/', - '1' => '/^[0-9a-f]+$/i', - '2' => '/^[0-9a-f]+$/i', - '3' => '/^OP_2DROP/', - '4' => '/^OP_DROP/', - '5' => '/^OP_DUP/', - '6' => '/^OP_HASH160/', - '7' => '/^[0-9a-f]{40}$/i', // pos 7 - '8' => '/^OP_EQUALVERIFY/', - '9' => '/^OP_CHECKSIG/', - ]; - - // Non-standard (claim_name and support_claim) - $define['p2c2'] = array('type' => 'nonstandard', - 'reqSigs' => 1, - 'data_index_for_hash' => 7); - $rule['p2c2'] = [ - '0' => '/^OP_CLAIM_NAME|OP_SUPPORT_CLAIM/', - '1' => '/^OP_0/', - '2' => '/^[0-9a-f]+$/i', - '3' => '/^OP_2DROP/', - '4' => '/^OP_DROP/', - '5' => '/^OP_DUP/', - '6' => '/^OP_HASH160/', - '7' => '/^[0-9a-f]{40}$/i', // pos 8 - '8' => '/^OP_EQUALVERIFY/', - '9' => '/^OP_CHECKSIG/' - ]; - - // update_claim - $define['p2uc'] = array('type' => 'nonstandard', - 'reqSigs' => 1, - 'data_index_for_hash' => 8); - $rule['p2uc'] = [ - '0' => '/^OP_UPDATE_CLAIM/', - '1' => '/^[0-9a-f]+$/i', - '2' => '/^[0-9a-f]+$/i', - '3' => '/^[0-9a-f]+$/i', - '4' => '/^OP_2DROP/', - '5' => '/^OP_2DROP/', - '6' => '/^OP_DUP/', - '7' => '/^OP_HASH160/', - '8' => '/^[0-9a-f]{40}$/i', // pos 8 - '9' => '/^OP_EQUALVERIFY/', - '10' => '/^OP_CHECKSIG/' - ]; - - // Standard: pay to pubkey hash - $define['p2ph'] = array('type' => 'pubkeyhash', - 'reqSigs' => 1, - 'data_index_for_hash' => 2); - $rule['p2ph'] = array( - '0' => '/^OP_DUP/', - '1' => '/^OP_HASH160/', - '2' => '/^[0-9a-f]{40}$/i', // 2 - '3' => '/^OP_EQUALVERIFY/', - '4' => '/^OP_CHECKSIG/'); - - if ($has_claim) { - unset($rule['p2ph']); - unset($rule['p2sh']); - - if ($has_op_0) { - unset($rule['p2c']); - } else { - unset($rule['p2c2']); - } - - if ($has_update_claim) { - unset($rule['p2c']); - } else { - unset($rule['p2uc']); - } - } else { - unset($rule['p2c']); - unset($rule['p2c2']); - unset($rule['p2uc']); - } - - // Work out how many rules are applied in each case - $valid = array(); - foreach ($rule as $tx_type => $def) { - $valid[$tx_type] = count($def); - } - - // Attempt to validate against each of these rules. - $matches = []; - for ($index = 0; $index < count($data); $index++) { - $test = $data[$index]; - foreach ($rule as $tx_type => $def) { - if (isset($def[$index])) { - preg_match($def[$index], $test, $matches[$tx_type]); - if (count($matches[$tx_type]) == 1) { - $valid[$tx_type]--; - break; - } - } - } - } - - // Loop through rules, check if any transaction is a match. - foreach ($rule as $tx_type => $def) { - if ($valid[$tx_type] == 0) { - // Load predefined info for this transaction type if detected. - $return = $define[$tx_type]; - if ($tx_type === 'p2pk') { - $return['hash160'] = self::hash160($data[$define[$tx_type]['data_index_for_hash']]); - $return['addresses'][0] = self::hash160_to_address($return['hash160'], self::pubKeyAddress[0]); - } else { - $return['hash160'] = $data[$define[$tx_type]['data_index_for_hash']]; - $return['addresses'][0] = self::hash160_to_address($return['hash160'], ($tx_type === 'p2sh') ? self::scriptAddress[0] : self::pubKeyAddress[0]); // TODO: Pay to claim transaction? - } - unset($return['data_index_for_hash']); - } - } - return (!isset($return)) ? false : $return; - } - - public static function _decode_scriptPubKey($script, $matchBitcoinCore = false): string{ - $data = array(); - while (strlen($script) !== 0) { - $byteHex = self::_return_bytes($script, 1); - $byteInt = hexdec($byteHex); - - if (isset(self::$op_code[$byteHex])) { - // This checks if the OPCODE is defined from the list of constants. - if ($matchBitcoinCore && self::$op_code[$byteHex] == "OP_0") { - $data[] = '0'; - } else if ($matchBitcoinCore && self::$op_code[$byteHex] == "OP_1") { - $data[] = '1'; - } else { - $data[] = self::$op_code[$byteHex]; - } - } elseif ($byteInt >= 0x01 && $byteInt <= 0x4e) { - // This checks if the OPCODE falls in the PUSHDATA range - if ($byteInt == 0x4d) { - // OP_PUSHDATA2 - $byteInt = hexdec(self::_return_bytes($script, 2, true)); - $data[] = self::_return_bytes($script, $byteInt); - } else if ($byteInt == 0x4e) { - // OP_PUSHDATA4 - $byteInt = hexdec(self::_return_bytes($script, 4, true)); - $data[] = self::_return_bytes($script, $byteInt); - } else if ($byteInt == 0x4c) { - $num_bytes = hexdec(self::_return_bytes($script, 1, true)); - $data[] = self::_return_bytes($script, $num_bytes); - } else { - $data[] = self::_return_bytes($script, $byteInt); - } - } elseif ($byteInt >= 0x51 && $byteInt <= 0x60) { - // This checks if the CODE falls in the OP_X range - $data[] = $matchBitcoinCore ? ($byteInt - 0x50) : 'OP_' . ($byteInt - 0x50); - } else { - throw new \RuntimeException("Failed to decode scriptPubKey"); - } - } - - return implode(" ", $data); - } - - /** - * Lock - * @param $process_name - * @return void - */ - public static function lock($process_name): void{ - $lock = Cache::lock($process_name); - if(!$lock->get()){ - echo $process_name." is already running.\n"; - exit(0); - } - } - - /** - * Unlock - * @param $process_name - * @return bool - */ - public static function unlock($process_name): bool{ - Cache::lock($process_name)->forceRelease(); - return true; - } - - public static function _return_bytes(&$string, $byte_count, $reverse = false): string{ - if (strlen($string) < $byte_count * 2) { - throw new InvalidArgumentException("Could not read enough bytes"); - } - $requested_bytes = substr($string, 0, $byte_count * 2); - // Overwrite $string, starting $byte_count bytes from the start. - $string = substr($string, $byte_count * 2); - // Flip byte order if requested. - return ($reverse == false) ? $requested_bytes : self::_flip_byte_order($requested_bytes); - } - - public static function _flip_byte_order($bytes): string{ - return implode('', array_reverse(str_split($bytes, 2))); - } - - /*public function parsehistoryblocks() { - set_time_limit(0); - header('Content-type: text/plain'); - - $block_hash = null; - // Get the minimum block hash first - $minBlock = $this->Blocks->find()->select(['Hash'])->order(['Height' => 'asc'])->first(); - if (!$minBlock) { - // get the best block - $req = ['method' => 'status','id'=>rand()]; - $response = self::curl_json_post(self::lbryurl, json_encode($req)); - $json = json_decode($response); - $block_hash = $json->result->blockchain_status->best_blockhash; - } else { - $block_hash = $minBlock->Hash; - } - - echo "Processing block: $block_hash... "; - $req = ['method' => 'block_show', 'params' => ['blockhash' => $block_hash],'id'=>rand()]; - $response = self::curl_json_post(self::lbryurl, json_encode($req)); - $json = json_decode($response); - $block_data = $json->result; - - // Check if the block exists - $oldBlock = $this->Blocks->find()->select(['Id'])->where(['Hash' => $block_hash])->first(); - if (!$oldBlock) { - // Block does not exist, create the block - $newBlock = $this->blockdb_data_from_json($block_data); - $entity = $this->Blocks->newEntity($newBlock); - $this->Blocks->save($entity); - } - echo "Done.\n"; - - $prevBlockHash = isset($block_data->previousblockhash) ? $block_data->previousblockhash : null; - do { - $oldBlock = $this->Blocks->find()->select(['Id'])->where(['Hash' => $prevBlockHash])->first(); - $req = ['method' => 'block_show', 'params' => ['blockhash' => $prevBlockHash],'id'=>rand()]; - $response = self::curl_json_post(self::lbryurl, json_encode($req)); - $json = json_decode($response); - $block_data = $json->result; - $prevBlockHash = isset($block_data->previousblockhash) ? $block_data->previousblockhash : null; - - if (!$oldBlock) { - echo "Inserting block: $block_data->hash... "; - $newBlock = $this->blockdb_data_from_json($block_data); - $entity = $this->Blocks->newEntity($newBlock); - $this->Blocks->save($entity); - } else { - echo "Updating block: $block_data->hash with confirmations: $block_data->confirmations... "; - $updData = ['Id' => $oldBlock->Id, 'Confirmations' => $block_data->confirmations]; - $entity = $this->Blocks->newEntity($newBlock); - $this->Blocks->save($entity); - } - echo "Done.\n"; - } while($prevBlockHash != null && strlen(trim($prevBlockHash)) > 0); - - exit(0); - } - - public function updatespends() { - set_time_limit(0); - - self::lock('updatespends'); - - try { - $conn = ConnectionManager::get('default'); - $inputs = $this->Inputs->find()->select(['Id', 'PrevoutHash', 'PrevoutN'])->where(['PrevoutSpendUpdated' => 0, 'IsCoinbase <>' => 1])->limit(500000)->toArray(); - - $count = count($inputs); - $idx = 0; - echo sprintf("Processing %d inputs.\n", $count); - foreach ($inputs as $in) { - $idx++; - $idx_str = str_pad($idx, strlen($count), '0', STR_PAD_LEFT); - - $tx = $this->Transactions->find()->select(['Id'])->where(['Hash' => $in->PrevoutHash])->first(); - if ($tx) { - $data_error = false; - - $conn->begin(); - - try { - // update the corresponding output and set it as spent - $conn->execute('UPDATE Outputs SET IsSpent = 1, SpentByInputId = ?, Modified = UTC_TIMESTAMP() WHERE TransactionId = ? AND Vout = ?', [$in->Id, $tx->Id, $in->PrevoutN]); - } catch (\Exception $e) { - $data_error = true; - } - - if (!$data_error) { - // update the input - $in_data = ['Id' => $in->Id, 'PrevoutSpendUpdated' => 1]; - $in_entity = $this->Inputs->newEntity($in_data); - $result = $this->Inputs->save($in_entity); - - if (!$result) { - $data_error = true; - } - } - - if ($data_error) { - echo sprintf("[$idx_str/$count] Could NOT update vout %s for transaction hash %s.\n", $in->PrevoutN, $in->PrevoutHash); - $conn->rollback(); - } else { - echo sprintf("[$idx_str/$count] Updated vout %s for transaction hash %s.\n", $in->PrevoutN, $in->PrevoutHash); - $conn->commit(); - } - } else { - echo sprintf("[$idx_str/$count] Transaction NOT found for tx hash %s.\n", $in->PrevoutHash); - } - } - } catch (\Exception $e) { - print_r($e); - } - - self::unlock('updatespends'); - } - */ - -} diff --git a/app/Http/Controllers/ClaimsController.php b/app/Http/Controllers/ClaimsController.php index 2a88588..b785de9 100644 --- a/app/Http/Controllers/ClaimsController.php +++ b/app/Http/Controllers/ClaimsController.php @@ -22,11 +22,11 @@ class ClaimsController extends Controller{ $order = 'RAND() ASC'; break; case 'oldest': - $order = 'Created ASC'; + $order = 'created_at ASC'; break; case 'newest': default: - $order = 'Created DESC'; + $order = 'created_at DESC'; break; } @@ -39,22 +39,22 @@ class ClaimsController extends Controller{ } $conditions = [ - ['ThumbnailUrl','IS','NOT NULL'], - ['LENGTH(TRIM(ThumbnailUrl))','>',0], - ['IsFiltered','<>',1], + ['thumbnail_url','IS','NOT NULL'], + ['LENGTH(TRIM(thumbnail_url))','>',0], + ['is_filtered','<>',1], ]; if ($afterId > 0) { - $conditions[] = ['Id','>',$afterId]; + $conditions[] = ['id','>',$afterId]; } else if ($beforeId) { - $conditions[] = ['Id','<',$beforeId]; + $conditions[] = ['id','<',$beforeId]; } if ($nsfw !== 'true') { - $conditions[] = ['IsNSFW','<>',1]; + $conditions[] = ['is_nsfw','<>',1]; } //->contain(['Stream', 'Publisher' => ['fields' => ['Name']]]) - $claims = Claim::query()->distinct(['ClaimId'])->where($conditions)->limit($pageLimit)->orderByRaw($order)->get(); + $claims = Claim::query()->distinct(['claim_id'])->where($conditions)->limit($pageLimit)->orderByRaw($order)->get(); return [ 'success' => true, diff --git a/app/Http/Controllers/MainController.php b/app/Http/Controllers/MainController.php index 2924e37..3ea9de0 100644 --- a/app/Http/Controllers/MainController.php +++ b/app/Http/Controllers/MainController.php @@ -127,9 +127,8 @@ class MainController extends Controller{ */ public function index(): JsonResponse|Response|View{ $lbcUsdPrice = $this->_getLatestPrice(); - $blocks = Block::query()->select(['Chainwork','Confirmations','Difficulty','Hash','Height','BlockTime','BlockSize'])->selectRaw('JSON_LENGTH(`TransactionHashes`) AS tx_count')->orderByDesc('Height')->limit(6)->get(); - $claims = Claim::query()->limit(5)->get(); - //$claims = $this->Claims->find()->select($this->Claims)->select(['publisher' => 'C.name'])->leftJoin(['C' => 'claim'], ['C.claim_id = Claims.publisher_id'])->order(['Claims.created_at' => 'DESC'])->limit(5)->toArray(); + $blocks = Block::query()->select(['chainwork', 'confirmations', 'difficulty', 'hash', 'height', 'block_time', 'block_size','tx_count'])->orderByDesc('height')->limit(6)->get(); + $claims = Claim::query()->leftJoin('claim AS c','c.claim_id','=','claim.publisher_id')->orderByDesc('claim.created_at')->limit(5)->get(); $hashRate = $this->_formatHashRate($this->_gethashrate()); return self::generateResponse('main.index',[ @@ -166,7 +165,7 @@ class MainController extends Controller{ // $count = $stmt->fetch(\PDO::FETCH_OBJ); $numClaims = 20000000; - $stmt = $conn->getPdo()->query('SELECT MAX(Id) AS MaxId FROM Claims'); + $stmt = $conn->getPdo()->query('SELECT MAX(Id) AS MaxId FROM claim'); $res = $stmt->fetch(PDO::FETCH_OBJ); $maxClaimId = $res->MaxId; @@ -185,7 +184,7 @@ class MainController extends Controller{ } $blockedList = json_decode($this->_getBlockedList()); - $claims = [];//Claim::query()->selectRaw('Name AS Publisher')->addSelect(['publisher' => 'C.name', 'publisher_transaction_hash_id' => 'C.transaction_hash_id', 'publisher_vout' => 'C.vout'])->leftJoin('Claims','claim_id','=','Claims.publisher_id')->where('Claims.id','>',$startLimitId)->where( 'Claims.Id','<=',$endLimitId)->orderByDesc('Id')->get(); + $claims = Claim::query()->select(['claim.*'])->addSelect(['c.name AS publisher','c.transaction_hash_id AS publisher_transaction_hash_id','c.vout AS publisher_vout'])->leftJoin('claim AS c','c.claim_id','=','claim.publisher_id')->where('claim.id','>',$startLimitId)->where('claim.id','<=',$endLimitId)->orderByDesc('claim.id')->get(); for ($i = 0; $i < count($claims); $i++) { if ($canConvert && $claims[$i]->fee > 0 && $claims[$i]->fee_currency == 'USD') { @@ -222,7 +221,7 @@ class MainController extends Controller{ 'claims' => $claims, ]); } else { - $claim = Claim::query()->addSelect(['publisher' => 'C.name'])->leftJoin('Claims','claim_id','=','Claims.publisher_id')->where('Claims.claim_id',$id)->orderByDesc('Claims.created_at')->first(); + $claim = Claim::query()->select('claim.*')->where('claim.claim_id',$id)->leftJoin('claim AS c','c.claim_id','=','claim.publisher_id')->orderByDesc('claim.created_at')->first(); if (!$claim) { return Redirect::to('/'); } @@ -285,8 +284,8 @@ class MainController extends Controller{ public function realtime(): JsonResponse|Response|View{ // Load 10 blocks and transactions - $blocks = Block::query()->select(['Height','BlockTime'])->selectRaw('JSON_LENGTH(`TransactionHashes`) AS tx_count')->orderByDesc('Height')->limit(10)->get(); - $transactions = Transaction::query()->select(['Id','Hash','Value','InputCount','OutputCount','TransactionTime','Created'])->orderByDesc('Created')->limit(10)->get(); + $blocks = Block::query()->select(['height','block_time','tx_count'])->orderByDesc('height')->limit(10)->get(); + $transactions = Transaction::query()->select(['id','hash','value','input_count','output_count','transaction_time','created_at'])->orderByDesc('created_at')->limit(10)->get(); return self::generateResponse('main.realtime',[ 'blocks' => $blocks, @@ -299,38 +298,38 @@ class MainController extends Controller{ if(is_numeric($criteria)){ $height = (int) $criteria; - $block = Block::query()->select(['Id'])->where('Height',$height)->first(); + $block = Block::query()->select(['id'])->where('height',$height)->first(); if($block){ return Redirect::to('/blocks/'.$height); } }elseif(strlen(trim($criteria)) === 34){ // Address - $address = Address::query()->select(['Id','Address'])->where('Address',$criteria)->first(); + $address = Address::query()->select(['id','address'])->where('address',$criteria)->first(); if($address){ - return Redirect::to('/address/'.$address->Address); + return Redirect::to('/address/'.$address->address); } }elseif(strlen(trim($criteria)) === 40){ // Claim ID - $claim = Claim::query()->select(['ClaimId'])->where('ClaimId',$criteria)->first(); + $claim = Claim::query()->select(['claim_id'])->where('claim_id',$criteria)->first(); if($claim){ - return Redirect::to('/claims/'.$claim->ClaimId); + return Redirect::to('/claims/'.$claim->claim_id); } }elseif(strlen(trim($criteria)) === 64) { // block or tx hash // Try block hash first - $block = Block::query()->select(['Height'])->where('Hash',$criteria)->first(); + $block = Block::query()->select(['height'])->where('hash',$criteria)->first(); if($block){ - return Redirect::to('/blocks/'.$block->Height); + return Redirect::to('/blocks/'.$block->height); }else{ - $tx = Transaction::query()->select(['Hash'])->where('Hash',$criteria)->first(); + $tx = Transaction::query()->select(['hash'])->where('hash',$criteria)->first(); if($tx){ - return Redirect::to('/tx/'.$tx->Hash); + return Redirect::to('/tx/'.$tx->hash); } } }else{ // finally, try exact claim name match - $claims = Claim::query()->distinct('ClaimId')->where('Name',$criteria)->orderByDesc('CreatedAt')->limit(10)->get(); //TODO Fix ordering by BidState (Controlling) + $claims = Claim::query()->distinct('claim_id')->where('name',$criteria)->orderByDesc('FIELD(bid_state,"Controlling")')->limit(10)->get(); if(count($claims)===1){ - return Redirect::to('/claims/'.$claims[0]->ClaimId); + return Redirect::to('/claims/'.$claims[0]->claim_id); } return self::generateResponse('main.find',[ 'claims' => $claims, @@ -348,7 +347,7 @@ class MainController extends Controller{ $page = intval(request()->query('page')); $conn = DB::connection(); - $stmt = $conn->getPdo()->query('SELECT Height AS Total FROM Blocks order by Id desc limit 1'); + $stmt = $conn->getPdo()->query('SELECT height AS Total FROM block order by id desc limit 1'); $count = $stmt->fetch(PDO::FETCH_OBJ); $numBlocks = $count->Total; @@ -361,8 +360,8 @@ class MainController extends Controller{ } $offset = ($page - 1) * $pageLimit; - $currentBlock = Block::query()->select(['Height'])->orderByDesc('Height')->first(); - $blocks = Block::query()->select(['Height', 'Difficulty', 'BlockSize', 'Nonce', 'BlockTime'])->offset($offset)->limit($pageLimit)->orderByDesc('Height')->get();//'tx_count' + $currentBlock = Block::query()->select(['height'])->orderByDesc('height')->first(); + $blocks = Block::query()->select(['height', 'difficulty', 'block_size', 'nonce', 'block_time','tx_count'])->offset($offset)->limit($pageLimit)->orderByDesc('height')->get(); return self::generateResponse('main.blocks',[ 'currentBlock' => $currentBlock, @@ -384,7 +383,7 @@ class MainController extends Controller{ } // Get the basic block transaction info - $txs = Transaction::query()->select(['Transactions.id', 'Transactions.value', 'Transactions.input_count', 'Transactions.output_count', 'Transactions.hash', 'Transactions.version'])->where('Transactions.block_hash_id',$block->hash)->get(); + $txs = Transaction::query()->select(['id','value','input_count','output_count','hash','version'])->where('block_hash_id',$block->hash)->get(); $last_block = Block::query()->select(['height'])->orderByDesc('height')->first(); $confirmations = $last_block->height - $block->height + 1; @@ -399,7 +398,7 @@ class MainController extends Controller{ public function tx($hash = null): JsonResponse|Response|RedirectResponse|View{ $sourceAddress = request()->query('address'); - $tx = Transaction::query()->where('Transactions.hash',$hash)->first(); + $tx = Transaction::query()->where('hash',$hash)->first(); if (!$tx) { return Redirect::to('/'); } @@ -636,8 +635,8 @@ class MainController extends Controller{ $conn = DB::connection(); // get avg block sizes for the time period - $stmt = $conn->getPdo()->prepare("SELECT AVG(BlockSize) AS AvgBlockSize, DATE_FORMAT(FROM_UNIXTIME(BlockTime), '$sqlDateFormat') AS TimePeriod " . - "FROM Blocks WHERE DATE_FORMAT(FROM_UNIXTIME(BlockTime), '$sqlDateFormat') >= ? GROUP BY TimePeriod ORDER BY TimePeriod ASC"); + $stmt = $conn->getPdo()->prepare("SELECT AVG(block_size) AS AvgBlockSize, DATE_FORMAT(FROM_UNIXTIME(block_time), '$sqlDateFormat') AS TimePeriod " . + "FROM block WHERE DATE_FORMAT(FROM_UNIXTIME(block_time), '$sqlDateFormat') >= ? GROUP BY TimePeriod ORDER BY TimePeriod ASC"); $stmt->execute([$start->format($dateFormat)]); $avgBlockSizes = $stmt->fetchAll(PDO::FETCH_OBJ); foreach ($avgBlockSizes as $size) { diff --git a/app/Jobs/AddrTXJob.php b/app/Jobs/AddrTXJob.php deleted file mode 100644 index b9bc004..0000000 --- a/app/Jobs/AddrTXJob.php +++ /dev/null @@ -1,19 +0,0 @@ -forceRelease(); - Artisan::call('explorer:block parsetxs'); - Cache::lock('parsetxs')->forceRelease(); - } - -} diff --git a/app/Jobs/ClaimIndexJob.php b/app/Jobs/ClaimIndexJob.php deleted file mode 100644 index f58b97a..0000000 --- a/app/Jobs/ClaimIndexJob.php +++ /dev/null @@ -1,19 +0,0 @@ -forceRelease(); - Artisan::call('explorer:block forevermempool'); - } - -} diff --git a/app/Jobs/LiveTXJob.php b/app/Jobs/LiveTXJob.php deleted file mode 100644 index c14c96c..0000000 --- a/app/Jobs/LiveTXJob.php +++ /dev/null @@ -1,19 +0,0 @@ -Tag = ''; - $this->TagUrl = ''; - $this->Created = Carbon::now(); - $this->Modified = Carbon::now(); - } - } diff --git a/app/Models/Block.php b/app/Models/Block.php index 4052007..60f6527 100644 --- a/app/Models/Block.php +++ b/app/Models/Block.php @@ -5,55 +5,36 @@ use Illuminate\Database\Eloquent\Model; /** * @mixin Model - * @property mixed Bits - * @property mixed Chainwork - * @property mixed Confirmations - * @property mixed Difficulty - * @property mixed Hash - * @property mixed Height - * @property mixed MedianTime - * @property mixed MerkleRoot - * @property mixed NameClaimRoot - * @property mixed Nonce - * @property mixed PreviousBlockHash - * @property mixed NextBlockHash - * @property mixed BlockSize - * @property mixed Target - * @property mixed BlockTime - * @property mixed TransactionHashes - * @property mixed Version - * @property mixed VersionHex + * @property mixed id + * @property mixed bits + * @property mixed chainwork + * @property mixed confirmations + * @property mixed difficulty + * @property mixed hash + * @property mixed height + * @property mixed merkle_root + * @property mixed name_claim_root + * @property mixed nonce + * @property mixed previous_block_hash + * @property mixed next_block_hash + * @property mixed block_size + * @property mixed block_time + * @property mixed version + * @property mixed version_hex + * @property mixed tx_count + * @property mixed created_at + * @property mixed modified_at */ class Block extends Model{ - protected $fillable = [ - 'Bits', - 'Chainwork', - 'Confirmations', - 'Difficulty', - 'Hash', - 'Height', - 'MedianTime', - 'MerkleRoot', - 'NameClaimRoot', - 'Nonce', - 'PreviousBlockHash', - 'NextBlockHash', - 'BlockSize', - 'Target', - 'BlockTime', - 'TransactionHashes', - 'Version', - 'VersionHex', - ]; - protected $table = 'Blocks'; + protected $table = 'block'; public $timestamps = false; public function jsonSerialize(): array{ return [ - 'height' => $this->Height, - 'block_time' => $this->BlockTime, - 'tx_count' => -1,//TODO + 'height' => $this->height, + 'block_time' => $this->block_time, + 'tx_count' => $this->tx_count, ]; } diff --git a/app/Models/Claim.php b/app/Models/Claim.php index 6f4ae37..081be03 100644 --- a/app/Models/Claim.php +++ b/app/Models/Claim.php @@ -6,18 +6,74 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; /** * @mixin Model + * @property mixed id + * @property mixed transaction_hash_id + * @property mixed vout + * @property mixed name + * @property mixed claim_id + * @property mixed claim_type + * @property mixed publisher_id + * @property mixed publisher_sig + * @property mixed certificate + * @property mixed sd_hash + * @property mixed transaction_time + * @property mixed version + * @property mixed value_as_hex + * @property mixed value_as_json + * @property mixed valid_at_height + * @property mixed height + * @property mixed effective_amount + * @property mixed author + * @property mixed description + * @property mixed content_type + * @property mixed is_nsfw + * @property mixed language + * @property mixed thumbnail_url + * @property mixed title + * @property mixed fee + * @property mixed fee_currency + * @property mixed fee_address + * @property mixed is_filtered + * @property mixed bid_state + * @property mixed created_at + * @property mixed modified_at + * @property mixed claim_address + * @property mixed is_cert_valid + * @property mixed is_cert_processed + * @property mixed license + * @property mixed type + * @property mixed release_time + * @property mixed source_hash + * @property mixed source_name + * @property mixed source_size + * @property mixed source_media_type + * @property mixed source_url + * @property mixed frame_width + * @property mixed frame_height + * @property mixed duration + * @property mixed audio_duration + * @property mixed email + * @property mixed has_claim_list + * @property mixed claim_reference + * @property mixed list_type + * @property mixed claim_id_list + * @property mixed transaction_hash_update + * @property mixed vout_update + * @property mixed claim_count */ class Claim extends Model{ - protected $fillable = []; - protected $table = 'Claims'; + protected $casts = [ + 'created_at' => 'datetime', + ]; + protected $table = 'claim'; public $timestamps = false; - public function publisher(): BelongsTo{ - return $this->belongsTo(Claim::class, 'PublisherId','ClaimId'); - } +// public function publisher(): BelongsTo{ +// return $this->belongsTo(Claim::class, 'publisher_id','claim_id'); +// } - function getLbryLink() { + public function getLbryLink(): string{ $link = $this->name; if (isset($this->publisher)) { $link = $this->publisher . '/' . $link; @@ -26,12 +82,12 @@ class Claim extends Model{ return $link; } - function getExplorerLink() { + public function getExplorerLink(): string{ $link = '/claims/' . $this->claim_id; return $link; } - function getContentTag() { + public function getContentTag(): ?string{ $ctTag = null; if (substr($this->content_type, 0, 5) === 'audio') { $ctTag = 'audio'; @@ -47,7 +103,7 @@ class Claim extends Model{ return $ctTag; } - function getAutoThumbText() { + public function getAutoThumbText(): string{ $autoThumbText = ''; if ($this->claim_type == 2) { $autoThumbText = strtoupper(substr($this->name, 1, min(strlen($this->name), 10))); diff --git a/app/Models/ClaimStream.php b/app/Models/ClaimStream.php deleted file mode 100644 index e1939e4..0000000 --- a/app/Models/ClaimStream.php +++ /dev/null @@ -1,15 +0,0 @@ -withSchedule(static function(Schedule $schedule){ - $schedule->job(new AddrTXJob)->everyMinute(); - $schedule->job(new BlocksJob)->everyMinute(); - $schedule->job(new BlockTXJob)->everyMinute(); - $schedule->job(new ClaimIndexJob)->everyMinute(); - $schedule->job(new FixZeroJob)->everyMinute(); - $schedule->job(new ForeverJob)->everyMinute(); - $schedule->job(new LiveTXJob)->everyMinute(); - $schedule->job(new PriceHistoryJob)->everyMinute(); - $schedule->job(new SpendsJob)->everyMinute(); - $schedule->job(new VerifyTagsJob)->everyMinute(); - }) ->create(); diff --git a/config/database.php b/config/database.php index 6ffa9d2..6eb18a4 100644 --- a/config/database.php +++ b/config/database.php @@ -15,7 +15,7 @@ return [ | */ - 'default' => 'default', + 'default' => 'chainquery', /* |-------------------------------------------------------------------------- @@ -29,31 +29,13 @@ return [ */ 'connections' => [ - 'default' => [ + 'chainquery' => [ 'driver' => 'mysql', - 'host' => env('DB_DEFAULT_HOST', '127.0.0.1'), - 'port' => env('DB_DEFAULT_PORT', '3306'), - 'database' => env('DB_DEFAULT_DATABASE', 'laravel'), - 'username' => env('DB_DEFAULT_USERNAME', 'root'), - 'password' => env('DB_DEFAULT_PASSWORD', ''), - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'prefix' => '', - 'prefix_indexes' => true, - 'strict' => true, - 'engine' => null, - 'options' => extension_loaded('pdo_mysql') ? array_filter([ - PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), - ]) : [], - ], - - 'localdb' => [ - 'driver' => 'mysql', - 'host' => env('DB_LOCAL_HOST', '127.0.0.1'), - 'port' => env('DB_LOCAL_PORT', '3306'), - 'database' => env('DB_LOCAL_DATABASE', 'laravel'), - 'username' => env('DB_LOCAL_USERNAME', 'root'), - 'password' => env('DB_LOCAL_PASSWORD', ''), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php deleted file mode 100644 index 584104c..0000000 --- a/database/factories/UserFactory.php +++ /dev/null @@ -1,44 +0,0 @@ - - */ -class UserFactory extends Factory -{ - /** - * The current password being used by the factory. - */ - protected static ?string $password; - - /** - * Define the model's default state. - * - * @return array - */ - public function definition(): array - { - return [ - 'name' => fake()->name(), - 'email' => fake()->unique()->safeEmail(), - 'email_verified_at' => now(), - 'password' => static::$password ??= Hash::make('password'), - 'remember_token' => Str::random(10), - ]; - } - - /** - * Indicate that the model's email address should be unverified. - */ - public function unverified(): static - { - return $this->state(fn (array $attributes) => [ - 'email_verified_at' => null, - ]); - } -} diff --git a/database/migrations/2025_05_01_093125_create_lbry_database.php b/database/migrations/2025_05_01_093125_create_lbry_database.php deleted file mode 100644 index a77e154..0000000 --- a/database/migrations/2025_05_01_093125_create_lbry_database.php +++ /dev/null @@ -1,288 +0,0 @@ -rawColumn('Id','SERIAL'); - - $table->string('Bits',20); - $table->string('Chainwork',70); - $table->unsignedInteger('Confirmations'); - $table->decimal('Difficulty',18,2); - $table->string('Hash',70); - $table->bigInteger('Height'); - $table->bigInteger('MedianTime'); - $table->string('MerkleRoot',70); - $table->string('NameClaimRoot',70); - $table->bigInteger('Nonce'); - $table->string('PreviousBlockHash',70); - $table->string('NextBlockHash',70); - $table->bigInteger('BlockSize'); - $table->string('Target',70); - $table->bigInteger('BlockTime'); - $table->bigInteger('Version'); - $table->string('VersionHex',10); - $table->text('TransactionHashes'); - $table->tinyInteger('TransactionsProcessed')->default(0); - - $table->dateTime('Created'); - $table->dateTime('Modified'); - - $table->primary(['Id'],'PK_Block'); - $table->unique(['Hash'],'Idx_BlockHash'); - //TODO: CONSTRAINT `Cnt_TransactionHashesValidJson` CHECK(`TransactionHashes` IS NULL OR JSON_VALID(`TransactionHashes`)) - $table->index(['Height'],'Idx_BlockHeight'); - $table->index(['BlockTime'],'Idx_BlockTime'); - $table->index(['MedianTime'],'Idx_MedianTime'); - $table->index(['PreviousBlockHash'],'Idx_PreviousBlockHash'); - $table->index(['Created'],'Idx_BlockCreated'); - $table->index(['Modified'],'Idx_BlockModified'); - }); - - Schema::create('Transactions',static function(Blueprint $table): void{ - $table->rawColumn('Id','SERIAL'); - - $table->string('BlockHash',70); - $table->unsignedInteger('InputCount'); - $table->unsignedInteger('OutputCount'); - $table->decimal('Value',18,8); - $table->decimal('Fee',18,8)->default(0); - $table->unsignedBigInteger('TransactionTime'); - $table->unsignedBigInteger('TransactionSize'); - $table->string('Hash',70); - $table->integer('Version'); - $table->unsignedInteger('LockTime'); - $table->text('Raw'); - - $table->dateTime('Created'); - $table->dateTime('Modified'); - $table->rawColumn('CreatedTime','INT UNSIGNED DEFAULT UNIX_TIMESTAMP() NOT NULL'); - //$table->unsignedInteger('CreatedTime')->default('UNIX_TIMESTAMP()'); - - $table->primary(['Id'],'PK_Transaction'); - $table->foreign(['BlockHash'],'FK_TransactionBlockHash')->references(['Hash'])->on('Blocks'); - $table->unique(['Hash'],'Idx_TransactionHash'); - $table->index(['TransactionTime'],'Idx_TransactionTime'); - $table->index(['CreatedTime'],'Idx_TransactionCreatedTime'); - $table->index(['Created'],'Idx_TransactionCreated'); - $table->index(['Modified'],'Idx_TransactionModified'); - }); - - Schema::create('Addresses',static function(Blueprint $table): void{ - $table->rawColumn('Id','SERIAL'); - - $table->string('Address',40); - $table->dateTime('FirstSeen'); - $table->decimal('TotalReceived',18,8)->default(0); - $table->decimal('TotalSent',18,8)->default(0); - $table->decimal('Balance',18,8)->virtualAs('TotalReceived - TotalSent')->persisted(); - $table->string('Tag',30); - $table->string('TagUrl',200); - $table->dateTime('Created'); - $table->dateTime('Modified'); - - $table->primary(['Id'],'PK_Address'); - $table->unique(['Address'],'Idx_AddressAddress'); - $table->unique(['Tag'],'Idx_AddressTag'); - $table->index(['TotalReceived'],'Idx_AddressTotalReceived'); - $table->index(['TotalSent'],'Idx_AddressTotalSent'); - $table->index(['Balance'],'Idx_AddressBalance'); - $table->index(['Created'],'Idx_AddressCreated'); - $table->index(['Modified'],'Idx_AddressModified'); - }); - - Schema::create('Inputs',static function(Blueprint $table): void{ - $table->rawColumn('Id','SERIAL'); - - $table->unsignedBigInteger('TransactionId'); - $table->string('TransactionHash',70); - $table->unsignedBigInteger('AddressId'); - $table->tinyInteger('IsCoinbase')->default(0); - $table->string('Coinbase',70); - $table->string('PrevoutHash',70); - $table->unsignedInteger('PrevoutN'); - $table->tinyInteger('PrevoutSpendUpdated')->default(0); - $table->unsignedInteger('Sequence'); - $table->decimal('Value',18,8); - $table->text('ScriptSigAsm'); - $table->text('ScriptSigHex'); - $table->dateTime('Created'); - $table->dateTime('Modified'); - - $table->primary(['Id'],'PK_Input'); - $table->foreign(['AddressId'],'FK_InputAddress')->references(['Id'])->on('Addresses'); - $table->foreign(['TransactionId'],'FK_InputTransaction')->references(['Id'])->on('Transactions'); - $table->index(['Value'],'Idx_InputValue'); - $table->index(['PrevoutHash'],'Idx_PrevoutHash'); - $table->index(['Created'],'Idx_InputCreated'); - $table->index(['Modified'],'Idx_InputModified'); - }); - - Schema::create('InputsAddresses',static function(Blueprint $table): void{ - $table->unsignedBigInteger('InputId'); - $table->unsignedBigInteger('AddressId'); - - $table->primary(['InputId','AddressId'],'PK_InputAddress'); - $table->foreign(['InputId'],'Idx_InputsAddressesInput')->references('Id')->on('Inputs'); - $table->foreign(['AddressId'],'Idx_InputsAddressesAddress')->references('Id')->on('Addresses'); - }); - - Schema::create('Outputs',static function(Blueprint $table): void{ - $table->rawColumn('Id','SERIAL'); - - $table->unsignedBigInteger('TransactionId'); - $table->decimal('Value',18,8); - $table->unsignedInteger('Vout'); - $table->string('Type',20); - $table->text('ScriptPubKeyAsm'); - $table->text('ScriptPubKeyHex'); - $table->unsignedInteger('RequiredSignatures'); - $table->string('Hash160',50); - $table->text('Addresses'); - $table->tinyInteger('IsSpent')->default(0); - $table->unsignedBigInteger('SpentByInputId'); - - $table->dateTime('Created'); - $table->dateTime('Modified'); - - $table->primary(['Id'],'PK_Output'); - $table->foreign(['TransactionId'],'FK_OutputTransaction')->references(['Id'])->on('Transactions'); - $table->foreign(['SpentByInputId'],'FK_OutputSpentByInput')->references(['Id'])->on('Inputs'); - //TODO CONSTRAINT `Cnt_AddressesValidJson` CHECK(`Addresses` IS NULL OR JSON_VALID(`Addresses`)) - $table->index(['Value'],'Idx_OutputValue'); - $table->index(['Created'],'Idx_OuptutCreated'); - $table->index(['Modified'],'Idx_OutputModified'); - }); - - Schema::create('OutputsAddresses',static function(Blueprint $table): void{ - $table->unsignedBigInteger('OutputId'); - $table->unsignedBigInteger('AddressId'); - - $table->primary(['OutputId','AddressId'],'PK_OutputAddress'); - $table->foreign(['OutputId'],'Idx_OutputsAddressesOutput')->references('Id')->on('Outputs'); - $table->foreign(['AddressId'],'Idx_OutputsAddressesAddress')->references('Id')->on('Addresses'); - }); - - Schema::create('TransactionsAddresses',static function(Blueprint $table): void{ - $table->unsignedBigInteger('TransactionId'); - $table->unsignedBigInteger('AddressId'); - - $table->decimal('DebitAmount',18,8)->default(0)->comment('Sum of the inputs to this address for the tx'); - $table->decimal('CreditAmount',18,8)->default(0)->comment('Sum of the outputs to this address for the tx'); - //$table->dateTime('TransactionTime')->default('UTC_TIMESTAMP()'); - $table->rawColumn('TransactionTime','DATETIME DEFAULT UTC_TIMESTAMP() NOT NULL'); - - $table->primary(['TransactionId','AddressId'],'PK_TransactionAddress'); - $table->foreign(['TransactionId'],'Idx_TransactionsAddressesTransaction')->references('Id')->on('Transactions'); - $table->foreign(['AddressId'],'Idx_TransactionsAddressesAddress')->references('Id')->on('Addresses'); - $table->index(['TransactionTime'],'Idx_TransactionsAddressesTransactionTime'); - $table->index(['DebitAmount'],'Idx_TransactionsAddressesDebit'); - $table->index(['CreditAmount'],'Idx_TransactionsAddressesCredit'); - }); - - Schema::create('Claims',static function(Blueprint $table): void{ - $table->rawColumn('Id','SERIAL'); - - $table->string('TransactionHash',70); - $table->unsignedInteger('Vout'); - $table->string('Name',1024); - $table->char('ClaimId',40); - $table->tinyInteger('ClaimType'); // 1 - CertificateType, 2 - StreamType - $table->char('PublisherId',40)->comment('references a ClaimId with CertificateType'); - $table->string('PublisherSig',200); - $table->text('Certificate'); - $table->unsignedInteger('TransactionTime'); - $table->string('Version',10); - - //Additional fields for easy indexing of stream types - $table->string('Author',512); - $table->mediumText('Description'); - $table->string('ContentType',162); - $table->tinyInteger('IsNSFW')->default(0); - $table->string('Language',20); - $table->text('ThumbnailUrl'); - $table->text('Title'); - $table->decimal('Fee',18,8)->default(0); - $table->char('FeeCurrency',3); - $table->tinyInteger('IsFiltered')->default(0); - - $table->dateTime('Created'); - $table->dateTime('Modified'); - - $table->primary(['Id'],'PK_Claim'); - $table->foreign(['TransactionHash'],'FK_ClaimTransaction')->references(['Hash'])->on('Transactions'); - //TODO $table->foreign(['PublisherId'],'FK_ClaimPublisher')->references(['ClaimId'])->on('Claims'); - $table->unique(['TransactionHash','Vout','ClaimId'],'Idx_ClaimUnique'); - //TODO CONSTRAINT `Cnt_ClaimCertificate` CHECK(`Certificate` IS NULL OR JSON_VALID(`Certificate`)) // certificate type - $table->index(['ClaimId'],'Idx_Claim'); - $table->index(['TransactionTime'],'Idx_ClaimTransactionTime'); - $table->index(['Created'],'Idx_ClaimCreated'); - $table->index(['Modified'],'Idx_ClaimModified'); - - //$table->index(['Author(191)'],'Idx_ClaimAuthor'); - $table->rawIndex('Author(191)','Idx_ClaimAuthor'); - $table->index(['ContentType'],'Idx_ClaimContentType'); - $table->index(['Language'],'Idx_ClaimLanguage'); - //$table->index(['Title(191)'],'Idx_ClaimTitle'); - $table->rawIndex('Title(191)','Idx_ClaimTitle'); - }); - - Schema::create('ClaimStreams',static function(Blueprint $table): void{ - $table->unsignedBigInteger('Id'); - - $table->mediumText('Stream'); - - $table->primary(['Id'],'PK_ClaimStream'); - $table->foreign(['Id'],'PK_ClaimStreamClaim')->references('Id')->on('Claims'); - }); - - Schema::create('PriceHistory',static function(Blueprint $table): void{ - $table->rawColumn('Id','SERIAL'); - - $table->decimal('BTC',18,8)->default(0); - $table->decimal('USD',18,2)->default(0); - - $table->dateTime('Created'); - - $table->primary(['Id'],'PK_PriceHistory'); - $table->unique(['Created'],'Idx_PriceHistoryCreated'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void{ - Schema::dropIfExists('Blocks'); - - Schema::dropIfExists('Transactions'); - - Schema::dropIfExists('Addresses'); - - Schema::dropIfExists('Inputs'); - - Schema::dropIfExists('InputsAddresses'); - - Schema::dropIfExists('Outputs'); - - Schema::dropIfExists('OutputsAddresses'); - - Schema::dropIfExists('TransactionsAddresses'); - - Schema::dropIfExists('Claims'); - - Schema::dropIfExists('ClaimStreams'); - - Schema::dropIfExists('PriceHistory'); - } - -}; diff --git a/database/migrations/2025_05_24_200358_create_auxiliary_database.php b/database/migrations/2025_05_24_200358_create_auxiliary_database.php deleted file mode 100644 index 7c510ea..0000000 --- a/database/migrations/2025_05_24_200358_create_auxiliary_database.php +++ /dev/null @@ -1,42 +0,0 @@ -integer('Id'); - - $table->string('Address',35); - $table->decimal('VerificationAmount',18,8); - $table->string('Tag',30); - $table->string('TagUrl',200); - $table->tinyInteger('IsVerified')->default(0); - - $table->dateTime('Created'); - $table->dateTime('Modified'); - - $table->primary(['Id'],'PK_TagAddressRequest'); - $table->unique(['Address','VerificationAmount'],'Idx_TagAddressRequestId'); - $table->index(['VerificationAmount'],'Idx_TagAddressRequestVerificationAmount'); - $table->index(['Address'],'Idx_TagAddressRequestAddress'); - $table->index(['Created'],'Idx_TagAddressRequestCreated'); - $table->index(['Modified'],'Idx_TagAddressRequestModified'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void{ - Schema::dropIfExists('TagAddressRequests'); - } - -}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php deleted file mode 100644 index d01a0ef..0000000 --- a/database/seeders/DatabaseSeeder.php +++ /dev/null @@ -1,23 +0,0 @@ -create(); - - User::factory()->create([ - 'name' => 'Test User', - 'email' => 'test@example.com', - ]); - } -} diff --git a/resources/views/main/address.blade.php b/resources/views/main/address.blade.php index 3b8b1b2..36af033 100644 --- a/resources/views/main/address.blade.php +++ b/resources/views/main/address.blade.php @@ -1,4 +1,5 @@ @extends('layout.default') +{{ /**@var \App\Models\Address $address*/'' }} @section('title','Address '.$address->address) diff --git a/resources/views/main/blocks.blade.php b/resources/views/main/blocks.blade.php index 374580b..029d77e 100644 --- a/resources/views/main/blocks.blade.php +++ b/resources/views/main/blocks.blade.php @@ -1,7 +1,7 @@ @extends('layout.default') {{ /**@var \App\Models\Block $block*/'' }} -@section('title',isset($block)?('Block Height '.$block->Height):'Blocks') +@section('title',isset($block)?('Block Height '.$block->height):'Blocks') @section('script') @if(isset($block)) @@ -42,16 +42,16 @@ @if(isset($block))
-

LBRY Block {{ $block->Height }}

-

{{ $block->Hash }}

+

LBRY Block {{ $block->height }}

+

{{ $block->hash }}

- @if($block->Height > 0) - « Previous Block + @if($block->height > 0) + « Previous Block @endif - Next Block » + Next Block »
@@ -63,15 +63,15 @@
Block Size (bytes)
Block Time
-
{{ number_format($block->BlockSize, 0, '', ',') }}
-
{{ \DateTime::createFromFormat('U', $block->BlockTime)->format('j M Y H:i:s') . ' UTC' }}
+
{{ number_format($block->block_size, 0, '', ',') }}
+
{{ \DateTime::createFromFormat('U', $block->block_time)->format('j M Y H:i:s') . ' UTC' }}
Bits
Confirmations
-
{{ $block->Bits }}
+
{{ $block->bits }}
{{ $confirmations }}
@@ -79,20 +79,20 @@
Difficulty
Nonce
-
{{ \App\Helpers\AmountHelper::format($block->Difficulty,'') }}
-
{{ $block->Nonce }}
+
{{ \App\Helpers\AmountHelper::format($block->difficulty,'') }}
+
{{ $block->nonce }}
-
Chainwork
{{ $block->Chainwork }}
+
Chainwork
{{ $block->chainwork }}
-
MerkleRoot
{{ $block->MerkleRoot }}
+
MerkleRoot
{{ $block->merkle_root }}
-
NameClaimRoot
{{ $block->NameClaimRoot }}
+
NameClaimRoot
{{ $block->name_claim_root }}