/**
  * @param BlockInterface $block
  * @return \BitWasp\Buffertools\BufferInterface
  */
 public function serialize(BlockInterface $block)
 {
     $buffer = $block->getBuffer();
     $size = $buffer->getSize();
     $data = new Parser($this->getHeaderTemplate()->write([Buffer::hex($this->network->getNetMagicBytes()), $size]));
     $data->writeBytes($size, $buffer);
     return $data->getBuffer();
 }
Пример #2
0
 /**
  * @param ChainState $state
  * @param BlockInterface $block
  * @return UtxoView
  */
 public function fetchView(ChainState $state, BlockInterface $block)
 {
     $txs = $block->getTransactions();
     $txCount = count($txs);
     if (1 === $txCount) {
         return new UtxoView([]);
     }
     list($required, $newUnspent) = $this->filter($block);
     $found = $this->db->fetchUtxos($required, $block->getHeader()->getPrevBlock());
     return new UtxoView(array_merge($found, $newUnspent));
 }
Пример #3
0
 /**
  * Process a block against the given state of the chain.
  * @param BlockInterface $block
  * @return bool
  */
 public function process(BlockInterface $block)
 {
     // Ignore the genesis block
     $header = $block->getHeader();
     $hash = $header->getBlockHash();
     if ($hash === $this->genesis->getHeader()->getBlockHash()) {
         return true;
     }
     if ($this->index()->height()->contains($hash)) {
         return true;
     }
     try {
         // Attempt to add it to the chain
         $this->add($block);
         $this->pow->checkHeader($header);
         $result = true;
     } catch (BlockPrevNotFound $e) {
         // If it fails because it doesn't elongate the chain, process it as an orphan.
         // Result will be determined
         $result = $this->processOrphan($block);
     } catch (BlockPowError $e) {
         $result = false;
         // Invalid block.
     }
     return $result;
 }
Пример #4
0
 /**
  * @param ChainState $state
  * @param BlockInterface $block
  * @param Headers $headers
  * @param UtxoIdx $utxoIdx
  * @return BlockIndex
  */
 public function accept(ChainState $state, BlockInterface $block, Headers $headers, UtxoIdx $utxoIdx)
 {
     $bestBlock = $state->getLastBlock();
     if ($bestBlock->getHash() !== $block->getHeader()->getPrevBlock()) {
         throw new \RuntimeException('Blocks:accept() Block does not extend this chain!');
     }
     $index = $headers->accept($state, $block->getHeader());
     $this->blockCheck->check($block)->checkContextual($block, $bestBlock);
     //$view = $utxoIdx->fetchView($state, $block);
     $view = $this->db->fetchUtxoView($block);
     $flagP2sh = $this->consensus->scriptVerifyPayToScriptHash($bestBlock->getHeader()->getTimestamp());
     $flags = new Flags($flagP2sh ? InterpreterInterface::VERIFY_P2SH : InterpreterInterface::VERIFY_NONE);
     $nInputs = 0;
     $nFees = 0;
     $nSigOps = 0;
     $txs = $block->getTransactions();
     foreach ($block->getTransactions() as $tx) {
         $nInputs += count($tx->getInputs());
         $nSigOps += $this->blockCheck->getLegacySigOps($tx);
         if ($nSigOps > $this->consensus->getParams()->getMaxBlockSigOps()) {
             throw new \RuntimeException('Blocks::accept() - too many sigops');
         }
         if (!$tx->isCoinbase()) {
             if ($flagP2sh) {
                 $nSigOps = $this->blockCheck->getP2shSigOps($view, $tx);
                 if ($nSigOps > $this->consensus->getParams()->getMaxBlockSigOps()) {
                     throw new \RuntimeException('Blocks::accept() - too many sigops');
                 }
             }
             $fee = $this->math->sub($view->getValueIn($this->math, $tx), $tx->getValueOut());
             $nFees = $this->math->add($nFees, $fee);
             $this->blockCheck->checkInputs($view, $tx, $index->getHeight(), $flags);
         }
     }
     $this->blockCheck->checkCoinbaseSubsidy($txs[0], $nFees, $index->getHeight());
     $this->db->insertBlock($block);
     $state->updateLastBlock($index);
     return $index;
 }
Пример #5
0
 /**
  * @param BlockInterface $block
  * @return \BitWasp\Buffertools\Buffer
  */
 public function serialize(BlockInterface $block)
 {
     return Buffertools::concat($this->headerSerializer->serialize($block->getHeader()), $this->getTxsTemplate()->write([$block->getTransactions()->all()]));
 }
Пример #6
0
 /**
  * @param BlockInterface $block
  * @param HeaderChainViewInterface $chainView
  * @param Headers $headers
  * @param bool $checkSignatures
  * @param bool $checkSize
  * @param bool $checkMerkleRoot
  * @return BlockIndexInterface
  */
 public function accept(BlockInterface $block, HeaderChainViewInterface $chainView, Headers $headers, $checkSignatures = true, $checkSize = true, $checkMerkleRoot = true)
 {
     $hash = $block->getHeader()->getHash();
     $index = $headers->accept($hash, $block->getHeader(), true);
     $outpointSerializer = new CachingOutPointSerializer();
     $txSerializer = new CachingTransactionSerializer(new TransactionInputSerializer($outpointSerializer));
     $blockSerializer = new CachingBlockSerializer($this->math, new BlockHeaderSerializer(), $txSerializer);
     $utxoSet = new UtxoSet($this->db, $outpointSerializer);
     $blockData = $this->prepareBatch($block, $txSerializer, $utxoSet);
     $this->blockCheck->check($block, $txSerializer, $blockSerializer, $checkSize, $checkMerkleRoot)->checkContextual($block, $chainView->getLastBlock());
     $forks = $this->prepareForks($chainView, $index);
     $this->checkBlockData($block, $blockData, $checkSignatures, $forks->getFlags(), $index->getHeight());
     $this->db->transaction(function () use($hash, $block, $blockSerializer, $utxoSet, $blockData) {
         $this->db->insertBlock($hash, $block, $blockSerializer);
         $utxoSet->applyBlock($blockData);
     });
     $chainView->blocks()->updateTip($index);
     $forks->next($index);
     $this->emit('block', [$index, $block, $blockData]);
     print_r($outpointSerializer->stats());
     return $index;
 }
Пример #7
0
 /**
  * {@inheritdoc}
  * @see \BitWasp\Bitcoin\SerializableInterface::getBuffer()
  */
 public function getBuffer()
 {
     return $this->block->getBuffer();
 }
Пример #8
0
 /**
  * @param BlockInterface $block
  * @return UtxoView
  */
 public function fetchUtxoView(BlockInterface $block)
 {
     $txs = $block->getTransactions();
     $txCount = count($txs);
     if (1 == $txCount) {
         return new UtxoView([]);
     }
     list($required, $outputSet) = $this->filterUtxoRequest($block);
     $requiredCount = count($required);
     $initialCount = count($outputSet);
     $joinList = '';
     $queryValues = ['hash' => $block->getHeader()->getPrevBlock()];
     for ($i = 0, $c = count($required), $last = $c - 1; $i < $c; $i++) {
         list($txid, $vout, $txidx) = $required[$i];
         if (0 == $i) {
             $joinList .= "SELECT :hashParent{$i} as hashParent, :noutparent{$i} as nOut, :txidx{$i} as txidx\n";
         } else {
             $joinList .= "  SELECT :hashParent{$i}, :noutparent{$i}, :txidx{$i} \n";
         }
         if ($i < $last) {
             $joinList .= "  UNION ALL\n";
         }
         $queryValues["hashParent{$i}"] = $txid;
         $queryValues["noutparent{$i}"] = $vout;
         $queryValues["txidx{$i}"] = $txidx;
     }
     $sql = "\n              SELECT    listed.hashParent as txid, listed.nOut as vout,\n                        o.value, o.scriptPubKey,\n                        allowed_block.height, listed.txidx\n              FROM      transaction_output o\n              INNER JOIN (\n                {$joinList}\n              ) as listed ON (listed.hashParent = o.parent_tx AND listed.nOut = o.nOutput)\n              INNER JOIN block_transactions as bt on listed.hashParent = bt.transaction_hash\n              JOIN (\n                    SELECT    parent.hash, parent.height\n                    FROM      headerIndex AS tip,\n                              headerIndex AS parent\n                    WHERE     tip.hash = :hash AND tip.lft BETWEEN parent.lft AND parent.rgt\n              ) as allowed_block on bt.block_hash = allowed_block.hash\n              ";
     $stmt = $this->dbh->prepare($sql);
     $stmt->execute($queryValues);
     foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $utxo) {
         $outputSet[] = new Utxo($utxo['txid'], $utxo['vout'], new TransactionOutput($utxo['value'], new Script(new Buffer($utxo['scriptPubKey']))));
     }
     if (count($outputSet) !== $initialCount + $requiredCount) {
         throw new \RuntimeException('Utxo was not found');
     }
     echo "Fetched {$requiredCount} of " . ($initialCount + $requiredCount) . "\n";
     return new UtxoView($outputSet);
 }
Пример #9
0
 /**
  * @param BlockInterface $block
  * @param BlockIndex $prevBlockIndex
  * @return bool
  */
 public function checkContextual(BlockInterface $block, BlockIndex $prevBlockIndex)
 {
     $newHeight = $prevBlockIndex->getHeight() + 1;
     $newTime = $block->getHeader()->getTimestamp();
     foreach ($block->getTransactions() as $transaction) {
         if (!$this->checkTransactionIsFinal($transaction, $newHeight, $newTime)) {
             throw new \RuntimeException('Block contains a non-final transaction');
         }
     }
     return $this;
 }
Пример #10
0
 /**
  * @param BlockInterface $blk
  * @return string
  */
 private function cacheIndexBlk(BlockInterface $blk)
 {
     return $this->cacheIndex($blk->getHeader()->getBlockHash());
 }
Пример #11
0
 /**
  * @param BlockIndexInterface $index
  * @param BlockInterface $block
  * @param BlockData $blockData
  */
 public function logBlock(BlockIndexInterface $index, BlockInterface $block, BlockData $blockData)
 {
     echo count($blockData->remainingNew) . "created and " . count($blockData->requiredOutpoints) . " destroyed\n";
     $this->log('block', ['hash' => $index->getHash()->getHex(), 'height' => $index->getHeight(), 'txs' => count($block->getTransactions()), 'nFees' => gmp_strval($blockData->nFees, 10), 'nSigOps' => $blockData->nSigOps, 'utxos' => ['created' => count($blockData->remainingNew), 'removed' => count($blockData->requiredOutpoints)]]);
 }
Пример #12
0
 /**
  * @param int $blockId
  * @param BlockInterface $block
  * @param HashStorage $hashStorage
  * @return bool
  */
 public function insertBlockTransactions($blockId, BlockInterface $block, HashStorage $hashStorage)
 {
     $txListBind = [];
     $txListData = [];
     $temp = [];
     // Prepare SQL statement adding all transaction inputs in this block.
     $inBind = [];
     $inData = [];
     // Prepare SQL statement adding all transaction outputs in this block
     $outBind = [];
     $outData = [];
     // Add all transactions in the block
     $txBind = [];
     $txData = [];
     /** @var BufferInterface $txHash */
     $transactions = $block->getTransactions();
     foreach ($transactions as $i => $tx) {
         $txHash = $hashStorage[$tx];
         $hash = $txHash->getBinary();
         $temp[$i] = $hash;
         $valueOut = $tx->getValueOut();
         $nOut = count($tx->getOutputs());
         $nIn = count($tx->getInputs());
         $txListBind[] = " ( :headerId, :txId{$i}) ";
         $txBind[] = " ( :hash{$i} , :version{$i} , :nLockTime{$i} , :nOut{$i} , :nValueOut{$i} , :nFee{$i} , :isCoinbase{$i} ) ";
         $txData["hash{$i}"] = $hash;
         $txData["nOut{$i}"] = $nOut;
         $txData["nValueOut{$i}"] = $valueOut;
         $txData["nFee{$i}"] = '0';
         $txData["nLockTime{$i}"] = $tx->getLockTime();
         $txData["isCoinbase{$i}"] = (int) $tx->isCoinbase();
         $txData["version{$i}"] = $tx->getVersion();
         for ($j = 0; $j < $nIn; $j++) {
             $input = $tx->getInput($j);
             $inBind[] = " ( :parentId{$i} , :nInput" . $i . "n" . $j . ", :hashPrevOut" . $i . "n" . $j . ", :nPrevOut" . $i . "n" . $j . ", :scriptSig" . $i . "n" . $j . ", :nSequence" . $i . "n" . $j . " ) ";
             $outpoint = $input->getOutPoint();
             $inData["hashPrevOut" . $i . "n" . $j] = $outpoint->getTxId()->getBinary();
             $inData["nPrevOut" . $i . "n" . $j] = (int) $outpoint->getVout();
             $inData["scriptSig" . $i . "n" . $j] = $input->getScript()->getBinary();
             $inData["nSequence" . $i . "n" . $j] = $input->getSequence();
             $inData["nInput" . $i . "n" . $j] = $j;
         }
         for ($k = 0; $k < $nOut; $k++) {
             $output = $tx->getOutput($k);
             $outBind[] = " ( :parentId{$i} , :nOutput" . $i . "n" . $k . ", :value" . $i . "n" . $k . ", :scriptPubKey" . $i . "n" . $k . " ) ";
             $outData["value" . $i . "n" . $k] = $output->getValue();
             $outData["scriptPubKey" . $i . "n" . $k] = $output->getScript()->getBinary();
             $outData["nOutput" . $i . "n" . $k] = $k;
         }
     }
     $insertTx = $this->dbh->prepare('INSERT INTO transactions  (hash, version, nLockTime, nOut, valueOut, valueFee, isCoinbase ) VALUES ' . implode(', ', $txBind));
     $insertTx->execute($txData);
     unset($txBind);
     // Populate inserts
     $txListData['headerId'] = $blockId;
     $lastId = (int) $this->dbh->lastInsertId();
     foreach ($temp as $i => $hash) {
         $rowId = $i + $lastId;
         $val = $rowId;
         $outData["parentId{$i}"] = $val;
         $inData["parentId{$i}"] = $val;
         $txListData["txId{$i}"] = $val;
     }
     unset($val);
     $insertTxList = $this->dbh->prepare('INSERT INTO block_transactions  (block_hash, transaction_hash) VALUES ' . implode(', ', $txListBind));
     unset($txListBind);
     $insertInputs = $this->dbh->prepare('INSERT INTO transaction_input (parent_tx, nInput, hashPrevOut, nPrevOut, scriptSig, nSequence) VALUES ' . implode(', ', $inBind));
     unset($inBind);
     $insertOutputs = $this->dbh->prepare('INSERT INTO transaction_output  (parent_tx, nOutput, value, scriptPubKey) VALUES ' . implode(', ', $outBind));
     unset($outBind);
     $insertTxList->execute($txListData);
     $insertInputs->execute($inData);
     $insertOutputs->execute($outData);
     return true;
 }