/** * @param BlockInterface $block * @param TransactionSerializerInterface $txSerializer * @return BlockData */ public function parseUtxos(BlockInterface $block, TransactionSerializerInterface $txSerializer) { $blockData = new BlockData(); $unknown = []; $hashStorage = new HashStorage(); // Record every Outpoint required for the block. foreach ($block->getTransactions() as $t => $tx) { if ($tx->isCoinbase()) { continue; } foreach ($tx->getInputs() as $in) { $outpoint = $in->getOutPoint(); $unknown[$outpoint->getTxId()->getBinary() . $outpoint->getVout()] = $outpoint; } } foreach ($block->getTransactions() as $tx) { /** @var BufferInterface $buffer */ $buffer = $txSerializer->serialize($tx); $hash = Hash::sha256d($buffer)->flip(); $hashStorage->attach($tx, $hash); $hashBin = $hash->getBinary(); foreach ($tx->getOutputs() as $i => $out) { $lookup = $hashBin . $i; if (isset($unknown[$lookup])) { // Remove unknown outpoints which consume this output $outpoint = $unknown[$lookup]; $utxo = new Utxo($outpoint, $out); unset($unknown[$lookup]); } else { // Record new utxos which are not consumed in the same block $utxo = new Utxo(new OutPoint($hash, $i), $out); $blockData->remainingNew[] = $utxo; } // All utxos produced are stored $blockData->parsedUtxos[] = $utxo; } } $blockData->requiredOutpoints = array_values($unknown); $blockData->hashStorage = $hashStorage; return $blockData; }
/** * @param BlockHeaderInterface[] $headers * @return HeadersBatch */ public function prepareBatch(array $headers) { $countHeaders = count($headers); if (0 === $countHeaders) { return new HeadersBatch($this->chains->best($this->math), []); } $bestPrev = null; $firstUnknown = null; $hashStorage = new HashStorage(); foreach ($headers as $i => &$head) { if ($this->chains->isKnownHeader($head->getPrevBlock())) { $bestPrev = $head->getPrevBlock(); } $hash = Hash::sha256d($head->getBuffer())->flip(); $hashStorage->attach($head, $hash); if ($firstUnknown === null && !$this->chains->isKnownHeader($hash)) { $firstUnknown = $i; } } if (!$bestPrev instanceof BufferInterface) { throw new \RuntimeException('Headers::accept(): Unknown start header'); } $view = $this->chains->isTip($bestPrev); if ($view === false) { throw new \RuntimeException('Headers::accept(): Unhandled fork'); } $prevIndex = $view->getIndex(); $access = $this->chains->access($view); $batch = []; if ($firstUnknown !== null) { $versionInfo = $this->db->findSuperMajorityInfoByView($view); $forks = new Forks($this->consensus->getParams(), $prevIndex, $versionInfo); for ($i = $firstUnknown; $i < $countHeaders; $i++) { /** * @var BufferInterface $hash * @var BlockHeaderInterface $header */ $header = $headers[$i]; $hash = $hashStorage[$header]; $this->headerCheck->check($hash, $header); $index = $this->getNextIndex($hash, $prevIndex, $header); $forks->next($index); $this->headerCheck->checkContextual($access, $index, $prevIndex, $forks); $batch[] = $index; $prevIndex = $index; } } return new HeadersBatch($view, $batch); }