/**
  * 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;
 }
Exemple #2
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;
 }
 /**
  * @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()]));
 }
Exemple #4
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;
 }
Exemple #5
0
 /**
  * @param BlockInterface $block
  * @return bool
  * @throws \Exception
  */
 public function insertBlock(BlockInterface $block)
 {
     if ($this->debug) {
         echo "db: called insertBlock \n";
     }
     $blockHash = $block->getHeader()->getHash()->getHex();
     try {
         $this->dbh->beginTransaction();
         $txListBind = [];
         $txListData = ['blockHash' => $blockHash];
         // 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 = [];
         $transactions = $block->getTransactions();
         foreach ($transactions as $i => $tx) {
             $hash = $tx->getTxId()->getHex();
             $valueOut = $tx->getValueOut();
             $nOut = count($tx->getOutputs());
             $nIn = count($tx->getInputs());
             $txListBind[] = " ( :blockHash, :tx{$i}) ";
             $txListData["tx{$i}"] = $hash;
             $txBind[] = " ( :hash{$i} , :version{$i} , :nLockTime{$i} , :tx{$i} , :nOut{$i} , :nValueOut{$i} , :nFee{$i} , :isCoinbase{$i} ) ";
             $txData["hash{$i}"] = $hash;
             $txData["tx{$i}"] = $tx->getBinary();
             $txData["nOut{$i}"] = $nOut;
             $txData["nValueOut{$i}"] = $valueOut;
             $txData["nFee{$i}"] = '0';
             $txData["nLockTime{$i}"] = $tx->getLockTime();
             $txData["isCoinbase{$i}"] = $tx->isCoinbase();
             $txData["version{$i}"] = $tx->getVersion();
             $inData["parenthash{$i}"] = $hash;
             for ($j = 0; $j < $nIn; $j++) {
                 $input = $tx->getInput($j);
                 $inBind[] = " ( :parenthash{$i} , :nInput{$i}{$j}, :hashPrevOut{$i}{$j}, :nPrevOut{$i}{$j}, :scriptSig{$i}{$j}, :nSequence{$i}{$j} ) ";
                 $inData["nInput{$i}{$j}"] = $j;
                 $inData["hashPrevOut{$i}{$j}"] = $input->getTransactionId();
                 $inData["nPrevOut{$i}{$j}"] = $input->getVout();
                 $inData["scriptSig{$i}{$j}"] = $input->getScript()->getBinary();
                 $inData["nSequence{$i}{$j}"] = $input->getSequence();
             }
             $outData["parenthash{$i}"] = $hash;
             for ($k = 0; $k < $nOut; $k++) {
                 $output = $tx->getOutput($k);
                 $outBind[] = " ( :parenthash{$i} , :nOutput{$i}{$k}, :value{$i}{$k}, :scriptPubKey{$i}{$k} ) ";
                 $outData["nOutput{$i}{$k}"] = $k;
                 $outData["value{$i}{$k}"] = $output->getValue();
                 $outData["scriptPubKey{$i}{$k}"] = $output->getScript()->getBinary();
             }
         }
         // Finish & prepare each statement
         // Insert the blocks hash
         $blockInsert = $this->dbh->prepare("INSERT INTO blockIndex ( hash ) VALUES ( :hash )");
         $blockInsert->bindValue(':hash', $blockHash);
         $insertTx = $this->dbh->prepare("INSERT INTO transactions (hash, version, nLockTime, transaction, nOut, valueOut, valueFee, isCoinbase ) VALUES " . implode(", ", $txBind));
         unset($txBind);
         $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);
         $blockInsert->execute();
         $insertTxList->execute($txListData);
         $insertTx->execute($txData);
         $insertInputs->execute($inData);
         $insertOutputs->execute($outData);
         $this->dbh->commit();
         return true;
     } catch (\Exception $e) {
         $this->dbh->rollBack();
         echo "INSERT FAIL!\n";
         echo $e->getMessage() . "\n";
         die("this shouldn't happen");
     }
     throw new \RuntimeException('MySqlDb: ');
 }
Exemple #6
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;
 }
 /**
  * @param BlockInterface $blk
  * @return string
  */
 private function cacheIndexBlk(BlockInterface $blk)
 {
     return $this->cacheIndex($blk->getHeader()->getBlockHash());
 }