Bitcoin Merkle Root


Merkle Root is one single information to represent all transactions in a block.

Generate Merkle Root

<?php 

use BitWasp\Buffertools\Buffer;

include_once "../libraries/vendor/autoload.php";
include_once("html_iframe_header.php");

$result = '';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
	try {
		$txIds = explode("\n", $_POST['txids']);
		$merkleRoot = "";
		
		function createMerkleRoot($txIds) {
			
			$txIds = array_map(function ($v) { $result = Buffer::hex(trim($v));return $result->flip()->getHex(); },$txIds);
			
			function createMerkleBranch($hashes) {
				
				$newHashes = [];
	
				$totalTxIds = @count($hashes);
				
				if ($totalTxIds % 2 > 0) {
					$hashes[ $totalTxIds ] = $hashes[ $totalTxIds - 1 ];
				}
				
				$groupHashes = array_chunk($hashes, 2);
				
				foreach($groupHashes as $twoHash) {
					$newHashes[] = hash('sha256', hex2bin(hash('sha256', hex2bin( implode("", $twoHash) ))));
				}
				
				if (@count($newHashes) == 1) {
					return $newHashes[0];
				} else {
					return createMerkleBranch($newHashes);
				}
			}
			
			if (count($txIds) == 1) {
				$merkleRoot = $txIds[0];
			} else {
				$merkleRoot = createMerkleBranch($txIds);
			}
			
			$result = Buffer::hex($merkleRoot);
			return $result->flip()->getHex();
		}
		
		$merkleRoot = createMerkleRoot($txIds);
		
	} catch (Exception $e) {
        $errmsg .= "Problem found. " . $e->getMessage();
    }
}

if ($errmsg) {
?>
	<div class="alert alert-danger">
		<strong>Error!</strong> <?php echo $errmsg?>
	</div>
<?php
}

if ($merkleRoot) {
?>
	<div class="table-responsive">
		<table border=0 class='table'>
			<tr><td>Merkle Root</td><td><?php echo $merkleRoot;?></td></tr>
		</table>
	</div>
<?php
}
?>
<form action='' method='post'>
	<div class="form-group">
		<label for="txids">Tx Ids (Follow TX order in block):</label>
		<textarea rows=10 class="form-control" name='txids' id='txids'><?php echo $_POST['txids']?></textarea>
		Press enter key to place new tx id in new line. For example, you can copy all tx ids from <a href="https://api.blockcypher.com/v1/btc/main/blocks/0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af" target="_blank"><img style='width:12px;height:12px;' src='../media/images/external_link.png'/></a> and match the generated merkle root with merkle root in block.
	</div>

	<input type='submit' class="btn btn-success btn-block"/>
</form>
<?php 
include_once("html_iframe_footer.php");		

Generate Merkle Proof

<?php 

use BitWasp\Buffertools\Buffer;

include_once "../libraries/vendor/autoload.php";
include_once("html_iframe_header.php");

$result = '';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
	try {
		$txIds = explode("\n", $_POST['txids']);
		$merkleRoot = "";
		
		function createMerkleProof($txId, $txIds) {
			
			$proofs = [];
		
			//convert to little endian
			$txId = Buffer::hex(trim($txId))->flip()->getHex();
			
			$txIds = array_map(function ($v) { return Buffer::hex(trim($v))->flip()->getHex(); },$txIds);
			
			function createMerkleBranch($targetHash, $hashes, &$proofs) {
				
				$newHashes = [];
				
				$totalTxIds = @count($hashes);
				
				if ($totalTxIds % 2 > 0) {
					$hashes[ $totalTxIds ] = $hashes[ $totalTxIds - 1 ];
				}
				
				$groupHashes = array_chunk($hashes, 2);
				
				foreach($groupHashes as $twoHash) {
					$newHash = hash('sha256', hex2bin(hash('sha256', hex2bin( implode("", $twoHash) ))));
					
					if ($twoHash[0] == $targetHash OR $twoHash[1] == $targetHash) {
						if ($twoHash[0] == $targetHash) {
							$proofs[] = [$twoHash[1], "right"];
						} else {
							$proofs[] = [$twoHash[0], "left"];
						}
						$targetHash = $newHash;
					}
					
					$newHashes[] = $newHash;
				}
				
				if (@count($newHashes) == 1) {
					return $newHashes[0];
				} else {
					return createMerkleBranch($targetHash, $newHashes,$proofs);
				}
			}
			
			if (count($txIds) > 1) {
				
				createMerkleBranch($txId,$txIds, $proofs);
			}
			
			return $proofs;
		}
		
		$merkleProofs = createMerkleProof($_POST['txid'], $txIds);
	} catch (Exception $e) {
        $errmsg .= "Problem found. " . $e->getMessage();
    }
}

if ($errmsg) {
?>
	<div class="alert alert-danger">
		<strong>Error!</strong> <?php echo $errmsg?>
	</div>
<?php
}

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
	if ($merkleProofs) {
	?>
		<div class="alert alert-success">
			<h6 class="mt-3">Merkle Proofs</h6>
			<textarea class="form-control" rows="5" id="comment" readonly><?php echo json_encode($merkleProofs)?></textarea>
		</div>
	<?php
	} else {
	?>
		<div class="alert alert-success">
			<h6 class="mt-3">Merkle Proofs</h6>
			N/A
		</div>
	<?php 
	}
}
?>

<form action='' method='post'>

	<div class="form-group">
		<label for="txid">Target Tx id:</label>
		<input class="form-control" type='text' name='txid' id='txid' value='<?php echo $_POST['txid']?>'>
	</div>
	
	<div class="form-group">
		<label for="txids">Tx Ids (Follow TX order in block):</label>
		<textarea rows=10 class="form-control" name='txids' id='txids'><?php echo $_POST['txids']?></textarea>
		Press enter key to place new tx id in new line. For example, you can copy all tx ids from <a href="https://api.blockcypher.com/v1/btc/main/blocks/0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af" target="_blank"><img style='width:12px;height:12px;' src='../media/images/external_link.png'/></a>.
	</div>

	<input type='submit' class="btn btn-success btn-block"/>
</form>
<?php 
include_once("html_iframe_footer.php");		

Verify Merkle Proof

<?php 

use BitWasp\Buffertools\Buffer;

include_once "../libraries/vendor/autoload.php";
include_once("html_iframe_header.php");

$result = '';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
	
	try {
		
		$merkleProofs = json_decode($_POST['proofs']);
		if (json_last_error() != JSON_ERROR_NONE) {
			throw new Exception("Merkle Proofs (JSON) has error.");
		}
		
		$txId = Buffer::hex($_POST['txid'])->flip()->getHex();
		$toBeConcatHash = $txId;//tx id in litle endian
		foreach($merkleProofs as $proof) {
			$hash = $proof[0];
			$dir = $proof[1];
	
			if ($dir == 'left') {
				$toBeConcatHash = hash('sha256', hex2bin(hash('sha256', hex2bin($hash.$toBeConcatHash))));
			} else {
				$toBeConcatHash = hash('sha256', hex2bin(hash('sha256', hex2bin($toBeConcatHash.$hash ))));
			}
		}
		$merkleRoot = Buffer::hex($toBeConcatHash)->flip()->getHex();//merkle root in big endian

		
	} catch (Exception $e) {
        $errmsg .= "Problem found. " . $e->getMessage();
    }
}

if ($errmsg) {
?>
	<div class="alert alert-danger">
		<strong>Error!</strong> <?php echo $errmsg?>
	</div>
<?php
}

if ($merkleRoot) {

	if ($merkleRoot == $_POST['root']) { 
	?>
	<div class="alert alert-success">
		<?php echo $merkleRoot?> <b>Matched!</b>
	</div>
	<?php
	} else {
	?>
	<div class="alert alert-danger">
		<?php echo $merkleRoot?> <b>Not Matched!</b>
	</div>
	<?php
	}
				
}
?>
<form action='' method='post'>

	<div class="form-group">
		<label for="root">Merkle Root:</label>
		<input class="form-control" type='text' name='root' id='root' value='<?php echo $_POST['root']?>'>
	</div>
	
	<div class="form-group">
		<label for="txid">Target Tx id:</label>
		<input class="form-control" type='text' name='txid' id='txid' value='<?php echo $_POST['txid']?>'>
	</div>
	
	<div class="form-group">
		<label for="proofs">Merkle Proofs (JSON):</label>
		<input class="form-control" name='proofs' id='proofs' value='<?php echo $_POST['proofs']?>'/>
	</div>

	<input type='submit' class="btn btn-success btn-block"/>
</form>
<?php 
include_once("html_iframe_footer.php");		








Tutorials
About Us
Contents have been open source in GITHUB. Please give me a ⭐ if you found this helpful :)
Community
Problem? Raise me a new issue.
Support Us
Buy me a coffee. so i can spend more nights for this :)

BTCSCHOOLS would like to present you with more pratical but little theory throughout our tutorials. Pages' content are constantly keep reviewed to avoid mistakes, but we cannot warrant correctness of all contents. While using this site, you agree to accept our terms of use, cookie & privacy policy. Copyright 2019 by BTCSCHOOLS. All Rights Reserved.