/** * @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 BlockInterface $block * @param TransactionSerializerInterface $txSerializer * @return Buffer * @throws MerkleTreeEmpty */ public function calcMerkleRoot(BlockInterface $block, TransactionSerializerInterface $txSerializer) { $hashFxn = function ($value) { return hash('sha256', hash('sha256', $value, true), true); }; $txCount = count($block->getTransactions()); if ($txCount === 0) { // TODO: Probably necessary. Should always have a coinbase at least. throw new MerkleTreeEmpty('Cannot compute Merkle root of an empty tree'); } if ($txCount === 1) { $transaction = $block->getTransaction(0); $serialized = $txSerializer->serialize($transaction); $binary = $hashFxn($serialized->getBinary()); } else { // Create a fixed size Merkle Tree $tree = new FixedSizeTree($txCount + $txCount % 2, $hashFxn); // Compute hash of each transaction $last = ''; foreach ($block->getTransactions() as $i => $transaction) { $last = $txSerializer->serialize($transaction)->getBinary(); $tree->set($i, $last); } // Check if we need to repeat the last hash (odd number of transactions) if (!($txCount % 2 === 0)) { $tree->set($txCount, $last); } $binary = $tree->hash(); } return (new Buffer($binary))->flip(); }