I've been trying to write my own merkle root script in PHP to help with my own understanding, but I've always struggled with recursion.

I've managed to write the function that will hash pairs of TXIDs, but I don't know how to make it keep calling itself so that I end up with only one hash left in the array.


  • Note: I realize this is a programming question, but I thought this merkle root code may prove to be helpful for others interested in trying the same thing.
    – inersha
    Apr 1, 2016 at 11:06
  • You can use this library for Merkle trees: github.com/drupol/phpmerkle Dec 2, 2019 at 15:09

2 Answers 2


Your script was a good attempt at this! I modified made it recursive with some very small changes.

Typically when designing a recursive function, it checks whether the state represents a complete computation, otherwise it runs some code and calls itself again. Your function was missing this part.

It also seemed to be computing the hashes incorrectly - the hashing operation is SHA256(SHA256($pair)). I refactored out the function binFlipByteOrder(), which only works on binary, so we can flip the hash before rendering it as hex.


function binFlipByteOrder($string) {
    return implode('', array_reverse(str_split($string, 1)));

function merkleroot($txids) {

    // Check for when the result is ready, otherwise recursion
    if (count($txids) === 1) {
        return $txids[0];

    // Calculate the next row of hashes
    $pairhashes = [];
    while (count($txids) > 0) {
        if (count($txids) >= 2) {
            // Get first two
            $pair_first = $txids[0];
            $pair_second = $txids[1];

            // Hash them
            $pair = $pair_first.$pair_second;
            $pairhashes[] = hash('sha256', hash('sha256', $pair, true), true);

            // Remove those two from the array

            // Re-set the indexes (the above just nullifies the values) and make a new array without the original first two slots.
            $txids = array_values($txids);

        if (count($txids) == 1) {
            // Get the first one twice
            $pair_first = $txids[0];
            $pair_second = $txids[0];

            // Hash it with itself
            $pair = $pair_first.$pair_second;
            $pairhashes[] = hash('sha256', hash('sha256', $pair, true), true);

            // Remove it from the array

            // Re-set the indexes (the above just nullifies the values) and make a new array without the original first two slots.
            $txids = array_values($txids);

    return merkleroot($pairhashes);

$txids = array(

$txidsBEbinary = [];
foreach ($txids as $txidBE) {
    // covert to binary, then flip
    $txidsBEbinary[] = binFlipByteOrder(hex2bin($txidBE));
$root = merkleroot($txidsBEbinary);

echo bin2hex(binFlipByteOrder($root)) . PHP_EOL;

Here is some pseudo code how it could work:

List<Hash> hashList;     //Contains the transaction ids of all transactions in the block

Hash deriveMerkleRoot(List<Hash> hashList):
  while (hashList.size() > 1) {
    hashList = pairHashes(hashList);
  return hashList[0];                                             // when only one hash is left, you've derived the merkle root

List<Hash> pairHashes(List<Hash> hashList):
  List<Hash> listOfPairedHashes = new List<Hash>();
  for(int i = 0; i < hashList.size(); i++) {
    if(i%2==0) {                                                  // Get every second element
      if(i+1 < hashList.size()) {
        listOfPairedHashes.put(hash(hashList[i], hashList[i+1])); // If there is one more, pair with it as partner
      } else {
        listOfPairedHashes.put(hash(hashList[i], hashList[i]));   // For last element pair with itself
  return listOfPairedHashes;

You'll still need to add some checks for cases such as "hashList is empty" etc. of course.

  • C# is not pseudo and is valid programming language. This is not PHP-related. C# classes and methods just confuse against native PHP functions or PHP in general and does not conform with the OP.
    – WiiLF
    May 4, 2023 at 3:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.