Bitcoin P2SH


P2SH (Pay To Script Hash) allows you to easily customize both standard script & non stardard script into client accepted script. To spend it, you must provide corresponded redeem script.
  • In bitcoin, P2SH address start with 3.
  • Address encoded in BASE58 format.
  • Stands for "Pay To Script Hash".
  • Introduced in BIP16.
  • Redeem script is script's script and you could consider it as new locking script.
  • High transaction fee is bear by redeemer rather than spender because this is redeemer's willingness to use more functional scripts.
Script Pair of P2SH
ScriptPubKey: OP_HASH160 <redeemScriptHash> OP_EQUAL
ScriptSig: <redeemScript>

P2SH Address

<?php 
use BitWasp\Bitcoin\Bitcoin;
use BitWasp\Buffertools\Buffer;
use BitWasp\Bitcoin\Network\NetworkFactory;
use BitWasp\Bitcoin\Address\ScriptHashAddress;
use BitWasp\Bitcoin\Script\ScriptFactory;

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

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
	try {
		
		$networkClass   = $_POST['network'];
		Bitcoin::setNetwork(NetworkFactory::$networkClass());
		$network        = Bitcoin::getNetwork();
		
		if (ctype_xdigit($_POST['redeem_script'])) {
			$scriptPubKey = ScriptFactory::fromHex($_POST['redeem_script']);
			$opcodes = $scriptPubKey->getOpcodes();
	
			$p2sh = new ScriptHashAddress($scriptPubKey->getScriptHash());
			$p2shAddress = $p2sh->getAddress();
		} else {
			throw new Exception("Redeem script must be hex.");
		}
		
	
	?>
		<div class="table-responsive">
			<table border=0 class='table'>
				<tr style='background-color:#f0f0f0'><td>Base58 address</td><td><?php echo $p2shAddress;?></td></tr>
				<tr><td>Redeem Script Hex </td><td><?php echo $scriptPubKey->getHex();?></td></tr>
				<tr><td>Redeem Script Asm</td>
					<td>
						<?php echo $scriptPubKey->getScriptParser()->getHumanReadable();?>
					</td>
				</tr>
				
				<tr><td>Redeem Script Hash Hex</td><td><?php echo $scriptPubKey->getScriptHash()->getHex();?></td></tr>
				
				<tr style='background-color:#f0f0f0'><td>ScriptPubKey Hex </td><td><?php echo $p2sh->getScriptPubKey()->getHex()?></td></tr>
				<tr style='background-color:#f0f0f0'><td>ScriptPubKey Asm</td>
					<td>
						<?php 
						foreach( $p2sh->getScriptPubKey()->getScriptParser()->decode() as $operation ) {
							if ($operation->isPush()) {
								
								echo htmlentities("<{$operation->getData()->getHex()}> ");
							} else {
								echo $opcodes->getOp($operation->getOp()) . " " ;
							}
						}
						?>
					</td>
				</tr>
			</table>
		</div>
<?php 
	} catch (Exception $e) {
		$errmsg .= "Problem found. " . $e->getMessage();

	}
} 

if ($errmsg) {
?>
	<div class="alert alert-danger">
		<strong>Error!</strong> <?php echo $errmsg?>
	</div>
<?php
}
?>
<form action='' method='post'>
	<div class="form-group">
		<label for="network">Network:</label>
		<select id="network" name="network" class="form-control" >
			<?php
			$networks = get_class_methods(new NetworkFactory());
			foreach($networks as $network) {
				echo "<option value='{$network}'".($network == $_POST['network'] ? " selected": "").">{$network}</option>";
			}
			?>
		</select>
	</div>
	<div class="form-group">
		<label for="redeem_script">Redeem Script (Hex)</label>
		
		<input class="form-control" type='text' name='redeem_script' id='redeem_script' value='<?php echo $_POST['redeem_script']?>'>
		
		* This script should be saved and should be shared with all the participants before a payment is made, so they may validate the authenticity of the address, it will also be used later to release the bitcoins.
		
	</div>
	<input type='submit' class="btn btn-success btn-block"/>
</form>
<?php
include_once("html_iframe_footer.php");		

Redeem Script Types

Learn to test. Firstly, you have to create P2SH address based on Redeem Script and fund money into it. Secondly, to test spend, you got to apply Unlock Script and fill ScriptSig field within ANY P2SH TX form.

1 + 5 = 6

<?php
use BitWasp\Buffertools\Buffer;
use BitWasp\Bitcoin\Script\ScriptFactory;

include_once "../libraries/vendor/autoload.php";

include_once("html_iframe_header.php");
?>
<div class='row'>
	<?php
	$redeemScript = ScriptFactory::create()->op('OP_5')->op('OP_ADD')->op('OP_6')->op('OP_EQUAL')->getScript();
	?>
	<div class="col-sm-12">
		<h6 class="mt-3" title="Redeem script plays important roles in both lock and unlock tx. That is, unlock tx requires redeem script while lock tx requires redeem script hash.">Redeem Script</h6>
		
		<div class='row'>
			<div class="col-sm-1">
				Hex 
			</div>
			<div class="col-sm-11">
				<textarea class="form-control" rows="1" readonly><?php echo $redeemScript->getHex();?></textarea> <a href='#hashtag1' onclick="javascript:parent.moveData($(this).prev().val(),'iframe_p2sh_address', 'redeem_script')">Insert</a>
			</div>
		</div>
		
		<div class='row'>
			<?php
			$opcodes = $redeemScript->getOpcodes();
			?>
			<div class="col-sm-1">
				Asm
			</div>                    
			<div class="col-sm-11"><textarea class="form-control" rows="1" readonly>
<?php

$opcodes = $redeemScript->getOpcodes();
foreach( $redeemScript->getScriptParser()->decode() as $operation ) {
	
	try {
		$op = $opcodes->getOp($operation->getOp());
	} catch (\RuntimeException $e) {
		$op = "";
	}
	
	echo $op ? $op . " " : "";
	
	if ($op != 'OP_0' AND $operation->isPush()) {
		$bytes = (int)ltrim($op, 'OP_PUSHDATA');
		$bytes = $bytes > 0 ? $bytes : 1;
		
		$hexsize = Buffer::int($operation->getDataSize(), $bytes)->getHex();
		echo htmlentities("{$hexsize}<{$operation->getData()->getHex()}> ");
	} 
}

?>
			</textarea>
			</div>
		</div>
	</div>
	
	<div class="col-sm-6">
		<h6 class="mt-3" title="Spend requires ScriptSig to unlock unspent tx output.">Unlock Script (ScriptSig)</h6>
		<div class='row'>
			<?php
			$scriptSig = ScriptFactory::create()->op('OP_1')->push($redeemScript->getBuffer())->getScript();
			?>
			<div class="col-sm-1">
				Hex 
			</div>
			<div class="col-sm-11"><textarea class="form-control" rows="1" readonly><?php echo $scriptSig->getHex();?></textarea>
			</div>
		</div>
		
		<div class='row'>
			
			<div class="col-sm-1">
				Asm
			</div>
			<div class="col-sm-11">
				<textarea class="form-control" rows="1" readonly>
<?php

$opcodes = $scriptSig->getOpcodes();


foreach( $scriptSig->getScriptParser()->decode() as $operation ) {
	
	try {
		$op = $opcodes->getOp($operation->getOp());
	} catch (\RuntimeException $e) {
		$op = "";
	}
	
	echo $op ? $op . " " : "";
	
	if ($op != 'OP_0' AND $operation->isPush()) {
		$bytes = (int)ltrim($op, 'OP_PUSHDATA');
		$bytes = $bytes > 0 ? $bytes : 1;
		
		$hexsize = Buffer::int($operation->getDataSize(), $bytes)->getHex();
		echo htmlentities("{$hexsize}<{$operation->getData()->getHex()}> ");
	} 
}

?>			
				</textarea>
			</div>
		</div>
	</div>
	
	<div class="col-sm-6">
		<h6 class="mt-3" title="scriptPubKey requires redeemScriptHash.">Lock Script (RedeemScriptHash)</h6>
		
		<div class='row'>
			
			<div class="col-sm-1">
				Hex 
			</div>
			<div class="col-sm-11"><textarea class="form-control" rows="1" readonly><?php echo $redeemScript->getScriptHash()->getHex()?></textarea>
			</div>
		</div>
	</div>
</div>
<?php
include_once("html_iframe_footer.php");				

Multisig wrapped in P2SH

This page is alias of Bitcoin MULTISIG. An example below is 2-of-3 multisig redeem script.
Redeem Script
2 <PubKey1> <PubKey2> <PubKey3> 3 OP_CHECKMULTISIG
<?php
use BitWasp\Buffertools\Buffer;
use BitWasp\Bitcoin\Script\ScriptFactory;

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

$redeemScript = ScriptFactory::create()->op('OP_2')->push(Buffer::hex("04a882d414e478039cd5b52a92ffb13dd5e6bd4515497439dffd691a0f12af9575fa349b5694ed3155b136f09e63975a1700c9f4d4df849323dac06cf3bd6458cd"))->push(Buffer::hex("046ce31db9bdd543e72fe3039a1f1c047dab87037c36a669ff90e28da1848f640de68c2fe913d363a51154a0c62d7adea1b822d05035077418267b1a1379790187"))->push(Buffer::hex("0411ffd36c70776538d079fbae117dc38effafb33304af83ce4894589747aee1ef992f63280567f52f5ba870678b4ab4ff6c8ea600bd217870a8b4f1f09f3a8e83"))->op('OP_3')->op('OP_CHECKMULTISIG')->getScript();
?>
<div class='row'>
	<div class="col-sm-1">
		Hex 
	</div>
	<div class="col-sm-11">
		<textarea class="form-control" rows="3" readonly><?php echo $redeemScript->getHex();?></textarea>
		<a href='#hashtag1' onclick="javascript:$('input#redeem_script').val($(this).prev().val());setTimeout(function() { $('input#redeem_script').focus(); }, 500);">Insert</a>
	</div>
	<?php
	$opcodes = $redeemScript->getOpcodes();
	?>
	<div class="col-sm-1">
		Asm
	</div>					
	<div class="col-sm-11">
		<textarea class="form-control" rows="3" readonly>
<?php
$opcodes = $redeemScript->getOpcodes();
foreach( $redeemScript->getScriptParser()->decode() as $operation ) {
	
	try {
		$op = $opcodes->getOp($operation->getOp());
	} catch (\RuntimeException $e) {
		$op = "";
	}
	
	echo $op ? $op . " " : "";
	
	if ($op != 'OP_0' AND $operation->isPush()) {
		$bytes = (int)ltrim($op, 'OP_PUSHDATA');
		$bytes = $bytes > 0 ? $bytes : 1;
		
		$hexsize = Buffer::int($operation->getDataSize(), $bytes)->getHex();
		echo htmlentities("{$hexsize}<{$operation->getData()->getHex()}> ");
	} 
}
?>
		</textarea>

	</div>

	<div class="col-sm-12">
		<h6 class="mt-3" title="Spend requires ScriptSig to unlock unspent tx output.">Unlock Script (ScriptSig)</h6>
		
		<div class='vertical-line-green'>
			<div class='row'>
				<div class="col-sm-12">
					<?php echo htmlentities("OP_0 <Sig1> <Sig3> OP_PUSHDATA1 <redeemScript>")?>
				</div>
			</div>
			
			<div class='row'>
				<div class="col-sm-12">
					of course the following signature combination <?php echo htmlentities("<Sig1> <Sig2>")?>, <?php echo htmlentities("<Sig2> <Sig3>")?>,  <?php echo htmlentities("<Sig1> <Sig2> <Sig3>")?> are valid too.
				</div>
			</div>
		</div>
		<div class='row'>
			<?php
			$scriptSig = ScriptFactory::create()->op('OP_0')->push(Buffer::hex("30440220762ce7bca626942975bfd5b130ed3470b9f538eb2ac120c2043b445709369628022051d73c80328b543f744aa64b7e9ebefa7ade3e5c716eab4a09b408d2c307ccd701"))->push(Buffer::hex("3045022100abf740b58d79cab000f8b0d328c2fff7eb88933971d1b63f8b99e89ca3f2dae602203354770db3cc2623349c87dea7a50cee1f78753141a5052b2d58aeb592bcf50f01"))->push($redeemScript->getBuffer())->getScript();
			
			?>
			<div class="col-sm-1">
				Hex 
			</div>
			<div class="col-sm-11">
				<textarea class="form-control" rows="3" readonly><?php echo $scriptSig->getHex();?></textarea>
			</div>
		</div>
		<div class='row'>
			<div class="col-sm-1">
				Asm
			</div>
			<div class="col-sm-11">
				<textarea class="form-control" rows="3" readonly>
<?php
$opcodes = $scriptSig->getOpcodes();
foreach( $scriptSig->getScriptParser()->decode() as $operation ) {
	
	try {
		$op = $opcodes->getOp($operation->getOp());
	} catch (\RuntimeException $e) {
		$op = "";
	}
	
	echo $op ? $op . " " : "";
	
	if ($op != 'OP_0' AND $operation->isPush()) {
		$bytes = (int)ltrim($op, 'OP_PUSHDATA');
		$bytes = $bytes > 0 ? $bytes : 1;
		
		$hexsize = Buffer::int($operation->getDataSize(), $bytes)->getHex();
		echo htmlentities("{$hexsize}<{$operation->getData()->getHex()}> ");
	} 
}
?>
				</textarea>
			</div>
		</div>
	</div>
	
	<div class="col-sm-12">
		<h6 class="mt-3" title="scriptPubKey requires redeemScriptHash.">Lock Script (RedeemScriptHash)</h6>
		
		<div class='row'>
			<div class="col-sm-1">
				Hex 
			</div>
			<div class="col-sm-11"><textarea class="form-control" rows="1" readonly><?php echo $redeemScript->getScriptHash()->getHex()?></textarea>
			</div>
		</div>
	</div>
</div>
<?php
include_once("html_iframe_footer.php");				

P2WPKH wrapped in P2SH

This page is alias of Bitcoin P2SH.P2WPKH.

P2WSH wrapped in P2SH

This page is alias of Bitcoin P2SH.P2WSH.

Any P2SH TX

<?php
use BitWasp\Bitcoin\Bitcoin;
use BitWasp\Buffertools\Buffer;
use BitWasp\Bitcoin\Network\NetworkFactory;
use BitWasp\Bitcoin\Key\Factory\PrivateKeyFactory;
use BitWasp\Bitcoin\Address\AddressCreator;
use BitWasp\Bitcoin\Address\Base58AddressInterface;
use BitWasp\Bitcoin\Transaction\Factory\TxBuilder;
use BitWasp\Bitcoin\Transaction\TransactionFactory;
use BitWasp\Bitcoin\Transaction\TransactionOutput;
use BitWasp\Bitcoin\Transaction\Factory\Signer;
use BitWasp\Bitcoin\Script\ScriptFactory;
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;
use BitWasp\Bitcoin\Script\ScriptType;

include_once "../libraries/vendor/autoload.php";

$noOfInputs = 10;
$noOfOutputs = 1;

include_once("html_iframe_header.php");
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
	try {
		$networkClass   = $_POST['network'];
		Bitcoin::setNetwork(NetworkFactory::$networkClass());
		
		$network        = Bitcoin::getNetwork();
		$ecAdapter      = Bitcoin::getEcAdapter();
		$privKeyFactory = new PrivateKeyFactory();
		
		$addrCreator = new AddressCreator();
		
		$spendTx = TransactionFactory::build();
		
		$signItems = [];
		
		
		if (!is_numeric($_POST['no_of_inputs']) OR !is_numeric($_POST['no_of_outputs'])) {
			throw new Exception("Error in 'no_of_inputs' or 'no_of_outputs'.");
		} 
		
		foreach(range(1,$_POST['no_of_inputs']) as $thisInput) {
			$utxoHash = trim($_POST["utxo_hash_{$thisInput}"]);
			$utxoNOutput = trim($_POST["utxo_n_{$thisInput}"]);
			$privkeyhex = trim($_POST["privkey_{$thisInput}"]);
			$utxoScript = trim($_POST["utxo_script_{$thisInput}"]);
			
			if (strlen($utxoHash)>0 AND strlen($utxoNOutput) > 0 AND strlen($privkeyhex) > 0) {
				$spendTx = $spendTx->input($utxoHash, $utxoNOutput);
				$signItems[] = [$privkeyhex, $utxoScript];
			} else {
				throw new Exception("Error in 'input#{$thisInput}'.");
			}
		}
		
		foreach(range(1,$_POST['no_of_outputs']) as $thisOutput) {
			
			$address = trim($_POST["address_{$thisOutput}"]);
			$amount = trim($_POST["amount_{$thisOutput}"]);
			$recipient = $addrCreator->fromString($address);
			
			if (!strlen($address)>0 or !strlen($amount)>0) {
				throw new Exception("Error in 'output#{$thisOutput}'.");
			}
			
			if (!$recipient instanceof Base58AddressInterface) {
				throw new Exception("Invalid P2SH address in 'output#{$thisOutput}' (Check base58Address).");
			} 
			
			$decodeScript = (new OutputClassifier())->decode($recipient->getScriptPubKey());
			
			if ($decodeScript->getType() != ScriptType::P2SH) {
				throw new Exception("Invalid P2SH address in 'output#{$thisOutput}' (Check scriptPubKey).");
			}
			
			$spendTx = $spendTx->payToAddress($amount, $recipient);
			
		}
		
		
		$thisTx = $spendTx->get();
		
		$signer = new Signer($thisTx, $ecAdapter);
		
		foreach($signItems as $nIn=>$signItem) {
			$privateKey = $privKeyFactory->fromHexCompressed($signItem[0]);
			
			$scriptPubKey = ScriptFactory::fromHex($signItem[1]);
			
			$txOutput = new TransactionOutput(0, $scriptPubKey );
			$signer = $signer->sign($nIn, $privateKey, $txOutput);
		}
		
	?>
		<div class="alert alert-success">
			<h6 class="mt-3">Final TX Hex</h6>
			
			<textarea class="form-control" rows="3" readonly><?php echo $signer->get()->getHex();?></textarea>
		</div>
	<?php
	
	} catch (Exception $e) {
		$errmsg .= "Problem found. " . $e->getMessage();

	}
}

if ($errmsg) {
?>
	<div class="alert alert-danger">
		<strong>Error!</strong> <?php echo $errmsg?>
	</div>
<?php
}
?>
<form action='' method='post'>
	<div class="form-group">
		<label for="network">Network: </label>
		<select name="network" id="network" class="form-control">
			<?php
			$networks = get_class_methods(new NetworkFactory());
			foreach($networks as $network) {
				echo "<option value='{$network}'".($network == $_POST['network'] ? " selected": "").">{$network}</option>";
			}
			?>
		</select>
	</div>
	
	<div class="row">
		<div class="col-sm-6">
			
			<div class="form-row">
				<div class="form-group col">
					<label for="no_of_inputs">Inputs: </label> 
					<select class="form-control" id="no_of_inputs" name='no_of_inputs' style='width:auto;' onchange="
						var self = $(this);
						var thisvalue = self.val();
						var form = self.closest('form');
						$('div[id^=row_input_]',form).hide();
						for(var i=1; i<= thisvalue; i++) { 
							$('div[id=row_input_'+  i + ']',form).show();
						}
						">
						<?php
						foreach(range(1,$noOfInputs) as $thisInput) {
							echo "<option value='{$thisInput}'".($thisInput == $_POST['no_of_inputs'] ? " selected": "").">{$thisInput}</option>";
						}
						?>
					</select>
				</div>
			</div>
			
			<?php
			$selectedNInputs = is_numeric($_POST['no_of_inputs']) ? $_POST['no_of_inputs'] : 1;
			
			foreach(range(1,$noOfInputs) as $thisInput) {
			?>
			
				<div class="form-row" id='row_input_<?php echo $thisInput?>' style="<?php echo ($thisInput > $selectedNInputs) ? "display:none" : "display:;"?>">
				
					<div class="form-group  col-sm-1">
						#<?php echo $thisInput?> 
					</div>
					<div class="form-group  col-sm-3">
						
						<input class="form-control" title="UTXO Tx Hash" placeholder='UTXO Tx Hash' type='text' name='utxo_hash_<?php echo $thisInput?>' value='<?php echo $_POST["utxo_hash_{$thisInput}"]?>'>
					</div>
					<div class="form-group  col-sm-1">
						<input class="form-control" title="UTXO N Output" placeholder='N' type='text' name='utxo_n_<?php echo $thisInput?>' value='<?php echo $_POST["utxo_n_{$thisInput}"]?>'>
					</div>
					
					<div class="form-group  col-sm-3">
						<input class="form-control" title="UTXO ScriptPubKey" placeholder='UTXO ScriptPubKey' type='text' name='utxo_script_<?php echo $thisInput?>' value='<?php echo $_POST["utxo_script_{$thisInput}"]?>'>
					</div>
					<div class="form-group  col-sm-4">
						<input class="form-control" title="Private Key Hex, for signing purpose." placeholder='Private Key Hex' type='text' name='privkey_<?php echo $thisInput?>' value='<?php echo $_POST["privkey_{$thisInput}"]?>'>
					</div>
					
				</div>
			<?php
			}
			?>
		</div>
		<div class="col-sm-6">
			<div class="form-row">
				<div class="form-group col">
					<label for="no_of_outputs">Outputs:</label> <select class="form-control" id="no_of_outputs" name='no_of_outputs' style='width:auto;' onchange="
						var self = $(this);
						var thisvalue = self.val();
						var form = self.closest('form');
						$('div[id^=row_output_]',form).hide();
						for(var i=1; i<= thisvalue; i++) { 
							$('div[id=row_output_'+  i + ']',form).show();
						}">
						<?php
						foreach(range(1,$noOfOutputs) as $thisOutput) {
							echo "<option value='{$thisOutput}'".($thisOutput == $_POST['no_of_outputs'] ? " selected": "").">{$thisOutput}</option>";
						}
						?>
					</select>
				</div>
			</div>
			<?php
			$selectedNOutputs = is_numeric($_POST['no_of_outputs']) ? $_POST['no_of_outputs'] : 1;
			
			foreach(range(1,$noOfOutputs) as $thisOutput) {
			?>
				<div class="form-row" id='row_output_<?php echo $thisOutput?>' style="<?php echo ($thisOutput > $selectedNOutputs) ? "display:none" : "display:;"?>">
					<div class="form-group col-sm-1">
						#<?php echo $thisOutput?> 
					</div>
					
					<div class="form-group col-sm-6">
						<input class="form-control" placeholder='P2SH Address' type='text' name='address_<?php echo $thisOutput?>' value='<?php echo $_POST["address_{$thisOutput}"]?>'>
					</div>
					<div class="form-group col-sm-5">
						<input class="form-control" placeholder='Amount' type='text' name='amount_<?php echo $thisOutput?>' value='<?php echo $_POST["amount_{$thisOutput}"]?>'>
					</div>
				</div>
	<?php
			}
	?>
		</div>
	</div>
	<input type='submit' class="btn btn-success btn-block"/>
</form>
<?php
include_once("html_iframe_footer.php");		
<?php

use BitWasp\Bitcoin\Bitcoin;
use BitWasp\Buffertools\Buffer;
use BitWasp\Bitcoin\Network\NetworkFactory;
use BitWasp\Bitcoin\Address\AddressCreator;
use BitWasp\Bitcoin\Transaction\TransactionFactory;
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;
use BitWasp\Bitcoin\Script\ScriptType;
use BitWasp\Bitcoin\Script\Script;

include_once "../libraries/vendor/autoload.php";

$noOfInputs = 10;
$noOfOutputs = 1;

if ($_SERVER['REQUEST_METHOD'] == 'GET' AND $_GET['ajax'] == '1') {
	$data = [];
	if (!ctype_xdigit($_GET['utx'])) {
		$data = ["error"=>"UTX must be hex."];
	} else {
		$utxHex = $_GET['utx'];
		$utx = TransactionFactory::fromHex($utxHex);
		$outputs = $utx->getOutputs();
		
		$data['txHash'] = $utx->getTxHash()->flip()->getHex();//swap endianess
		$data['outputs'] = [];
		if (@count($outputs)) {
			foreach($outputs as $k=>$output) {
				if ($k < 10) { //limit to retrieve max 10 inputs only
					
					$decodeScript = (new OutputClassifier())->decode($output->getScript());
						
					if ($decodeScript->getType() != ScriptType::P2SH) {
						$data = ["error"=>"Load fail! Your unspent transaction output (utxo) can only contain P2SH ScriptPubKey."];
						break;
					}
					$data['outputs'][] = ["amount"=>$output->getValue(), "n"=>$k, "scriptPubKey"=>$output->getScript()->getHex()];
				}
			}
		}
	}
	
	die(json_encode($data));
}
include_once("html_iframe_header.php");

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
	
	try {
		$networkClass   = $_POST['network'];
		Bitcoin::setNetwork(NetworkFactory::$networkClass());
		
		$network        = Bitcoin::getNetwork();
		$ecAdapter      = Bitcoin::getEcAdapter();
		
		$addrCreator = new AddressCreator();
		
		$spendTx = TransactionFactory::build();
		
		$signItems = [];
		
		if (!strlen($_POST['utx']) or !ctype_xdigit($_POST['utx'])) {					
			throw new Exception("UTX must be hex.");
		} 
		
		if (!is_numeric($_POST['no_of_inputs']) OR !is_numeric($_POST['no_of_outputs'])) {
			throw new Exception("Error in 'no_of_inputs' or 'no_of_outputs'.");
		}
		
		$utxHex = $_POST['utx'];
		$utx = TransactionFactory::fromHex($utxHex);	
		$utxos = $utx->getOutputs();
		
		foreach($utxos as $k=>$utxo) {
			$scriptSig = trim($_POST["script_sig_" . ($k+1)]);
			$scriptSig = new Script(Buffer::hex($scriptSig));
			$spendTx = $spendTx->spendOutputFrom($utx, $k, $scriptSig);
		}

		foreach(range(1,$_POST['no_of_outputs']) as $thisOutput) {
			
			$address = trim($_POST["address_{$thisOutput}"]);
			$amount = trim($_POST["amount_{$thisOutput}"]);
			
			if (strlen($address)>0 AND strlen($amount)>0) {
				$recipient = $addrCreator->fromString($address);
				$spendTx = $spendTx->payToAddress($amount, $recipient);
			} else {
				throw new Exception("Error in 'output#{$thisOutput}'.");
			}
		}
		
		
	?>
		<div class="alert alert-success">
			<h6 class="mt-3">Final TX Hex</h6>
			
			<textarea class="form-control" rows="3" readonly><?php echo $spendTx->get()->getHex();?></textarea>
		</div>
	<?php
	
	} catch (Exception $e) {
		$errmsg .= "Problem found. " . $e->getMessage();

	}
}

if ($errmsg) {
?>
	<div class="alert alert-danger">
		<strong>Error!</strong> <?php echo $errmsg?>
	</div>
<?php
}
?>
<form action='' method='post'>
	<div class="form-group">
		<label for="network">Network: </label>
		<select name="network" id="network" class="form-control">
			<?php
			$networks = get_class_methods(new NetworkFactory());
			foreach($networks as $network) {
				echo "<option value='{$network}'".($network == $_POST['network'] ? " selected": "").">{$network}</option>";
			}
			?>
		</select>
	</div>

	<div class="form-group">
		<label for="utx">Unspent Transaction (Hex):</label>
		
		<div class="input-group mb-3">
			<input class="form-control" type='text' name='utx' id='utx' value='<?php echo $_POST['utx']?>'>
			<div class="input-group-append">
				<input class="btn btn-success" type="button" id="form2_tabitem3_load" value="Load" onclick="
				var self = $(this);
				self.val('......'); 
				
				var form = self.closest('form');
				var allInputDivs = form.find('div[id^=row_input_]');
				var allInputs = $( ':input',allInputDivs );
				allInputs.val('');
				allInputDivs.hide();
				$('select#no_of_inputs',form).empty();
				$('span#total_inputs',form).html('0');
				$.ajax({
					url: '?ajax=1&tab=form2_tabitem3&utx=' + $('input#utx',form).val(), 
					success:function(result){
						try {
							j = eval('(' + result + ')');
							
							if ('error' in j && j.error.length>0) {
								var error = true;
							} else {
								var error = false;
							}
							
							if (!error) {
								var txHash = j.txHash;
								var outputs = j.outputs;
								var totalInputs = 0;
								
								if (outputs.length > 0) {
									$('select#no_of_inputs',form).prepend('<option value=\''+outputs.length+'\' selected=\'selected\'>'+outputs.length+'</option>');

									var x;	
									for (x in outputs) {
										var divid = parseInt(x) + 1;
										var amount = parseInt(outputs[x].amount);
										totalInputs += amount;
										
										$('div#row_input_' + divid             ,form).show();
										$('input[name=utxo_hash_' + divid+']'  ,form).val(txHash);
										$('input[name=utxo_n_' + divid+']'     ,form).val(outputs[x].n);
										$('input[name=utxo_script_' + divid+']',form).val(outputs[x].scriptPubKey);
										$('input[name=utxo_amount_' + divid+']',form).val(amount);
										$('input[name=privkey_'+divid+']'      ,form).removeAttr('readonly');
									}
								}
								
								$('span#total_inputs',form).html(totalInputs);
							} else {
								alert(j.error);
							}
						} catch(e) {
							alert('Invalid Json Format.');
						}
					},
					complete:function() {
						self.val('Load');
					}
				});"/>
			</div>
		</div>
		<!--* This sample will extract UTX Outputs from provided UTX Hex-->
	</div>
	<div class="row">
		<div class="col-sm-6">
			
			<div class="form-row">
				<div class="form-group col">
					<label for="no_of_inputs">Inputs: <i>Max 10 inputs only. Please load them from UTX above.</i></label> 
					
					<div class="row">
						<div class="col-sm-2">
							<select class="form-control" id="no_of_inputs" name='no_of_inputs' style='width:auto;' onchange="
							var self = $(this);
							var thisvalue = self.val();
							var form = self.closest('form');
							$('div[id^=row_input_]',form).hide();
							for(var i=1; i<= thisvalue; i++) { 
								$('div[id=row_input_'+  i + ']',form).show();
							}
							" readonly>
								<?php
								$optionval = $_POST['no_of_inputs']?$_POST['no_of_inputs'] : 0;
								
								if ($optionval > 0) {
								?>
								<option value='<?php echo $optionval?>'><?php echo $optionval?></option>
								<?php
								}
								?>
							</select>
						</div>
						
						<div class="col-sm-10">
							Total Sathoshi: 
							<span id='total_inputs'>
							<?php echo 
							array_reduce(
								array_keys($_POST),
								function($carry, $key) { 
									if (preg_match('/^utxo_amount_/', $key)) {
										return $carry + (int)$_POST[$key];
									} else {					
										return $carry;
									}
								}, 0
							);?>
							</span>
						</div>
					</div>
				</div>
			</div>
			
			<?php
			$selectedNInputs = is_numeric($_POST['no_of_inputs']) ? $_POST['no_of_inputs'] : 0;
			
			foreach(range(1,$noOfInputs) as $thisInput) {
			?>
			
				<div class="form-row" id='row_input_<?php echo $thisInput?>' style="<?php echo ($thisInput > $selectedNInputs) ? "display:none" : "display:;"?>">
					<input type='hidden' name='utxo_amount_<?php echo $thisInput?>' value='<?php echo $_POST["utxo_amount_{$thisInput}"]?>'/>
					
					<div class="form-group  col-sm-1">
						#<?php echo $thisInput?> 
					</div>
					<div class="form-group  col-sm-3">	
						<input class="form-control" title="UTXO Tx Hash" placeholder='UTXO Tx Hash' type='text' name='utxo_hash_<?php echo $thisInput?>' value='<?php echo $_POST["utxo_hash_{$thisInput}"]?>' readonly>
					</div>
					<div class="form-group  col-sm-1">
						<input class="form-control" title="UTXO N Output" placeholder='N' type='text' name='utxo_n_<?php echo $thisInput?>' value='<?php echo $_POST["utxo_n_{$thisInput}"]?>' readonly>
					</div>
					<div class="form-group  col-sm-3">
						<input class="form-control" title="UTXO ScriptPubKey" placeholder='UTXO ScriptPubKey' type='text' name='utxo_script_<?php echo $thisInput?>' value='<?php echo $_POST["utxo_script_{$thisInput}"]?>' readonly>
					</div>
					
					<div class="form-group  col-sm-4">
						<input class="form-control" title="Input signature to unlock UTXO." placeholder='ScriptSig' type='text' name='script_sig_<?php echo $thisInput?>' value='<?php echo $_POST["script_sig_{$thisInput}"]?>'>
					</div>
				</div>
			<?php
			}
			?>
		</div>
		<div class="col-sm-6">
			<div class="form-row">
				<div class="form-group col">
					<label for="no_of_outputs">Outputs:</label> <select class="form-control" id="no_of_outputs" name='no_of_outputs' style='width:auto;' onchange="
						var self = $(this);
						var thisvalue = self.val();
						var form = self.closest('form');
						$('div[id^=row_output_]',form).hide();
						for(var i=1; i<= thisvalue; i++) { 
							$('div[id=row_output_'+  i + ']',form).show();
						}">
						<?php
						foreach(range(1,$noOfOutputs) as $thisOutput) {
							echo "<option value='{$thisOutput}'".($thisOutput == $_POST['no_of_outputs'] ? " selected": "").">{$thisOutput}</option>";
						}
						?>
					</select>
				</div>
			</div>
			<?php
			$selectedNOutputs = is_numeric($_POST['no_of_outputs']) ? $_POST['no_of_outputs'] : 1;
			
			
			foreach(range(1,$noOfOutputs) as $thisOutput) {
			?>
				<div class="form-row" id='row_output_<?php echo $thisOutput?>' style="<?php echo ($thisOutput > $selectedNOutputs) ? "display:none" : "display:;"?>">
					<div class="form-group col-sm-1">
						#<?php echo $thisOutput?> 
					</div>
					
					<div class="form-group col-sm-6">
						<input class="form-control" placeholder='Any Address' type='text' name='address_<?php echo $thisOutput?>' value='<?php echo $_POST["address_{$thisOutput}"]?>'>
					</div>
					<div class="form-group col-sm-5">
						<input class="form-control" placeholder='Amount' type='text' name='amount_<?php echo $thisOutput?>' value='<?php echo $_POST["amount_{$thisOutput}"]?>'>
					</div>
				</div>
	<?php
			}
	?>
		</div>
	</div>
	
	<input type='submit' class="btn btn-success btn-block"/>
</form>

<small>
Please be minded that for ease of learning. We have put a little restriction to both inputs and outputs. In reality,  inputs and outputs in bitcoin & its derived coins support many of different tx type.
</small>
<?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.