/** * @param callable|null $hashFunction * @return Buffer * @throws MerkleTreeEmpty */ public function calculateHash(callable $hashFunction = null) { $hashFxn = $hashFunction ?: function ($value) { return hash('sha256', hash('sha256', $value, true), true); }; $txCount = count($this->transactions); 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) { $binary = $hashFxn($this->transactions[0]->getBinary()); } else { // Create a fixed size Merkle Tree $tree = new FixedSizeTree($txCount + $txCount % 2, $hashFxn); // Compute hash of each transaction $last = ''; foreach ($this->transactions as $i => $transaction) { $last = $transaction->getBinary(); $tree->set($i, $last); } // Check if we need to repeat the last hash (odd number of transactions) if (!$this->math->isEven($txCount)) { $tree->set($txCount, $last); } $binary = $tree->hash(); } $this->setLastHash((new Buffer($binary))->flip()); return $this->getLastHash(); }