Bitcoin MULTISIG


In reality, some organization may require more than 1 director's cheque signature in order to release the fund. Same thing accomplish this in bitcoin is using MULTISIG mechanism.
P2SH.Multisig (Multisig wrapped in P2SH)
  • Most common way to use multisig.
  • Described in BIP67.
  • Exist after P2MS.
  • Use of P2SH address implies that spender no longer bear for high tx fees as compare to P2MS below.
P2MS
  • Stands for "Pay To Multi Signature"
  • M-of-N Standard Transactions described in BIP11.
  • Not common as P2MS has no address format and limited to 3 public keys.
  • In this tutorial, you are able to fund into P2MS's ScriptPubKey for better understanding to bitcoin script.
Script Pair of P2SH.Multisig
ScriptPubKey: OP_HASH160 <redeemScriptHash> OP_EQUAL
ScriptSig: OP_0 < ... > OP_PUSHDATA1 <redeemScript>
Redeem Script : < ... > OP_CHECKMULTISIG
Script Pair of P2MS
ScriptSig:
ScriptPubKey :

Multisig Address

<?php 
use BitWasp\Bitcoin\Bitcoin;
use BitWasp\Bitcoin\Network\NetworkFactory;
use BitWasp\Buffertools\Buffer;
use BitWasp\Bitcoin\Key\Factory\PublicKeyFactory;
use BitWasp\Bitcoin\Script\ScriptFactory;
use BitWasp\Bitcoin\Script\P2shScript;
use BitWasp\Bitcoin\Script\Opcodes;

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

$M_N_Range = range(1,15);

include_once("html_iframe_header.php");

if ($_SERVER['REQUEST_METHOD'] == 'POST') {

	try {
		
		$networkClass   = $_POST['network'];
		Bitcoin::setNetwork(NetworkFactory::$networkClass());
		$network        = Bitcoin::getNetwork();
		$pubKeys = [];
		
		$publicKeyFactory = new PublicKeyFactory();
		if (!in_array($_POST['reqsig'], $M_N_Range)) {
			throw new Exception("'Required Signature' value is not valid.");
		} else {
			foreach($_POST['pubkey'] as $pubkey_hex) {
				if (strlen($pubkey_hex) > 0 AND !ctype_xdigit($pubkey_hex)) {
					throw new Exception("Public key must be hex.");
				}
				
				if (strlen($pubkey_hex) > 0 AND ctype_xdigit($pubkey_hex)) {
					$validPubKeys[] = $publicKeyFactory->fromHex($pubkey_hex);;
				}
			}
		}
		
		if ($_POST['reqsig'] > count($validPubKeys)) {
			throw new Exception("Required signature value should not exceed number of public key.");
		}  
		
		// make a n-of-m multisignature script
		$multisig = ScriptFactory::scriptPubKey()->multisig($_POST['reqsig'], $validPubKeys, $sort = false);
		
		// use the P2shScript 'decorator' to 'extend' script with extra functions relevant to a P2SH script
		$redeemScript = new P2shScript($multisig);
		
		$scriptPubKey = $redeemScript->getOutputScript();
		
		$opcodes = $scriptPubKey->getOpcodes();
	
	?>
		<div class="table-responsive">
			<h6 class="mt-3">P2SH.Multisig</h6>
			<table border=0 class='table'>
				<tr style='background-color:#f0f0f0'><td>Base58 address</td><td><?php echo $redeemScript->getAddress()->getAddress();?></td></tr>
				<tr><td>Redeem Script Hex </td><td><?php echo $redeemScript->getHex();?></td></tr>
				<tr><td>Redeem Script Asm</td>
					<td>
						<?php 
						foreach( $redeemScript->getScriptParser()->decode() as $operation ) {
							if ($operation->isPush()) {
								echo htmlentities("<{$operation->getData()->getHex()}> ");
							} else {
								echo $opcodes->getOp($operation->getOp()) . " " ;
							}
						}
						?>
					</td>
				</tr>
				
				<tr><td>Redeem Script Hash Hex</td><td><?php echo $redeemScript->getScriptHash()->getHex();?></td></tr>
				
				<tr style='background-color:#f0f0f0'><td>ScriptPubKey Hex </td><td><?php echo $scriptPubKey->getHex()?></td></tr>
				<tr style='background-color:#f0f0f0'><td>ScriptPubKey Asm</td>
					<td>
						<?php 
						foreach( $scriptPubKey->getScriptParser()->decode() as $operation ) {
							if ($operation->isPush()) {								
								echo htmlentities("<{$operation->getData()->getHex()}> ");
							} else {
								echo $opcodes->getOp($operation->getOp()) . " " ;
							}
						}
						?>
					</td>
				</tr>
			</table>
			
			<?php
			if (@count($validPubKeys) <= 3) {
			?>
				<h6 class="mt-3">P2MS</h6>
				<table border=0 class='table'>
					<tr><td>ScriptPubKey Hex </td><td><?php echo $redeemScript->getHex();?></td></tr>
					
					<tr><td>ScriptPubKey Asm</td>
						<td>
							<?php 
							foreach( $redeemScript->getScriptParser()->decode() as $operation ) {
								if ($operation->isPush()) {
									
									echo htmlentities("<{$operation->getData()->getHex()}> ");
								} else {
									echo $opcodes->getOp($operation->getOp()) . " " ;
								}
							}
							?>
						</td>
					</tr>
				</table>
			<?php
			}
			?>
		</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="pubkey">Public Key Hex:</label>
		
		<div class="input-group mb-3">
			<input class="form-control" type='text' name='pubkey[]' id='pubkey' value='<?php echo $_POST['pubkey'][0]?>'>
			<div class="input-group-append">
				<input class="btn btn-success" type="button" value="+" onclick="$('#multisig-pubkey-holder').find('div').first().clone().appendTo('#multisig-pubkey-holder')"/>
			</div>
		</div>
	</div>
	<?php
	$displayPublickey = count($_POST['pubkey'])-1;
	$displayPublickey = max(1, $displayPublickey);
	?>
	<div id="multisig-pubkey-holder" class="form-group">
		
		<?php
		foreach(range(1, $displayPublickey) as $n) {
		?>
		<div class="input-group mb-3">
			<input class="form-control" type='text' name='pubkey[]' value='<?php echo $_POST['pubkey'][$n]?>'>
			<div class="input-group-append">
				<input class="btn btn-success" type="button" value=" - " onclick="
					var length = $(this).closest('#multisig-pubkey-holder').find('input[value=\' - \']').length;
					if (length > 1) {
						$(this).parent('div').parent('div').remove();
					} else {
						alert('At least 2 public keys are required');
					}
				"/>
			</div>
		</div>
		<?Php
		}
		?>
		
	</div>
	
	<div class="form-group">
		<label for="reqsig">Required Signature To Spend:</label>
		<select id="reqsig" name="reqsig" class="form-control" >
			<?php
			foreach($M_N_Range as $k) {
				echo "<option value='{$k}'".($k == $_POST['reqsig'] ? " selected": "").">{$k}</option>";
			}
			?>
		</select>
	</div>
	
	
	<input type='submit' class="btn btn-success btn-block"/>
</form>
<?php
include_once("html_iframe_footer.php");		

Fund & Spend Multisig

<?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\Transaction\Factory\TxBuilder;
use BitWasp\Bitcoin\Transaction\TransactionFactory;
use BitWasp\Bitcoin\Transaction\TransactionOutput;
use BitWasp\Bitcoin\Transaction\Factory\Signer;
use BitWasp\Bitcoin\Script\ScriptType;
use BitWasp\Bitcoin\Script\ScriptFactory;
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;

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

$noOfInputs = 10;
$noOfOutputs = 1;

include_once("html_iframe_header.php");

if ($_GET['tab'] == 'form2_tabitem1' AND $_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}"]);
			
			if (!strlen($address) or !strlen($amount)) {
				throw new Exception("Error in 'output#{$thisOutput}'.");
			}
			
			if (ctype_xdigit($address)) {//hex
				
				$recipient = ScriptFactory::fromHex($address);
				
				$decodeScript = (new OutputClassifier())->decode($recipient);
				
				if ($decodeScript->getType() != ScriptType::MULTISIG) {
					throw new Exception("Invalid P2MS output in 'output#{$thisOutput}' (Check scriptPubKey).");
				}
				
				$spendTx = $spendTx->output($amount, $recipient);
				
				
			} else {
				
				
				$recipient = $addrCreator->fromString($address);
				
				$decodeScript = (new OutputClassifier())->decode($recipient->getScriptPubKey());
				
				if ($decodeScript->getType() != ScriptType::P2SH) {
					throw new Exception("Invalid P2SH addresss 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="5" id="comment" 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='?tab=form2_tabitem1#hashtag2' 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" style="width:auto;" id="no_of_inputs" name='no_of_inputs' 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
			$selected_n_inputs = 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 > $selected_n_inputs) ? "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 $_GET['tab'] == 'form2_tabitem1' ?$_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
			$selected_n_outputs = 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 > $selected_n_outputs) ? "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" title='P2SH.Multisig Address / P2MS ScriptPubKey' placeholder='P2SH.Multisig Address / P2MS ScriptPubKey' 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\Key\Factory\PrivateKeyFactory;
use BitWasp\Bitcoin\Address\AddressCreator;
use BitWasp\Bitcoin\Address\ScriptHashAddress;
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\Transaction\Factory\SignData;
use BitWasp\Bitcoin\Script\ScriptType;
use BitWasp\Bitcoin\Script\ScriptFactory;
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;
use BitWasp\Bitcoin\Script\ScriptInfo\Multisig;
use BitWasp\Bitcoin\Script\P2shScript;
    
include_once "../libraries/vendor/autoload.php";

$noOfInputs = 10;
$noOfOutputs = 1;

if ($_SERVER['REQUEST_METHOD'] == 'GET' AND $_GET['ajax'] == '1') {
	
	$data = [];
	
	if ($_GET['action'] == 'load_utx') {
		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());
						
						$reqSigs = -1;
						if ($decodeScript->getType() == ScriptType::P2SH) {
							$type = "p2sh.multisig";
						} else if ($decodeScript->getType() == ScriptType::MULTISIG) {
							$type = "p2ms";
							
							$multiSig = Multisig::fromScript($output->getScript());
							$reqSigs = $multiSig->getRequiredSigCount();
						} else {
							$data = ["error"=>"Load fail! Your unspent transaction output (utxo) can only contain P2SH or P2MS ScriptPubKey."];
							break;
						}
						
						$data['outputs'][] = ["amount"=>$output->getValue(), "n"=>$k, "scriptPubKey"=>$output->getScript()->getHex(), "type"=>$type,'reqSigs'=>$reqSigs];
					}
				}
			}
		}
	} else if ($_GET['action'] == 'load_redeemscript') {
		if (!ctype_xdigit($_GET['redeemscript'])) {
			$errmsg .= "Redeem script must be hex.";
		} else if (!ctype_xdigit($_GET['scriptpubkey'])) {
			$errmsg .= "Redeem script must be hex.";
		} else  {
			$redeemScript = ScriptFactory::fromHex($_GET['redeemscript']);
			$p2sh = new ScriptHashAddress($redeemScript->getScriptHash());
			$scriptPubKeyFromRS = $p2sh->getScriptPubKey()->getHex();
			
			if ($scriptPubKeyFromRS != $_GET['scriptpubkey']) {
				$errmsg .= "Redeem script not match to UTXO ScriptPubKey.";
			} else {
				$decodeScript = (new OutputClassifier())->decode($redeemScript);
			
				if ($decodeScript->getType() != ScriptType::MULTISIG) {
					$errmsg .= "Redeem script is not MULTISIG script.";
				} else {
					$multiSig = Multisig::fromScript($redeemScript);
					$reqSigs = $multiSig->getRequiredSigCount();
					$data = ['reqSigs' => $reqSigs];
				}
			}
		}
		
		if ($errmsg) {
			$data = ["error"=>$errmsg];
		} 
			
	}
	
	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();
		$privKeyFactory = new PrivateKeyFactory();
		
		$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) {
			$spendTx = $spendTx->spendOutputFrom($utx, $k);
			$privkeyhexCount = count($_POST["privkey_" . ($k+1)]);
			$utxo_script = $utxo->getScript()->getHex();
			$redeemScript = $_POST["redeemscript_" . ($k+1)];
			
			
			if ($privkeyhexCount > 0) {
				if (strlen($redeemScript) > 0) { //p2sh.multisig
				
					$redeemScript = new P2shScript(ScriptFactory::fromHex($redeemScript));
					$signData = new SignData();
					$signData->p2sh($redeemScript);
					for($i=0; $i<$privkeyhexCount; $i++) {
						$signItems[] = [ $_POST["privkey_" . ($k+1)][$i], $utxo_script, $k, $signData];
					}
				} else {
					for($i=0; $i<$privkeyhexCount; $i++) {
						$signItems[] = [ $_POST["privkey_" . ($k+1)][$i], $utxo_script, $k];
					}
				}
			}
		}
		
		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}'.");
			}
			
		}
		$thisTx = $spendTx->get();
		$signer = new Signer($thisTx, $ecAdapter);
		foreach($signItems as $nIn=>$signItem) {
			$privateKey = $privKeyFactory->fromHexCompressed($signItem[0]);
			$scriptPubKey = ScriptFactory::fromHex($signItem[1]);
			$nIn = $signItem[2];
			$signData = $signItem[3];
			
			$txOutput = new TransactionOutput(0, $scriptPubKey );
			
			$signer = $signer->sign($nIn, $privateKey, $txOutput,$signData);
		}
		
		
	?>
		<div class="alert alert-success">
			<h6 class="mt-3">Final TX Hex</h6>
			
			<textarea class="form-control" rows="5" id="comment" 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="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" 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&action=load_utx&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);
										var reqSigs = parseInt(outputs[x].reqSigs);
										var type = outputs[x].type;
										
										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);
										
										var privKeySelector = $('input[name=\'privkey_'+divid+'[]\']'      ,form);
										var firstPrivKeyInput = privKeySelector.filter(':first');
										privKeySelector.not(':first').remove();
										
										if (type == 'p2ms') {
											firstPrivKeyInput.removeAttr('readonly');    
											reqSigs--;
											
											if (reqSigs > 0) {
												for(var i=0; i<reqSigs;i++) {
													firstPrivKeyInput.closest('div').append(firstPrivKeyInput.clone());
												}
											}
											
											$('input[name=redeemscript_' + divid+']',form).parent('div').parent('div').hide();
										} else {
											$('input[name=redeemscript_' + divid+']',form).parent('div').parent('div').show();
											
											firstPrivKeyInput.attr('readonly','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
			$selected_n_inputs = 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 > $selected_n_inputs) ? "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">
						
						<?php
						
						$count = count($_POST["privkey_{$thisInput}"]);
						$count = $count > 0 ? $count : 1;
						
						foreach(range(0, $count-1) as $arrayIdx) {
						?>
						<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}"][$arrayIdx]?>'>
						<?php
						}
						?>
					</div>
					
					<div class="form-group  col-sm-1" >
					
					</div>
					<div class="form-group  col-sm-11">
						<div class="input-group mb-3">
							<input class="form-control" title="Enter Redeem Script to reveal number of required signature." placeholder='Enter Redeem Script to reveal number of required signature.' type='text' name='redeemscript_<?php echo $thisInput?>' value='<?php echo $_POST["redeemscript_{$thisInput}"]?>'/>
							<div class="input-group-append">
								<input class="btn btn-success" type="button" value="" onclick="
									var self = $(this);
									self.val('......'); 
									
									var form = self.closest('form');
									var formrow = self.closest('.form-row');
									
									var scriptpubkey = $('input[name^=utxo_script_]', formrow).val();
									var redeemscript = $('input[name^=redeemscript_]', formrow).val();
									
									$.ajax({
										url: '?ajax=1&action=load_redeemscript&scriptpubkey='+ scriptpubkey+ '&redeemscript=' + redeemscript, 
										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 reqSigs = parseInt(j.reqSigs);
													
													var privKeySelector = $('input[name^=\'privkey_\']',formrow);
													var firstPrivKeyInput = privKeySelector.filter(':first');
													
													firstPrivKeyInput.removeAttr('readonly');
													privKeySelector.not(':first').remove();
													reqSigs--;
													if (reqSigs > 0) {
														for(var i=0; i<reqSigs;i++) {
															firstPrivKeyInput.closest('div').append(firstPrivKeyInput.clone());
														}
													}
											
													
												} else {
													alert(j.error);
												}
											} catch(e) {
												alert('Invalid Json Format.');
											}
										},
										complete:function() {
											self.val('Reveal');
										}
									});
								"/>
							</div>
						</div>
					</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
			$selected_n_outputs = 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 > $selected_n_outputs) ? "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>
<?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.