2024-11-21 01:24:10 -05:00
<!DOCTYPE html>
< html >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, user-scalable=no" >
< script src = "https://unpkg.com/@dashincubator/ripemd160/ripemd160.js" ></ script >
< script src = "https://unpkg.com/@cmdcode/tapscript@1.4.0" ></ script >
< script src = "https://bundle.run/noble-secp256k1@1.2.14" ></ script >
< style >
* {
box-sizing : border-box ;
font-size : 1.15 rem ;
font-family : Arial , sans-serif ;
}
body {
margin : 0 ;
}
. inner_html {
max-width : 800 px ;
padding : 3 rem 1 rem ;
margin : auto ;
line-height : 1.25 ;
padding : 0 ;
}
. inner_body {
margin : 3 rem 1 rem ;
word-wrap : break-word ;
}
h1 {
font-size : 2 rem ;
}
h2 , h2 * {
font-size : 1.5 rem ;
}
input , textarea {
line-height : 1.25 ;
width : 100 % ;
height : 1.8 rem ;
font-size : 1.15 rem ;
border : 1 px solid grey ;
}
textarea {
height : 10 rem ;
}
. hidden {
display : none !important ;
}
. list_container {
overflow-y : scroll ;
}
. no_blocks_msg {
height : 248 px ;
line-height : 248 px ;
text-align : center ;
}
. list_of_blocks {
display : flex ;
justify-content : center ;
}
. block {
display : inline-block ;
height : 11.5 rem ;
width : 192 px ;
margin : 1 rem ;
cursor : pointer ;
overflow : hidden ;
flex-shrink : 0 ;
}
. block : hover {
background-color : pink ;
}
. block_inner {
display : inline-block ;
position : relative ;
height : 9.5 rem ;
width : 7.5 rem ;
margin : 1.5 rem ;
}
. block_back {
border : 1 px solid black ;
height : 6 rem ;
width : 6 rem ;
float : right ;
display : inline ;
background-color : #232838 ;
border-left : 0 ;
border-bottom : 0 ;
}
. block_front {
border : 1 px solid black ;
height : 6 rem ;
width : 6 rem ;
display : flex ;
align-items : center ;
position : absolute ;
bottom : 2 rem ;
background-color : #3953c6 ;
color : white ;
text-align : center ;
}
. block_front div {
width : 100 % ;
}
. block_num {
position : absolute ;
bottom : 0 rem ;
width : 80 % ;
text-align : center ;
}
. wallet {
border : 1 px solid black ;
border-radius : 1 rem ;
padding : 1 rem ;
text-align : center ;
margin-top : 1 rem ;
}
. auto_generate_label {
display : inline-block ;
}
. wallet_btns , . broadcast_btn , . auto_generate_label ,
. auto_go , . broadcast_success_label {
margin-top : 1 rem ;
}
. block_info , . tx_info_label , . addy_info_label {
display : flex ;
justify-content : space-between ;
}
. block_explorer , . tx , . input , . output ,
. tx_explorer , . addy_explorer {
border : 1 px solid black ;
border-radius : 1 rem ;
padding : 1 rem ;
margin-bottom : 1 rem ;
}
. close_explorer {
cursor : pointer ;
}
. explorer_block_num , . explorer_txid ,
. explorer_addy , . close_explorer {
padding : 1 rem ;
}
. explorer_block_num , . explorer_addy ,
. explorer_txid {
padding-left : 0 ;
}
. txid_link , . block_link , . addy_link {
color : blue ;
text-decoration : underline ;
cursor : pointer ;
}
@ media screen and ( max-width : 600px ) {
}
</ style >
< script >
var $ = document . querySelector . bind ( document );
var $$ = document . querySelectorAll . bind ( document );
var url_params = new URLSearchParams ( window . location . search );
var url_keys = url_params . keys ();
var $_GET = {}
for ( var key of url_keys ) $_GET [ key ] = url_params . get ( key );
</ script >
</ head >
< body >
< div class = "list_container" >
< div class = "no_blocks_msg" > Generate a block!</ div >
< div class = "list_of_blocks" ></ div >
</ div >
< div class = "inner_html" >
< div class = "inner_body" >
< h1 > Testnet generator</ h1 >
< div class = "block_explorer hidden" >
< h2 class = "block_info" >< span class = "explorer_block_num" ></ span >< span class = "close_explorer" > × </ span ></ h2 >
< p > Transaction count: < span class = "tx_count" > 0</ span ></ p >
< p > Timestamp: < span class = "block_timestamp" > None</ span ></ p >
< p > Subsidy: < span class = "block_subsidy" > 50 btc</ span ></ p >
< p > Fees: < span class = "block_fees" > 0 btc</ span ></ p >
< p style = "font-weight: bold;" > Transactions</ p >
< div class = "txs" ></ div >
</ div >
< div class = "tx_explorer hidden" >
< h2 class = "tx_info_label" >< span class = "explorer_txid" ></ span >< span class = "close_explorer" > × </ span ></ h2 >
< p > Number of inputs: < span class = "inputs_count" > 0</ span ></ p >
< p > Number of outputs: < span class = "outputs_count" > 0</ span ></ p >
< p > Mined in block: < span class = "mined_in_block" > None</ span ></ p >
< p > Timestamp: < span class = "tx_timestamp" > None</ span ></ p >
< p > Tx size: < span class = "tx_size" > 0 bytes</ span ></ p >
< p > Tx fee: < span class = "tx_fee" > 0 btc</ span ></ p >
< p > Feerate: < span class = "tx_feerate" > 0 sats per byte</ span ></ p >
< div class = "tx_info" ></ div >
</ div >
< div class = "addy_explorer hidden" >
< h2 class = "addy_info_label" >< span class = "explorer_addy" ></ span >< span class = "close_explorer" > × </ span ></ h2 >
< p class = "full_address" ></ p >
< p > Times this address received money: < span class = "received_count" > 0</ span ></ p >
< p > Times this address sent money: < span class = "sent_count" > 0</ span ></ p >
< p > Total amount received: < span class = "total_received" > 0</ span ></ p >
< p > Total amount sent: < span class = "total_sent" > 0</ span ></ p >
< p > Address balance: < span class = "addy_balance" > 0</ span ></ p >
< div class = "addy_tx_info" ></ div >
</ div >
< div class = "stuff_thats_not_an_explorer" >
< div >< button class = "generate_block" > Generate block</ button ></ div >
< div class = "auto_gen" >
< span class = "auto_generate_label" > Auto generator: </ span >
< select class = "auto_generate" >
< option > --- Auto generate ---</ option >
< option > 1/2 second</ option >
< option > 1 second</ option >
< option > 3 seconds</ option >
< option > 5 seconds</ option >
< option > 10 seconds</ option >
< option > 30 seconds</ option >
< option > 1 minute</ option >
< option > 2 minutes</ option >
< option > 5 minutes</ option >
< option > 10 minutes</ option >
</ select >
< button class = "auto_go" disabled > Go</ button >
</ div >
< div class = "wallet" >
< h2 > Your wallet</ h2 >
< p > Your address:</ p >
< p class = "your_addy" ></ p >
< div class = "balance_div" > Balance: < span class = "balance" > 0</ span > btc</ div >
< div class = "wallet_btns" >< button class = "send_btn" > Send</ button ></ div >
</ div >
< h2 class = "txs_in_mempool_label" > Transactions in mempool: < span class = "txs_in_mempool" > 0</ span ></ h2 >
< p > Enter a transaction you want to broadcast</ p >
< textarea class = "tx_broadcaster" ></ textarea >
< div class = "broadcast_btn" >< button > Broadcast</ button ></ div >
< div class = "broadcast_success_label hidden" > Success: < span class = "broadcast_success_txid txid_link" ></ span ></ div >
</ div >
</ div >
</ div >
< script >
var testnet_generator = {
blocks: {},
utxo_set: {},
mempool: [],
utxos: {},
addresses: {},
hexToBytes: hex => Uint8Array.from( hex.match( /.{1,2}/g ).map( byte => parseInt( byte, 16 ) ) ),
bytesToHex: bytes => bytes.reduce( ( str, byte ) => str + byte.toString( 16 ).padStart( 2, "0" ), "" ),
getPrivkey: () => testnet_generator.bytesToHex( nobleSecp256k1.utils.randomPrivateKey() ),
getPubkey: privkey => nobleSecp256k1.getPublicKey( privkey, true ).substring( 2 ),
getAddy: () => {
var privkey = testnet_generator.getPrivkey();
var pubkey = testnet_generator.getPubkey( privkey );
var addy = tapscript.Address.p2tr.fromPubKey( pubkey, "testnet" );
return [ addy, privkey ];
},
intToHex: int => {
var hex = BigInt( int ).toString( 16 );
if ( hex.length % 2 ) hex = "0" + hex;
return hex;
},
reverseHexString: s => s.match(/[a-fA-F0-9]{2}/g).reverse().join(''),
littleEndianSignedHexToDecimal: hex => {
var rev = testnet_generator.reverseHexString( hex );
var first_byte = rev.substring( 0, 2 );
first_byte_as_decimal = parseInt( first_byte, 16 );
if ( first_byte_as_decimal < 128 ) {
return parseInt ( rev , 16 );
} else {
// get the number of bytes in the hex string
var num_of_bytes_in_hex_string = rev.length / 2 ;
// get the corresponding max X-byte number
var maxnum = ( 2 ** ( num_of_bytes_in_hex_string * 8 ) );
// halve it
var maxnum_halved = maxnum / 2 ;
// subtract the hex number from that
return maxnum_halved - parseInt ( rev , 16 );
}
},
2024-11-21 12:24:13 -05:00
hexToBinary: hex = > {
var array_hex = hex.match( /\ w { 2 }/ g );
var array_bin = [];
array_hex . forEach ( item = > array_bin.push( ( parseInt ( item , 16 ). toString ( 2 ) ). padStart ( 8 , ' 0 ' ) ) );
return array_bin . join ( "" );
},
2024-11-21 01:24:10 -05:00
isValidHex: hex = > {
if ( ! hex ) return ;
var length = hex.length;
if ( length % 2 ) return ;
try {
var bigint = BigInt( " 0x " + hex , " hex " );
} catch ( e ) {
return ;
}
var prepad = bigint.toString( 16 );
var i ; for ( i = 0; i < length ; i ++ ) prepad = "0" + prepad ;
var padding = prepad.slice( -Math . abs ( length ) );
return ( padding = == hex );
},
isZeroNegative: zero = > {
var isZero = zero === 0 ;
var isNegative = 1 / zero = == -Infinity ;
return isNegative && isZero ;
},
waitSomeTime: num = > new Promise ( resolve = > setTimeout( resolve , num ) ),
sha256: s = > {
if ( typeof s = = " string " ) s = new TextEncoder (). encode ( s );
return crypto . subtle . digest ( ' SHA-256 ', s ). then ( hashBuffer = > {
var hashArray = Array.from( new Uint8Array ( hashBuffer ) );
var hashHex = hashArray
. map ( bytes = > bytes.toString( 16 ). padStart ( 2 , ' 0 ' ) )
. join ( '' );
return hashHex ;
});
},
sha1: s = > {
if ( typeof s = = " string " ) s = new TextEncoder (). encode ( s );
return crypto . subtle . digest ( ' SHA-1 ', s ). then ( hashBuffer = > {
var hashArray = Array.from( new Uint8Array ( hashBuffer ) );
var hashHex = hashArray
. map ( bytes = > bytes.toString( 16 ). padStart ( 2 , ' 0 ' ) )
. join ( '' );
return hashHex ;
});
},
rmd160: s = > {
if ( typeof s = = " string " ) s = new TextEncoder (). encode ( s );
var hash = RIPEMD160.create();
hash . update ( new Uint8Array ( s ) );
return testnet_generator . bytesToHex ( hash . digest () );
},
getVin: ( txid , vout , amnt , addy ) = > ({
txid,
vout,
prevout: {
value: amnt,
scriptPubKey: tapscript.Address.toScriptPubKey( addy ),
},
}),
getVout: ( amnt, addy ) => ({
value: amnt,
scriptPubKey: tapscript.Address.toScriptPubKey( addy ),
}),
satsToBitcoin: sats => {
var btc = String( sats ).padStart( 8, "0" ).slice( 0,-8 ) + "." + String( sats ).padStart( 8, "0" ).slice( -8 );
if ( btc.endsWith( "00000" ) ) {
btc = btc.substring( 0, btc.length - 5 );
var i; for ( i=0; i< 5 ; i ++ ) {
if ( btc . endsWith ( " 0 " ) ) btc = btc.substring( 0 , btc . length - 1 );
}
if ( btc . endsWith ( "." ) ) btc = btc.substring( 0 , btc . length - 1 );
if ( ! btc ) btc = 0;
}
return btc ;
},
showTx: ( txhex , idx ) = > {
if ( idx === undefined ) idx = 1;
var txs_html = ``;
var tx = tapscript.Tx.decode( txhex );
var txid = tapscript.Tx.util.getTxid( tx );
var display_fee = !!idx ? "" : "hidden";
var tx_html = `
< div class = "tx" >
< div > Txid: < span class = "txid txid_link" data-txhex = "${txhex}" data-idx = "${idx}" > ${txid}</ span ></ div >
< div class = "${display_fee}" > Fee: < span class = "fee" > 0</ span > btc</ div >
< div class = "inputs" ></ div >
< div class = "outputs" ></ div >
</ div >
`;
var div = document.createElement( 'div' );
div.innerHTML = tx_html;
var inputs_html = ``;
var outputs_html = ``;
tx.vin.forEach( ( input, index ) => {
if ( !index ) inputs_html = `< p style = "text-align: center;" > Inputs</ p > `;
var input_id = `${input[ "txid" ]}_${input[ "vout" ]}`;
if ( !idx ) var from_addy = "";
else var from_addy = testnet_generator.utxo_set[ input_id ].addy;
if ( !idx ) var amnt_sent = "";
else var amnt_sent = testnet_generator.utxo_set[ input_id ].amnt;
var input_txid = input[ "txid" ];
var input_vout = input[ "vout" ];
var input_idx = 0;
if ( `${input[ "txid" ]}_${input[ "vout" ]}` in testnet_generator.utxo_set ) input_idx = testnet_generator.utxo_set[ `${input[ "txid" ]}_${input[ "vout" ]}` ][ "idx" ];
if ( !idx ) var input_txhex = "";
else var input_txhex = testnet_generator.utxo_set[ `${input[ "txid" ]}_${input[ "vout" ]}` ][ "txhex" ];
if ( !idx ) inputs_html = inputs_html + `< div class = "input" > Coinbase</ div > `;
else inputs_html = inputs_html + `
< div class = "input" >
< p > "From" address: < span class = "from_address addy_link" > ${from_addy}</ span ></ p >
< p > Amount sent from this address in this tx: < span class = "amnt_sent" > ${testnet_generator.satsToBitcoin( amnt_sent )} btc</ span ></ p >
< p > Tx that originally created this input: < span class = "input_txid txid_link" data-txhex = "${input_txhex}" data-idx = "${input_idx}" > ${input_txid}</ span ></ p >
< p > Vout of that tx: < span class = "input_vout" > ${input[ "vout" ]}</ span ></ p >
</ div >
`;
});
div.getElementsByClassName( "inputs" )[ 0 ].innerHTML = inputs_html;
tx.vout.forEach( ( output, index ) => {
if ( !index ) outputs_html = `< p style = "text-align: center;" > Outputs</ p > `;
var output_id = `${txid}_${index}`;
var addy = testnet_generator.utxo_set[ output_id ].addy;
var amnt = testnet_generator.utxo_set[ output_id ].amnt;
outputs_html = outputs_html + `
< div class = "output" >
< p > "To" address: < span class = "to_address addy_link" > ${addy}</ span ></ p >
< p > Amount received by this address in this tx: < span class = "amnt_received" > ${testnet_generator.satsToBitcoin( amnt )} btc</ span ></ p >
</ div >
`;
});
div.getElementsByClassName( "outputs" )[ 0 ].innerHTML = outputs_html;
var sum_of_inputs = 0;
tx.vin.forEach( input => {
if ( !idx ) return;
var input_id = `${input[ "txid" ]}_${input[ "vout" ]}`;
var amnt = testnet_generator.utxo_set[ input_id ].amnt;
sum_of_inputs = sum_of_inputs + amnt;
});
var sum_of_outputs = 0;
tx.vout.forEach( output => sum_of_outputs = sum_of_outputs + Number( output[ "value" ] ) );
if ( !idx ) sum_of_outputs = 0;
div.getElementsByClassName( "fee" )[ 0 ].innerText = testnet_generator.satsToBitcoin( sum_of_inputs - sum_of_outputs );
txs_html = txs_html + div.innerHTML;
return txs_html;
},
showSoloTx: ( txid, txhex, idx ) => {
$( '.explorer_txid' ).innerText = `Tx ${txid.substring( 0, 4 )}...${txid.substring( txid.length - 4 )}`;
$( '.tx_info' ).innerHTML = testnet_generator.showTx( txhex, idx );
$$( '.close_explorer' ).forEach( close_btn => {
close_btn.onclick = () => {
showPage( 'stuff_thats_not_an_explorer' );
}
});
var tx = tapscript.Tx.decode( txhex );
var inputs_count = tx.vin.length;
$( '.inputs_count' ).innerText = inputs_count;
var outputs_count = tx.vout.length;
$( '.outputs_count' ).innerText = outputs_count;
if ( !isNaN( testnet_generator.utxo_set[ `${txid}_0` ].block_created ) ) {
var mined_in_block = testnet_generator.utxo_set[ `${txid}_0` ].block_created;
$( '.mined_in_block' ).innerHTML = `${mined_in_block} [< span class = "block_link" data-blocknum = "${mined_in_block}" > view block</ span > ]`;
$( '.block_link' ).onclick = e => {testnet_generator.showBlockDetails( e.target.getAttribute( "data-blocknum" ) )}
var blockhash = testnet_generator.intToHex( mined_in_block ).padStart( 64, "0" );
var timestamp = testnet_generator.blocks[ blockhash ][ "timestamp" ];
$( '.tx_timestamp' ).innerText = String( new Date( timestamp * 1000 ) );
} else {
var mined_in_block = "not mined yet";
$( '.mined_in_block' ).innerHTML = `${mined_in_block}`;
}
var tx_size = txhex.length / 2;
$( '.tx_size' ).innerText = `${tx_size} bytes`;
var sum_of_inputs = 0;
tx.vin.forEach( input => {
if ( !idx ) return;
var input_id = `${input[ "txid" ]}_${input[ "vout" ]}`;
var amnt = testnet_generator.utxo_set[ input_id ].amnt;
sum_of_inputs = sum_of_inputs + amnt;
});
var sum_of_outputs = 0;
tx.vout.forEach( output => sum_of_outputs = sum_of_outputs + Number( output[ "value" ] ) );
if ( !idx ) sum_of_outputs = 0;
var tx_fee_in_sats = sum_of_inputs - sum_of_outputs;
$( '.tx_fee' ).innerText = testnet_generator.satsToBitcoin( tx_fee_in_sats );
$( '.tx_feerate' ).innerText = `${( tx_fee_in_sats / tx_size ).toFixed( 2 )} sats per byte`;
window.scrollTo( 0, 0 );
$$( '.txid_link' ).forEach( item => {
item.onclick = e => {
var txid = e.target.innerText;
var txhex = e.target.getAttribute( "data-txhex" );
var idx = Number( e.target.getAttribute( "data-idx" ) );
showPage( 'tx_explorer' );
testnet_generator.showSoloTx( txid, txhex, idx );
}
});
},
showAddyInfo: ( addy ) => {
$( '.explorer_addy' ).innerText = `${addy.substring( 0, 4 )}...${addy.substring( addy.length - 4 )}`;
$( '.full_address' ).innerText = addy;
$( '.received_count' ).innerText = testnet_generator.addresses[ addy ].received.length;
$( '.sent_count' ).innerText = testnet_generator.addresses[ addy ].sent.length;
var sent = [];
var total_sent = 0;
var total_sent_to_self = 0;
testnet_generator.addresses[ addy ].sent.forEach( input_id => {
var txhex = testnet_generator.utxo_set[ input_id.substring( 0, 65 ) + "0" ].txhex;
sent.push( [ txhex, 1, undefined ] );
var tx = tapscript.Tx.decode( txhex );
var input_i_seek = Number( input_id.substring( 65 ) );
var input = tx.vin[ input_i_seek ];
var utxo_id = `${input[ "txid" ]}_${input[ "vout" ]}`;
var recipients = [];
tx.vout.forEach( ( vout, index ) => {
var recipient = tapscript.Address.fromScriptPubKey( vout[ "scriptPubKey" ], "testnet" );
if ( recipient !== addy ) return;
total_sent_to_self = total_sent_to_self + Number( vout[ "value" ] );
});
var txinfo = testnet_generator.utxo_set[ utxo_id ];
total_sent = total_sent + txinfo.amnt;
});
$( '.total_sent' ).innerText = `${testnet_generator.satsToBitcoin( total_sent - total_sent_to_self )} btc`;
var received = [];
var total_received = 0;
testnet_generator.addresses[ addy ].received.forEach( output_id => {
var txinfo = testnet_generator.utxo_set[ output_id ];
received.push( [ txinfo.txhex, txinfo.idx, txinfo.block_created ] );
total_received = total_received + txinfo.amnt;
});
$( '.total_received' ).innerText = `${testnet_generator.satsToBitcoin( total_received - total_sent_to_self )} btc`;
$( '.addy_balance' ).innerText = `${testnet_generator.satsToBitcoin( ( total_received - total_sent_to_self ) - ( total_sent - total_sent_to_self ) )} btc`;
var txs_html = `< p style = "text-align: center; font-weight: bold;" > Transactions where this address received money</ p > `;
received.forEach( tx => {
var [ txhex, idx, blocknum ] = tx;
txs_html = txs_html + testnet_generator.showTx( txhex, idx, blocknum );
});
txs_html = txs_html + `< p style = "text-align: center; font-weight: bold;" > Transactions where this address sent money</ p > `;
sent.forEach( tx => {
var [ txhex, idx, blocknum ] = tx;
txs_html = txs_html + testnet_generator.showTx( txhex, idx, blocknum );
});
$( '.addy_tx_info' ).innerHTML = txs_html;
$$( '.close_explorer' ).forEach( close_btn => {
close_btn.onclick = () => {
showPage( 'stuff_thats_not_an_explorer' );
}
});
window.scrollTo( 0, 0 );
$$( '.txid_link' ).forEach( item => {
item.onclick = e => {
var txid = e.target.innerText;
var txhex = e.target.getAttribute( "data-txhex" );
var idx = Number( e.target.getAttribute( "data-idx" ) );
showPage( 'tx_explorer' );
testnet_generator.showSoloTx( txid, txhex, idx );
}
});
},
showBlockDetails: num => {
var blockhash = testnet_generator.intToHex( num ).padStart( 64, "0" );
var timestamp = testnet_generator.blocks[ blockhash ][ "timestamp" ];
var txs = testnet_generator.blocks[ blockhash ][ "txs" ];
showPage( 'block_explorer' );
$( '.explorer_block_num' ).innerText = `Block ${num}`;
$$( '.close_explorer' ).forEach( close_btn => {
close_btn.onclick = () => {
showPage( 'stuff_thats_not_an_explorer' );
}
});
$( '.tx_count' ).innerText = txs.length;
$( '.block_timestamp' ).innerText = String( new Date( timestamp * 1000 ) );
$( '.block_fees' ).innerText = `${testnet_generator.satsToBitcoin( testnet_generator.blocks[ blockhash ][ "fees" ] )} btc`;
var txs_html = ``;
txs.forEach( ( txhex, idx ) => {
txs_html = txs_html + testnet_generator.showTx( txhex, idx, num );
});
$( '.txs' ).innerHTML = txs_html;
$$( '.txid_link' ).forEach( item => {
item.onclick = e => {
var txid = e.target.innerText;
var txhex = e.target.getAttribute( "data-txhex" );
var idx = Number( e.target.getAttribute( "data-idx" ) );
showPage( 'tx_explorer' );
testnet_generator.showSoloTx( txid, txhex, idx, num );
}
});
$$( '.addy_link' ).forEach( item => {
item.onclick = e => {
var addy = e.target.innerText;
showPage( 'addy_explorer' );
testnet_generator.showAddyInfo( addy );
}
});
},
}
var makeBlock = () => {
var num = 0;
if ( $( '.block_num' ) ) num = Number( $$( '.block_num' )[ $$( '.block_num' ).length - 1 ].innerText ) + 1;
var generated_txid = testnet_generator.intToHex( num ).padStart( 64, "0" );
var vout = 0;
var amnt = 50 * 100_000_000;
var fees_from_mempool = 0;
testnet_generator.mempool.forEach( ( tx, idx ) => {
var sum_of_inputs = 0;
tx = tapscript.Tx.decode( tx );
var txid = tapscript.Tx.util.getTxid( tx );
tx.vin.forEach( input => {
var input_id = `${input[ "txid" ]}_${input[ "vout" ]}`;
var amnt = testnet_generator.utxo_set[ input_id ].amnt;
sum_of_inputs = sum_of_inputs + amnt;
testnet_generator.utxo_set[ input_id ].spent = true;
testnet_generator.utxo_set[ input_id ].block_spent = num;
});
var sum_of_outputs = 0;
tx.vout.forEach( ( output, index ) => {
sum_of_outputs = sum_of_outputs + Number( output[ "value" ] );
testnet_generator.utxo_set[ `${txid}_${index}` ] = {
txid,
vout: index,
amnt: Number( output[ "value" ] ),
addy: tapscript.Address.fromScriptPubKey( output[ "scriptPubKey" ], "testnet" ),
spent: false,
txhex: tapscript.Tx.encode( tx ).hex,
idx: idx + 1,
block_created: num,
block_spent: undefined,
}
});
fees_from_mempool = fees_from_mempool + sum_of_inputs - sum_of_outputs;
});
amnt = amnt + fees_from_mempool;
var privkey = $( '.your_addy' ).getAttribute( "data-privkey" );
var to_pubkey = testnet_generator.getPubkey( privkey );
var to_addy = tapscript.Address.p2tr.fromPubKey( to_pubkey, "testnet" );
var from_pubkey = "00".repeat( 32 );
var from_addy = tapscript.Address.p2tr.fromPubKey( from_pubkey, "testnet" );
var txdata = tapscript.Tx.create({
vin: [testnet_generator.getVin( generated_txid, vout, amnt, from_addy )],
vout: [testnet_generator.getVout( amnt, to_addy )],
});
var txid = tapscript.Tx.util.getTxid( txdata );
var txs = [ tapscript.Tx.encode( txdata ).hex ];
txs = [ ...txs, ...testnet_generator.mempool ];
testnet_generator.mempool = [];
testnet_generator.blocks[ generated_txid ] = {
timestamp: Math.floor( Date.now() / 1000 ),
fees: fees_from_mempool,
txs,
}
testnet_generator.utxo_set[ `${txid}_${vout}` ] = {
txid,
vout,
amnt,
addy: to_addy,
spent: false,
txhex: tapscript.Tx.encode( txdata ).hex,
idx: 0,
block_created: num,
block_spent: undefined,
}
var output_id = `${txid}_${vout}`;
if ( !( to_addy in testnet_generator.addresses ) ) testnet_generator.addresses[ to_addy ] = {
sent: [],
received: [],
}
testnet_generator.addresses[ to_addy ][ "received" ].push( output_id );
var utxo_id = testnet_generator.getPrivkey().substring( 0, 16 );
calcBalance();
showBlock( num, testnet_generator.blocks[ generated_txid ].txs.length );
calcMempool();
}
var showBlock = ( num, count ) => {
var div = document.createElement( "div" );
div.innerHTML = `
< div class = "block" >
< div class = "block_inner" >
< div class = "block_back" ></ div >
< div class = "block_front" >< div > tx count:< br > ${count}</ div ></ div >
< div class = "block_num" > ${num}</ div >
</ div >
</ div >
`;
var block = div.firstElementChild;
if ( $( '.block' ) ) {
var blockwidth = 229;
var desired_width = Math.max( ( blockwidth * ( $$( '.block' ).length + 1 ) ), window.innerWidth );
$( '.list_of_blocks' ).style.width = desired_width + "px";
$( '.list_container' ).scrollTo({
top: 0,
left: $( '.list_of_blocks' ).offsetWidth,
behavior: "smooth",
});
}
$( '.no_blocks_msg' ).classList.add( "hidden" );
block.onclick = () => {testnet_generator.showBlockDetails( num );}
$( '.list_of_blocks' ).append( block );
}
var calcBalance = () => {
var bal = 0;
var my_utxos = {}
Object.keys( testnet_generator.utxo_set ).forEach( utxo_id => {
if ( testnet_generator.utxo_set[ utxo_id ].spent ) return;
var utxo = testnet_generator.utxo_set[ utxo_id ];
if ( utxo[ "addy" ] === $( '.your_addy' ).innerText ) {
bal = bal + utxo[ "amnt" ];
my_utxos[ utxo_id ] = {
utxo_id,
txid: utxo[ "txid" ],
vout: utxo[ "vout" ],
amnt: utxo[ "amnt" ],
addy: utxo[ "addy" ],
skey: $( '.your_addy' ).getAttribute( "data-privkey" ),
}
}
});
testnet_generator.utxos = my_utxos;
$( '.balance' ).innerText = testnet_generator.satsToBitcoin( bal );
}
$( '.generate_block' ).onclick = makeBlock;
var showAddress = () => {
var [ addy, privkey ] = testnet_generator.getAddy();
$( '.your_addy' ).innerText = addy;
$( '.your_addy' ).setAttribute( "data-privkey", privkey );
}
var sendMoney = () => {
var destino = prompt( `enter the address you want to send money to` );
if ( !destino ) return;
var amnt = Number( prompt( `enter an amount of sats you want to send there` ) );
if ( !amnt ) return;
var feerate = Number( prompt( `enter the feerate you want to use` ) );
if ( !feerate ) return;
var txfee = 300 * feerate;
var input_ids = getUtxos( amnt + txfee );
if ( input_ids === "unable" ) return alert( `you don't have enough money, consider generating some blocks` );
var inputs = [];
input_ids.forEach( utxo_id => inputs.push( testnet_generator.utxos[ utxo_id ] ) );
var vin = [];
inputs.forEach( input => vin.push( testnet_generator.getVin( input[ "txid" ], input[ "vout" ], input[ "amnt" ], input[ "addy" ] ) ) );
var txdata = tapscript.Tx.create({
vin,
vout: [testnet_generator.getVout( amnt, destino )],
});
var sum_of_inputs = 0;
inputs.forEach( input => sum_of_inputs = sum_of_inputs + input[ "amnt" ] );
var change = sum_of_inputs - amnt - txfee;
var change_addy = $( '.your_addy' ).innerText;
if ( change > 330 ) txdata.vout.push( testnet_generator.getVout( change, change_addy ) );
var i; for ( i=0; i< txdata.vin.length ; i ++ ) {
var privkey = inputs[ i ][ " skey " ];
var sig = tapscript.Signer.taproot.sign( privkey , txdata , i ). hex ;
txdata . vin [ i ]. witness = [ sig ];
}
var txhex = tapscript.Tx.encode( txdata ). hex ;
broadcast ( txhex );
// highlight the mempool count label
$( '. txs_in_mempool_label ' ). style . backgroundColor = "pink" ;
setTimeout ( () = > {
$( '.txs_in_mempool_label' ).style.transition = "all 1s ease-in";
$( '.txs_in_mempool_label' ).style.backgroundColor = "white";
}, 10 );
calcMempool();
return txhex;
}
$( '.send_btn' ).onclick = sendMoney;
var getUtxos = amount => {
var utxo_set = [];
Object.keys( testnet_generator.utxos ).forEach( utxo_id => utxo_set.push( testnet_generator.utxos[ utxo_id ] ) );
var set_to_return = [];
var sum_so_far = 0;
var new_utxo_set = JSON.parse( JSON.stringify( utxo_set ) );
var unable = false;
var loop = () => {
if ( !new_utxo_set.length ) {
unable = true;
return;
}
var rand = Math.floor( Math.random() * new_utxo_set.length );
set_to_return.push( new_utxo_set[ rand ][ "utxo_id" ] );
sum_so_far = sum_so_far + new_utxo_set[ rand ][ "amnt" ];
new_utxo_set.splice( rand, 1 );
if ( sum_so_far < amount + 330 ) loop ();
}
loop();
if ( unable ) return `unable`;
return set_to_return;
}
showAddress();
var validateTx = async txhex => {
var tx = null;
try {
tx = tapscript.Tx.decode( txhex );
} catch ( e ) {}
if ( !tx || typeof tx != "object" ) return 'that tx was invalid for unknown reasons';
var input_that_isnt_real = -1;
var input_with_insufficient_witness_length = -1;
var input_with_invalid_sig = -1;
var input_whose_stack_is_too_small = -1;
var input_with_improperly_formatted_sig = -1;
var input_with_improperly_formatted_element = -1;
var input_with_improperly_formatted_element = -1;
var input_whose_script_failed_a_verify_opcode = -1;
var input_with_op_nop = -1;
var input_whose_script_left_multiple_elements_on_the_stack = -1;
var input_whose_script_ended_on_false = -1;
var input_with_if_branch_preceded_by_something_other_than_1_or_0 = -1;
var input_with_script_containing_op_return = -1;
var input_whose_script_treats_a_string_as_an_integer = -1;
var input_whose_script_tried_to_get_a_nonexistent_element_from_the_stack = -1;
var input_whose_script_failed_CLTV_due_to_not_enough_time_passed = -1;
2024-11-21 15:18:48 -05:00
var input_whose_script_uses_a_negative_number_for_CLTV_or_CSV = -1;
2024-11-21 01:24:10 -05:00
var input_whose_locktime_value_does_not_match_the_CLTV_value = -1;
var input_whose_script_uses_CLTV_but_the_sequence_number_is_maxxed_out = -1;
var input_whose_script_failed_CSV = -1;
2024-11-21 12:24:13 -05:00
var input_whose_script_failed_relative_timelock = -1;
2024-11-21 01:24:10 -05:00
var i; for ( i=0; i< tx.vin.length ; i ++ ) {
var input = tx.vin[ i ];
var index = i;
var input_id = `${input[ " txid " ]} _ ${ input [ " vout " ]}`;
var input_exists = false;
if ( input_id in testnet_generator . utxo_set ) input_exists = true;
else input_exists = false;
if ( testnet_generator . utxo_set [ input_id ]. spent ) input_exists = false;
if ( ! input_exists ) {
input_that_isnt_real = index;
break ;
}
var addy = testnet_generator.utxo_set[ input_id ]. addy ;
tx . vin [ i ]. prevout = {
value: testnet_generator . utxo_set [ input_id ]. amnt ,
scriptPubKey: tapscript . Address . toScriptPubKey ( addy ),
}
}
if ( input_that_isnt_real > = 0 ) return `that tx was invalid because input ${input_that_isnt_real} tries to spend a utxo that does not exist or is already spent by another transaction in the mempool`;
//note: locktime is encoded as little endian
var locktime = tx.locktime;
var blockheight = Object.keys( testnet_generator.blocks ).length - 1;
if ( locktime < 500000000 && locktime > blockheight ) return `that tx was invalid because its locktime is set to a value greater than the current blockheight -- wait longer! The current blockheight is ${blockheight} and your locktime is set to ${locktime} so you need to wait til then`;
var current_block = Object.keys( testnet_generator.blocks ).sort()[ Object.keys( testnet_generator.blocks ).sort().length - 1 ];
var current_blocktime = testnet_generator.blocks[ current_block ].timestamp;
if ( locktime >= 500000000 && locktime > current_blocktime ) return `that tx was invalid because its locktime is set to a value greater than the median timestamp of the last 11 blocks (actually in this implementation I don't do the median stuff, I just check the latest block's timestamp) -- wait longer! The current timestamp is ${current_blocktime} (which is ${String( new Date( current_blocktime * 1000 ) )}) and your locktime is set to ${locktime} (which is ${String( new Date( locktime * 1000 ) )}) so you need to wait til then`;
var i; for ( i=0; i< tx.vin.length ; i ++ ) {
var input = tx.vin[ i ];
var index = i;
var input_id = `${input[ " txid " ]} _ ${ input [ " vout " ]}`;
var addy = testnet_generator.utxo_set[ input_id ]. addy ;
var type = tapscript.Address.decode( addy ). type ;
2024-11-21 12:24:13 -05:00
var sequence_binary = testnet_generator.hexToBinary( input . sequence );
var first_bit = Number( sequence_binary [ 0 ] );
var tenth_bit = Number( sequence_binary [ 9 ] );
2024-11-21 01:24:10 -05:00
var sequence = testnet_generator.littleEndianSignedHexToDecimal( input . sequence );
2024-11-21 12:24:13 -05:00
var sequence_value = null;
if ( ! first_bit ) {
// the " disable flag " is not set so we are about to enforce the sequence number as a relative locktime
var mined_in_block = testnet_generator.utxo_set[ input_id ]. block_created ;
var current_blockheight = Object.keys( testnet_generator . blocks ). length - 1 ;
sequence_value = input.sequence.substring( 4 );
if ( sequence_value . substring ( 0 , 2 ) != " 00 " ) {
sequence_value = parseInt( sequence_value , 16 );
} else {
sequence_value = parseInt( sequence_value . substring ( 2 ), 16 );
}
if ( ! tenth_bit ) {
// the " type " bit is not set so this is a block-based relative timelock rather
// than a timestamp-based relative timelock
if ( ! mined_in_block && parseInt ( sequence_binary , 2 ) > 0 ) {
input_whose_script_failed_relative_timelock = index;
break;
} else if ( mined_in_block ) {
var num_of_blocks_since_parent = current_blockheight - mined_in_block;
2024-11-21 15:18:48 -05:00
if ( num_of_blocks_since_parent > 0 ) num_of_blocks_since_parent = num_of_blocks_since_parent + 1;
2024-11-21 12:24:13 -05:00
if ( num_of_blocks_since_parent < sequence_value ) {
input_whose_script_failed_relative_timelock = index;
break ;
}
}
} else {
// the " type " bit is set so this is a timestmap-based relative timelock rather
// than a block-based relative timelock
if ( ! mined_in_block && parseInt ( sequence_binary , 2 ) > 0 ) {
input_whose_script_failed_relative_timelock = index;
break;
} else if ( mined_in_block ) {
var id_of_parent_block = testnet_generator.intToHex( mined_in_block ).padStart( 64, "0" );
var mined_at_timestamp = testnet_generator.blocks[ id_of_parent_block ].timestamp;
var time_to_wait = sequence_value * 512;
var now = Math.floor( Date.now() / 1000 );
var time_since_parent = now - mined_at_timestamp;
if ( time_since_parent < time_to_wait ) {
input_whose_script_failed_relative_timelock = index;
break ;
}
}
}
}
2024-11-21 01:24:10 -05:00
if ( type = == " p2tr " ) {
// ensure at least one element exists on the witness stack
if ( ! input . witness . length ) {
input_with_insufficient_witness_length = index;
break ;
}
// if only one element exists on the witness stack , treat it as a sig and check that it is valid for the taproot address when treated as a pubkey
if ( input . witness . length = == 1 ) {
var sig = input.witness[ 0 ];
var sighash = tapscript.Signer.taproot.hash( tx , index );
var pubkey = tapscript.Address.decode( addy ). script [ 1 ];
var sig_is_valid = await nobleSecp256k1 . schnorr . verify ( sig , sighash , pubkey );
if ( ! sig_is_valid ) input_with_invalid_sig = index;
} else {
// if there are multiple elements on the witness stack , treat the second-to-last element as the script and execute it with the elements prior to that as params
var script = input.witness[ input . witness . length - 2 ];
var params = [];
input . witness . forEach ( ( element , element_index ) = > {
2024-12-30 01:04:29 -05:00
if ( element === "OP_0" || element === "OP_FALSE" || element === "" ) element = 0;
if ( element === "OP_1" || element === "OP_TRUE" ) element = 1;
2024-11-21 01:24:10 -05:00
if ( element_index < input.witness.length - 2 ) params . push ( element );
});
var script_as_asm = tapscript.Script.fmt.toAsm( script );
var tapleaf = tapscript.Tap.encodeScript( script_as_asm );
var ifs_stack = [true];
var alt_stack = [];
console . log ( " params: " );
console . log ( params );
console . log ( " script: " );
console . log ( script_as_asm );
var exeloop = async () = > {
var element = script_as_asm[ 0 ];
console.log( element );
if ( !ifs_stack.includes( false ) && !element.startsWith( "OP_" ) ) {
if ( !testnet_generator.isValidHex( element ) && element !== "" ) {
input_with_improperly_formatted_element = index;
return;
}
//if the element is 0, push 0
if ( element === "" ) element = 0;
//convert hex values less than 4 bytes to the corresponding number
if ( element !== 0 && element.length / 2 < 5 ) element = testnet_generator.littleEndianSignedHexToDecimal( element );
params . push ( element );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_CHECKSIG " ) {
// console . log ( " params: " );
// console . log ( JSON . parse ( JSON . stringify ( params ) ) );
// console . log ( " script: " );
// console . log ( JSON . parse ( JSON . stringify ( script_as_asm ) ) );
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var pubkey = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var sig = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
// console . log ( " pubkey: " );
// console . log ( pubkey );
// console . log ( " sig: " );
// console . log ( sig );
var sig_length_is_good = false;
if ( sig . length = == 128 || sig . length = == 130 || ! sig . length ) sig_length_is_good = true;
if ( sig . length = == 130 && sig . substring ( 128 ) != " 01 " && sig . substring ( 128 ) != " 02 " && sig . substring ( 128 ) != " 03 " && sig . substring ( 128 ) != " 81 " && sig . substring ( 128 ) != " 82 " && sig . substring ( 128 ) != " 83 " ) sig_length_is_good = false;
if ( ! sig_length_is_good || ( sig . length && ! testnet_generator . isValidHex ( sig ) ) ) input_with_improperly_formatted_sig = index;
if ( ! sig . length ) {
params . push ( 0 );
} else {
var sigflag = 1; // sighash_all
if ( sig . length = == 130 && sig . substring ( 128 ) != " 02 " ) sigflag = 2; // sighash_single
if ( sig . length = == 130 && sig . substring ( 128 ) != " 03 " ) sigflag = 3; // sighash_none
if ( sig . length = == 130 && sig . substring ( 128 ) != " 81 " ) sigflag = 128 | 1 ; // sighash_all | anyone_can_pay
if ( sig . length = == 130 && sig . substring ( 128 ) != " 82 " ) sigflag = 128 | 2 ; // sighash_single | anyone_can_pay
if ( sig . length = == 130 && sig . substring ( 128 ) != " 83 " ) sigflag = 128 | 3 ; // sighash_none | anyone_can_pay
if ( sigflag != 1 ) var sighash = tapscript.Signer.taproot.hash( tx , index , { extension: tapleaf , sigflag }). hex ;
else var sighash = tapscript.Signer.taproot.hash( tx , index , { extension: tapleaf } ). hex ;
var sig_is_valid = await nobleSecp256k1 . schnorr . verify ( sig , sighash , pubkey );
if ( sig_is_valid ) {
params . push ( 1 );
}
else {
input_with_invalid_sig = index;
return ;
}
}
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_CHECKSIGVERIFY " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var pubkey = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var sig = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
var sig_length_is_good = false;
if ( sig . length = == 128 || sig . length = == 130 || ! sig . length ) sig_length_is_good = true;
if ( sig . length = == 130 && sig . substring ( 128 ) != " 01 " && sig . substring ( 128 ) != " 02 " && sig . substring ( 128 ) != " 03 " && sig . substring ( 128 ) != " 81 " && sig . substring ( 128 ) != " 82 " && sig . substring ( 128 ) != " 83 " ) sig_length_is_good = false;
if ( ! sig_length_is_good || ( sig . length && ! testnet_generator . isValidHex ( sig ) ) ) input_with_improperly_formatted_sig = index;
if ( ! sig . length ) {
input_whose_script_failed_a_verify_opcode = index;
return ;
} else {
var sigflag = 1; // sighash_all
if ( sig . length = == 130 && sig . substring ( 128 ) != " 02 " ) sigflag = 2; // sighash_single
if ( sig . length = == 130 && sig . substring ( 128 ) != " 03 " ) sigflag = 3; // sighash_none
if ( sig . length = == 130 && sig . substring ( 128 ) != " 81 " ) sigflag = 128 | 1 ; // sighash_all | anyone_can_pay
if ( sig . length = == 130 && sig . substring ( 128 ) != " 82 " ) sigflag = 128 | 2 ; // sighash_single | anyone_can_pay
if ( sig . length = == 130 && sig . substring ( 128 ) != " 83 " ) sigflag = 128 | 3 ; // sighash_none | anyone_can_pay
if ( sigflag != 1 ) var sighash = tapscript.Signer.taproot.hash( tx , index , { extension: tapleaf , sigflag }). hex ;
else var sighash = tapscript.Signer.taproot.hash( tx , index , { extension: tapleaf } ). hex ;
var sig_is_valid = await nobleSecp256k1 . schnorr . verify ( sig , sighash , pubkey );
if ( ! sig_is_valid ) {
input_whose_script_failed_a_verify_opcode = index;
return ;
}
}
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_CHECKSIGADD " ) {
// console . log ( " params: " );
// console . log ( params );
// console . log ( " script: " );
// console . log ( script_as_asm );
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var pubkey = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var num_to_add_to = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var sig = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
var sig_length_is_good = false;
if ( sig . length = == 128 || sig . length = == 130 || ! sig . length ) sig_length_is_good = true;
if ( sig . length = == 130 && sig . substring ( 128 ) != " 01 " && sig . substring ( 128 ) != " 02 " && sig . substring ( 128 ) != " 03 " && sig . substring ( 128 ) != " 81 " && sig . substring ( 128 ) != " 82 " && sig . substring ( 128 ) != " 83 " ) sig_length_is_good = false;
if ( ! sig_length_is_good || ( sig . length && ! testnet_generator . isValidHex ( sig ) ) ) input_with_improperly_formatted_sig = index;
// console . log ( " num_to_add_to: ", num_to_add_to , " pubkey: ", pubkey , " sig: ", sig );
if ( ! sig . length ) {
params . push ( num_to_add_to );
} else {
var sigflag = 1; // sighash_all
if ( sig . length = == 130 && sig . substring ( 128 ) != " 02 " ) sigflag = 2; // sighash_single
if ( sig . length = == 130 && sig . substring ( 128 ) != " 03 " ) sigflag = 3; // sighash_none
if ( sig . length = == 130 && sig . substring ( 128 ) != " 81 " ) sigflag = 128 | 1 ; // sighash_all | anyone_can_pay
if ( sig . length = == 130 && sig . substring ( 128 ) != " 82 " ) sigflag = 128 | 2 ; // sighash_single | anyone_can_pay
if ( sig . length = == 130 && sig . substring ( 128 ) != " 83 " ) sigflag = 128 | 3 ; // sighash_none | anyone_can_pay
if ( sigflag != 1 ) var sighash = tapscript.Signer.taproot.hash( tx , index , { extension: tapleaf , sigflag }). hex ;
else var sighash = tapscript.Signer.taproot.hash( tx , index , { extension: tapleaf } ). hex ;
var sig_is_valid = await nobleSecp256k1 . schnorr . verify ( sig , sighash , pubkey );
// console . log ( " sig is valid , right ?", sig_is_valid , " pubkey: ", pubkey , " sighash: ", sighash , " sig: ", sig );
if ( sig_is_valid ) {
// console . log ( ' I will increment this: ', num_to_add_to );
num_to_add_to = num_to_add_to + 1 ;
params . push ( num_to_add_to );
// console . log ( ' done , now it is this: ', num_to_add_to );
} else {
input_with_invalid_sig = index;
return ;
}
}
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_0 " || element = == " OP_FALSE " || element = == "" ) {
params . push ( 0 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_1 " || element = == " OP_TRUE " ) {
params . push ( 1 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_2 " ) {
params . push ( 2 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_3 " ) {
params . push ( 3 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_4 " ) {
params . push ( 4 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_5 " ) {
params . push ( 5 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_6 " ) {
params . push ( 6 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_7 " ) {
params . push ( 7 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_8 " ) {
params . push ( 8 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_9 " ) {
params . push ( 9 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_10 " ) {
params . push ( 10 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_11 " ) {
params . push ( 11 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_12 " ) {
params . push ( 12 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_13 " ) {
params . push ( 13 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_14 " ) {
params . push ( 14 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_15 " ) {
params . push ( 15 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_16 " ) {
params . push ( 16 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_EQUAL " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_2 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( item_2 = == item_1 ) params . push ( 1 );
else params . push ( 0 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_EQUALVERIFY " ) {
// console . log ( " params: " );
// console . log ( params );
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_2 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( item_2 !== item_1 ) {
input_whose_script_failed_a_verify_opcode = index;
return ;
}
// console . log ( " params: " );
// console . log ( params );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_GREATERTHAN " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var num_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var num_2 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( num_2 > num_1 ) params.push( 1 );
else params.push( 0 );
}
else if ( !ifs_stack.includes( false ) && element === "OP_LESSTHAN" ) {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var num_1 = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var num_2 = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( num_2 < num_1 ) params . push ( 1 );
else params . push ( 0 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_GREATERTHANOREQUAL " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var num_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var num_2 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
// console . log ( num_2 , num_1 , " the first number should be greater than or equal to the second number " );
if ( num_2 > = num_1 ) params.push( 1 );
else params.push( 0 );
}
else if ( !ifs_stack.includes( false ) && element === "OP_LESSTHANOREQUAL" ) {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var num_1 = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var num_2 = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( num_2 < = num_1 ) params.push( 1 );
else params.push( 0 );
}
else if ( !ifs_stack.includes( false ) && element === "OP_SHA1" ) {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_to_hash = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( typeof item_to_hash === "string" ) var as_bytes = testnet_generator.hexToBytes( item_to_hash );
else {
item_to_hash = item_to_hash.toString( 16 );
if ( item_to_hash % 2 ) item_to_hash = "0" + item_to_hash;
var as_bytes = testnet_generator.hexToBytes( item_to_hash );
}
var hashed = await testnet_generator.sha1( as_bytes );
params.push( hashed );
}
else if ( !ifs_stack.includes( false ) && element === "OP_SHA256" ) {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_to_hash = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( typeof item_to_hash === "string" ) var as_bytes = testnet_generator.hexToBytes( item_to_hash );
else {
item_to_hash = item_to_hash.toString( 16 );
if ( item_to_hash % 2 ) item_to_hash = "0" + item_to_hash;
var as_bytes = testnet_generator.hexToBytes( item_to_hash );
}
var hashed = await testnet_generator.sha256( as_bytes );
params.push( hashed );
}
else if ( !ifs_stack.includes( false ) && element === "OP_HASH256" ) {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_to_hash = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( typeof item_to_hash === "string" ) var as_bytes = testnet_generator.hexToBytes( item_to_hash );
else {
item_to_hash = item_to_hash.toString( 16 );
if ( item_to_hash % 2 ) item_to_hash = "0" + item_to_hash;
var as_bytes = testnet_generator.hexToBytes( item_to_hash );
}
var hashed = await testnet_generator.sha256( as_bytes );
var as_bytes = testnet_generator.hexToBytes( hashed );
hashed = await testnet_generator.sha256( as_bytes );
params.push( hashed );
}
else if ( !ifs_stack.includes( false ) && element === "OP_RIPEMD160" ) {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_to_hash = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( typeof item_to_hash === "string" ) var as_bytes = testnet_generator.hexToBytes( item_to_hash );
else {
item_to_hash = item_to_hash.toString( 16 );
if ( item_to_hash % 2 ) item_to_hash = "0" + item_to_hash;
var as_bytes = testnet_generator.hexToBytes( item_to_hash );
}
var hashed = testnet_generator.rmd160( as_bytes );
params.push( hashed );
}
else if ( !ifs_stack.includes( false ) && element === "OP_HASH160" ) {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_to_hash = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( typeof item_to_hash === "string" ) var as_bytes = testnet_generator.hexToBytes( item_to_hash );
else {
item_to_hash = item_to_hash.toString( 16 );
if ( item_to_hash % 2 ) item_to_hash = "0" + item_to_hash;
var as_bytes = testnet_generator.hexToBytes( item_to_hash );
}
var hashed = await testnet_generator.sha256( as_bytes );
var as_bytes = testnet_generator.hexToBytes( hashed );
hashed = testnet_generator.rmd160( as_bytes );
params.push( hashed );
}
else if ( !ifs_stack.includes( false ) && element === "OP_SWAP" ) {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_1 = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_2 = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
params.push( item_1 );
params.push( item_2 );
}
else if ( !ifs_stack.includes( false ) && element === "OP_DROP" ) {
// console.log( "before acting:" );
// console.log( params );
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
params.reverse();
params.splice( 0, 1 );
params.reverse();
// console.log( "results:" );
// console.log( params );
}
else if ( !ifs_stack.includes( false ) && element === "OP_2DROP" ) {
console.log( "before acting:" );
console.log( params );
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
params.reverse();
params.splice( 0, 1 );
params.reverse();
console.log( "results:" );
console.log( params );
}
else if ( !ifs_stack.includes( false ) && element === "OP_1NEGATE" ) {
params.push( -1 );
}
else if ( !ifs_stack.includes( false ) && element === "OP_NOP" ) {
input_with_op_nop = index;
return;
}
else if ( element === "OP_IF" ) {
// console.log( "params:" );
// console.log( params );
//add a boolean to the ifs_stack that is false if the previous element
//was something other than OP_1 or OP_TRUE, or if any element in
//the ifs_stack is false (because that means we are in an if_branch
//that should not be executed) -- otherwise true
if ( ifs_stack.includes( false ) ) {
ifs_stack.push( false );
} else {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_to_check = params[ params.length - 1 ];
// console.log( "item_to_check:", item_to_check );
if ( item_to_check !== 1 && item_to_check !== 0 ) {
input_with_if_branch_preceded_by_something_other_than_1_or_0 = index;
return;
}
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( item_to_check !== 1 ) ifs_stack.push( false );
else ifs_stack.push( true );
}
// console.log( "ifs_stack:" );
// console.log( ifs_stack );
}
else if ( element === "OP_NOTIF" ) {
// console.log( "params:" );
// console.log( params );
if ( ifs_stack.includes( false ) ) {
ifs_stack.push( false );
} else {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_to_check = params[ params.length - 1 ];
// console.log( "item_to_check:", item_to_check );
if ( item_to_check !== 1 && item_to_check !== 0 ) {
input_with_if_branch_preceded_by_something_other_than_1_or_0 = index;
return;
}
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( item_to_check !== 0 ) ifs_stack.push( false );
else ifs_stack.push( true );
}
// console.log( "ifs_stack:" );
// console.log( ifs_stack );
}
else if ( element === "OP_ELSE" ) {
//flip the last element in the ifs_stack
ifs_stack[ ifs_stack.length - 1 ] = !ifs_stack[ ifs_stack.length - 1 ];
// console.log( "ifs_stack:" );
// console.log( ifs_stack );
}
else if ( element === "OP_ENDIF" ) {
// console.log( "params:" );
// console.log( params );
//drop the last element in the ifs_stack
ifs_stack.pop();
// console.log( "ifs_stack:" );
// console.log( ifs_stack );
}
else if ( !ifs_stack.includes( false ) && element === "OP_VERIFY" ) {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_to_check = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( item_to_check !== 1 ) {
input_whose_script_failed_a_verify_opcode = index;
return;
}
}
else if ( !ifs_stack.includes( false ) && element === "OP_RETURN" ) {
input_with_script_containing_op_return = index;
return;
}
else if ( !ifs_stack.includes( false ) && element === "OP_0NOTEQUAL" ) {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_to_check = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
2024-12-10 14:35:00 -05:00
if ( typeof item_to_check !== "number" ) {
input_whose_script_treats_a_string_as_an_integer = index;
return;
}
2024-11-21 01:24:10 -05:00
if ( item_to_check === 0 ) params.push( 0 );
else params.push( 1 );
}
else if ( !ifs_stack.includes( false ) && element === "OP_TOALTSTACK" ) {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_to_push = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
alt_stack.unshift( item_to_push );
}
else if ( !ifs_stack.includes( false ) && element === "OP_FROMALTSTACK" ) {
if ( !alt_stack.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_to_pull = alt_stack[ 0 ];
params.push( item_to_pull );
}
else if ( !ifs_stack.includes( false ) && element === "OP_IFDUP" ) {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_to_dup = params[ params.length - 1 ];
var copy = JSON.parse( JSON.stringify( item_to_dup ) );
if ( item_to_dup !== 0 ) params.push( copy );
}
else if ( !ifs_stack.includes( false ) && element === "OP_DEPTH" ) {
params.push( params.length );
}
else if ( !ifs_stack.includes( false ) && element === "OP_NIP" ) {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_1 = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_2 = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
params.push( item_2 );
}
else if ( !ifs_stack.includes( false ) && element === "OP_OVER" ) {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_1 = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var item_2 = params[ params.length - 1 ];
var copy = JSON.parse( JSON.stringify( item_to_dup ) );
params.reverse();
params.splice( 0, 1 );
params.reverse();
params.push( item_2 );
params.push( item_1 );
params.push( copy );
}
else if ( !ifs_stack.includes( false ) && element === "OP_PICK" ) {
if ( !params.length ) {
input_whose_stack_is_too_small = index;
return;
}
var num_to_pick = params[ params.length - 1 ];
params.reverse();
params.splice( 0, 1 );
params.reverse();
if ( typeof num_to_pick !== "number" ) {
input_whose_script_treats_a_string_as_an_integer = index;
return;
}
if ( num_to_pick > 2147483647 ) {
input_whose_script_treats_a_string_as_an_integer = index;
return;
}
if ( num_to_pick < -2147483647 ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( params . length < num_to_pick ) {
input_whose_script_tried_to_get_a_nonexistent_element_from_the_stack = index;
return ;
}
var item_to_dup = params[ params . length - ( num_to_pick + 1 ) ];
var copy = JSON.parse( JSON . stringify ( item_to_dup ) );
params . push ( copy );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_ROLL " ) {
// console . log ( " before acting: " );
// console . log ( params );
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var num_to_pick = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof num_to_pick !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( num_to_pick > 2147483647 ) {
input_whose_script_treats_a_string_as_an_integer = index;
return;
}
if ( num_to_pick < -2147483647 ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( params . length < num_to_pick ) {
input_whose_script_tried_to_get_a_nonexistent_element_from_the_stack = index;
return ;
}
var item_to_move = params[ params . length - ( num_to_pick + 1 ) ];
var copy = JSON.parse( JSON . stringify ( item_to_move ) );
// console . log ( " results: " );
// console . log ( params );
params . splice ( params . length - ( num_to_pick + 1 ), 1 );
params . push ( copy );
// console . log ( params );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_ROT " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_3 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_2 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
params . push ( item_2 );
params . push ( item_3 );
params . push ( item_1 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_TUCK " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_2 = params[ params . length - 1 ];
var copy = JSON.parse( JSON . stringify ( item_2 ) );
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
params . push ( copy );
params . push ( item_1 );
params . push ( item_2 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_DUP " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_to_dup = params[ params . length - 1 ];
var copy = JSON.parse( JSON . stringify ( item_to_dup ) );
params . push ( copy );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_2DUP " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_to_dup_1 = params[ params . length - 1 ];
var copy_1 = JSON.parse( JSON . stringify ( item_to_dup_1 ) );
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_to_dup_2 = params[ params . length - 1 ];
var copy_2 = JSON.parse( JSON . stringify ( item_to_dup_2 ) );
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
params . push ( copy_2 );
params . push ( copy_1 );
params . push ( copy_2 );
params . push ( copy_1 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_3DUP " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_to_dup_1 = params[ params . length - 1 ];
var copy_1 = JSON.parse( JSON . stringify ( item_to_dup_1 ) );
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_to_dup_2 = params[ params . length - 1 ];
var copy_2 = JSON.parse( JSON . stringify ( item_to_dup_2 ) );
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_to_dup_3 = params[ params . length - 1 ];
var copy_3 = JSON.parse( JSON . stringify ( item_to_dup_3 ) );
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
params . push ( copy_3 );
params . push ( copy_2 );
params . push ( copy_1 );
params . push ( copy_3 );
params . push ( copy_2 );
params . push ( copy_1 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_2OVER " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_4 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_3 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_2 = params[ params . length - 1 ];
var copy_2 = JSON.parse( JSON . stringify ( item_2 ) );
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_1 = params[ params . length - 1 ];
var copy_1 = JSON.parse( JSON . stringify ( item_1 ) );
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
params . push ( item_1 );
params . push ( item_2 );
params . push ( item_3 );
params . push ( item_4 );
params . push ( copy_1 );
params . push ( copy_2 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_2ROT " ) {
console . log ( " before acting: " );
console . log ( params );
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_6 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_5 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_4 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_3 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_2 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
params . push ( item_3 );
params . push ( item_4 );
params . push ( item_5 );
params . push ( item_6 );
params . push ( item_1 );
params . push ( item_2 );
console . log ( " results: " );
console . log ( params );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_2SWAP " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_4 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_3 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_2 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
params . push ( item_3 );
params . push ( item_4 );
params . push ( item_1 );
params . push ( item_2 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_SIZE " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var item = params[ params . length - 1 ];
if ( typeof item = == " number " ) {
if ( item > = -2147483647 && item < 2147483648 ) var size = 4;
if ( item > = -8388607 && item < 8388608 ) var size = 3;
if ( item > = -32767 && item < 32768 ) var size = 2;
if ( item > = -127 && item < 128 ) var size = 1;
if ( item = == 0 ) var size = 0;
} else {
var size = item.length / 2 ;
}
params . push ( size );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_1ADD " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input = params[ params . length - 1 ];
if ( typeof input !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
params . push ( input + 1 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_1SUB " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input = params[ params . length - 1 ];
if ( typeof input !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
params . push ( input - 1 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_NEGATE " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input = params[ params . length - 1 ];
if ( typeof input !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
params . push ( -input );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_ABS " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input = params[ params . length - 1 ];
if ( typeof input !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( input < 0 || ( input = == 0 && testnet_generator . isZeroNegative ( input ) ) ) params . push ( -input );
else params . push ( input );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_NOT " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input = params[ params . length - 1 ];
if ( typeof input !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( input = == 0 ) var output = 1;
else var output = 0;
params . push ( output );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_ADD " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_1 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_2 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_2 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
params . push ( input_1 + input_2 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_SUB " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_1 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_2 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_2 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
params . push ( input_1 - input_2 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_BOOLAND " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_1 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_2 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_2 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( input_1 !== 1 ) input_1 = 0;
if ( input_2 !== 1 ) input_2 = 0;
params . push ( input_1 && input_2 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_BOOLOR " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_1 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_2 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_2 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( input_1 !== 1 ) input_1 = 0;
if ( input_2 !== 1 ) input_2 = 0;
params . push ( input_1 | input_2 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_NUMEQUAL " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_1 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_2 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_2 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
params . push ( input_1 = == input_2 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_NUMEQUALVERIFY " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_1 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_2 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_2 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( input_1 !== input_2 ) {
input_whose_script_failed_a_verify_opcode = index;
return ;
}
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_NUMNOTEQUAL " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_1 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_2 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_2 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
params . push ( input_1 !== input_2 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_MIN " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_1 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_2 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_2 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
params . push ( Math . min ( input_1 , input_2 ) );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_MAX " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_1 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_1 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input_2 = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input_2 !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
params . push ( Math . max ( input_1 , input_2 ) );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_WITHIN " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof input !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var min = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof min !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var max = params[ params . length - 1 ];
params . reverse ();
params . splice ( 0 , 1 );
params . reverse ();
if ( typeof max !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( input > = min && input < max ) params . push ( 1 );
else params . push ( 0 );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_CHECKLOCKTIMEVERIFY " ) {
// console . log ( " before acting: " );
// console . log ( params );
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input = params[ params . length - 1 ];
if ( typeof input !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
if ( input > locktime ) {
input_whose_script_failed_CLTV_due_to_not_enough_time_passed = index;
return;
}
if ( input < 0 || ( input = == 0 && testnet_generator . isZeroNegative ( input ) ) ) {
2024-11-21 15:18:48 -05:00
input_whose_script_uses_a_negative_number_for_CLTV_or_CSV = index;
2024-11-21 01:24:10 -05:00
return ;
}
if ( input > = 500000000 && locktime < 500000000 || locktime > = 500000000 && input < 500000000 ) {
input_whose_locktime_value_does_not_match_the_CLTV_value = index;
return ;
}
if ( sequence = == -2147483647 ) {
input_whose_script_uses_CLTV_but_the_sequence_number_is_maxxed_out = index;
return ;
}
// console . log ( " results: " );
// console . log ( params );
}
else if ( ! ifs_stack . includes ( false ) && element = == " OP_CHECKSEQUENCEVERIFY " ) {
if ( ! params . length ) {
input_whose_stack_is_too_small = index;
return ;
}
var input = params[ params . length - 1 ];
if ( typeof input !== " number " ) {
input_whose_script_treats_a_string_as_an_integer = index;
return ;
}
2024-11-21 15:18:48 -05:00
if ( first_bit ) {
input_whose_script_failed_CSV = index;
return ;
}
if ( sequence_value < input ) {
2024-11-21 01:24:10 -05:00
input_whose_script_failed_CSV = index;
return ;
}
if ( input < 0 || ( input = == 0 && testnet_generator . isZeroNegative ( input ) ) ) {
2024-11-21 15:18:48 -05:00
input_whose_script_uses_a_negative_number_for_CLTV_or_CSV = index;
2024-11-21 01:24:10 -05:00
return ;
}
if ( input > = 500000000 && locktime < 500000000 || locktime > = 500000000 && input < 500000000 ) {
input_whose_locktime_value_does_not_match_the_CLTV_value = index;
return ;
}
if ( sequence = == " ffffffff " ) {
input_whose_script_uses_CLTV_but_the_sequence_number_is_maxxed_out = index;
return ;
}
}
else console . log ( " the previous opcode or element was unsupported or we are in an if_branch that isn ' t supposed to execute " );
script_as_asm . splice ( 0 , 1 );
if ( script_as_asm . length ) await exeloop ();
}
await exeloop ();
console . log ( ' done ' );
if ( params . length != 1 ) {
input_whose_script_left_multiple_elements_on_the_stack = index;
console . log ( params );
break ;
}
if ( params [ 0 ] !== 1 ) {
input_whose_script_ended_on_false = index;
console . log ( params );
break ;
}
}
}
}
// TODO: ensure there are never more than 1000 stack elements
// including the elements on the alt_stack
// TODO: ensure stack elements are 520 bytes or fewer
// TODO: ensure outputs are valid
// TODO: ensure outputs sum to less than or equal to inputs
// TODO: if the tx is non-standard , warn the user but give an option to anull standardness rules
if ( input_whose_script_failed_CLTV_due_to_not_enough_time_passed > = 0 ) return `that tx was invalid because input ${input_whose_script_failed_CLTV_due_to_not_enough_time_passed} has a script containing OP_CHECKLOCKTIMEVERIFY and not enough time passed or the locktime field was set lower than is required`;
2024-11-21 15:18:48 -05:00
if ( input_whose_script_uses_a_negative_number_for_CLTV_or_CSV >= 0 ) return `that tx was invalid because input ${input_whose_script_uses_a_negative_number_for_CLTV_or_CSV} has a script containing OP_CHECKLOCKTIMEVERIFY and tried to pass a negative number to it`;
2024-11-21 01:24:10 -05:00
if ( input_whose_locktime_value_does_not_match_the_CLTV_value >= 0 ) return `that tx was invalid because input ${input_whose_locktime_value_does_not_match_the_CLTV_value} has a locktime value does not match the value passed to OP_CHECKLOCKTIMEVERIFY in the script -- i.e. one of them a blockheight and the other is a unix timestamp`;
if ( input_whose_script_uses_CLTV_but_the_sequence_number_is_maxxed_out >= 0 ) return `that tx was invalid because input ${input_whose_script_uses_CLTV_but_the_sequence_number_is_maxxed_out} uses OP_CHECKLOCKTIMEVERIFY in the script but its sequence number is set to 0xffffffff -- this is not allowed, try setting the sequence number to 0xfffffffd or less`;
if ( input_whose_script_failed_CSV >= 0 ) return `that tx was invalid because input ${input_whose_script_failed_CSV} uses OP_CHECKSEQUENCEVERIFY in the script but its value is lower than the sequence number of that input`;
if ( input_with_if_branch_preceded_by_something_other_than_1_or_0 >= 0 ) return `that tx was invalid because input ${input_with_if_branch_preceded_by_something_other_than_1_or_0} had an if_branch that was preceded by something other than a 1 or a 0`;
if ( input_with_insufficient_witness_length >= 0 ) return `that tx was invalid because input ${input_with_insufficient_witness_length} needed at least one element on the witness stack and it wasn't there`;
if ( input_with_invalid_sig >= 0 ) return `that tx was invalid because input ${input_with_invalid_sig} had an invalid sig`;
if ( input_whose_stack_is_too_small >= 0 ) return `that tx was invalid because input ${input_whose_stack_is_too_small} ran into an opcode that required more elements on the stack than were available there`;
if ( input_with_improperly_formatted_sig >= 0 ) return `that tx was invalid because input ${input_with_improperly_formatted_sig} ran into an opcode that required a signature, and the signature passed to it was improperly formatted`;
if ( input_with_improperly_formatted_element >= 0 ) return `that tx was invalid because input ${input_with_improperly_formatted_element} had a non-opcode element on the witness stack that was formatted as something other than a hex string`;
if ( input_whose_script_failed_a_verify_opcode >= 0 ) return `that tx was invalid because the script for input ${input_whose_script_failed_a_verify_opcode} had an opcode with "verify" in it (one of these: OP_CHECKSIGVERIFY, OP_CHECKMULTISIGVERIFY, OP_EQUALVERIFY, OP_NUMEQUALVERIFY, or OP_VERIFY) and "verify" failed`;
if ( input_with_op_nop >= 0 ) return `that tx was invalid because the script for input ${input_with_op_nop} had an op_nop opcode, which is non-standard`;
if ( input_whose_script_left_multiple_elements_on_the_stack >= 0 ) return `that tx was invalid because input ${input_whose_script_left_multiple_elements_on_the_stack} left multiple elements on the stack, and at the end of script execution there is only supposed to be 1 element left on the stack`;
if ( input_whose_script_ended_on_false >= 0 ) return `that tx was invalid because input ${input_whose_script_ended_on_false} ended with a value other than 1 on the stack`;
if ( input_with_script_containing_op_return >= 0 ) return `that tx was invalid because input ${input_with_script_containing_op_return} ran into an op_return while executing the script`;
if ( input_whose_script_treats_a_string_as_an_integer >= 0 ) return `that tx was invalid because input ${input_whose_script_treats_a_string_as_an_integer} has a script that tried to treat a string as an integer`;
if ( input_whose_script_tried_to_get_a_nonexistent_element_from_the_stack >= 0 ) return `that tx was invalid because input ${input_whose_script_tried_to_get_a_nonexistent_element_from_the_stack} has a script that tried to get a nonexistent element from the stack`;
2024-11-21 12:24:13 -05:00
if ( input_whose_script_failed_relative_timelock >= 0 ) return `that tx was invalid because input ${input_whose_script_failed_relative_timelock} has a non-zero relative timelock in its sequence number, which means the tx must wait a certain amount of time after that input utxo gets mined, and not enough time has passed`;
2024-11-21 01:24:10 -05:00
}
var broadcast = async txhex => {
if ( !txhex || typeof txhex === "object" ) txhex = $( '.tx_broadcaster' ).value;
var reason_why_tx_is_invalid = await validateTx( txhex );
if ( reason_why_tx_is_invalid ) return alert( reason_why_tx_is_invalid );
testnet_generator.mempool.push( txhex );
var txid = tapscript.Tx.util.getTxid( txhex );
$( '.broadcast_success_label' ).classList.remove( "hidden" );
$( '.broadcast_success_txid' ).innerText = txid;
$( '.broadcast_success_txid' ).setAttribute( "data-txhex", txhex );
$( '.broadcast_success_txid' ).setAttribute( "data-idx", 1 );
$( '.broadcast_success_txid' ).onclick = () => {
showPage( 'tx_explorer' );
testnet_generator.showSoloTx( txid, txhex, 1 );
};
var tx = tapscript.Tx.decode( txhex );
var txid = tapscript.Tx.util.getTxid( tx );
var i; for ( i=0; i< tx.vin.length ; i ++ ) {
var input = tx.vin[ i ];
var index = i;
var input_exists = false;
var input_id = `${input[ " txid " ]} _ ${ input [ " vout " ]}`;
testnet_generator . utxo_set [ input_id ]. spent = true;
var addy = testnet_generator.utxo_set[ input_id ]. addy ;
testnet_generator . addresses [ addy ][ " sent " ]. push ( `${ txid } _ ${ i }` );
}
tx . vout . forEach ( ( output , vout ) = > {
testnet_generator.utxo_set[ `${txid}_${vout}` ] = {
txid,
vout,
amnt: Number( output[ "value" ] ),
addy: tapscript.Address.fromScriptPubKey( output[ "scriptPubKey" ], "testnet" ),
spent: false,
txhex,
idx: undefined,
block_created: undefined,
block_spent: undefined,
}
});
var i; for ( i=0; i< tx.vout.length ; i ++ ) {
var output = tx.vout[ i ];
var addy = tapscript.Address.fromScriptPubKey( output [ " scriptPubKey " ], " testnet " );
var output_id = `${txid}_${i}`;
if ( !( addy in testnet_generator . addresses ) ) testnet_generator . addresses [ addy ] = {
sent: [],
received: [],
}
testnet_generator . addresses [ addy ][ " received " ]. push ( output_id );
}
calcBalance ();
calcMempool ();
}
$( '. broadcast_btn ' ). onclick = broadcast;
var calcMempool = () = > $( '.txs_in_mempool' ).innerText = testnet_generator.mempool.length;
var runScript = () => {
var keypairs = [testnet_generator.getAddy(), testnet_generator.getAddy(), testnet_generator.getAddy()];
var script = [
"OP_3",
2024-11-21 15:18:48 -05:00
"OP_CHECKSEQUENCEVERIFY",
2024-11-21 01:24:10 -05:00
"OP_DROP",
];
keypairs.forEach( ( keypair, idx ) => {
if ( !idx ) script.push( testnet_generator.getPubkey( keypair[ 1 ] ), "OP_CHECKSIG" );
else script.push( testnet_generator.getPubkey( keypair[ 1 ] ), "OP_CHECKSIGADD" );
});
script.push( "OP_1", "OP_GREATERTHANOREQUAL" );
console.log( "script:" );
console.log( script );
var tree = [tapscript.Tap.encodeScript( script )];
var tapleaf = tree[ 0 ];
var backup_pubkey = "ab".repeat( 32 );
var [ tpubkey, cblock ] = tapscript.Tap.getPubKey( backup_pubkey, { target: tapleaf });
var addy = tapscript.Address.p2tr.fromPubKey( tpubkey, 'testnet' );
console.log( 'send money to this address:' );
console.log( addy );
var txhex = sendMoney();
if ( !testnet_generator.isValidHex( txhex ) ) return;
var txid = tapscript.Tx.util.getTxid( txhex );
var vout = 0;
var amnt = Number( prompt( `enter the amount you sent` ) );
// var txid = prompt( `enter the txid` );
// var vout = Number( prompt( `and the vout` ) );
// var amnt = Number( prompt( `and the amnt` ) );
var txdata = tapscript.Tx.create({
vin: [testnet_generator.getVin( txid, vout, amnt, addy )],
vout: [testnet_generator.getVout( amnt - 500, $( '.your_addy' ).innerText )],
});
2024-11-21 15:18:48 -05:00
txdata.vin[ 0 ].sequence = 3;
2024-11-21 01:24:10 -05:00
txdata.vin.forEach( ( input, index ) => {
// var sig_0 = tapscript.Signer.taproot.sign( keypairs[ 0 ][ 1 ], txdata, 0, { extension: tapleaf }).hex;
// sig_0 = ( BigInt( '0x' + sig_0 ) - BigInt( '0x01' ) ).toString( 16 );
// if ( sig_0.length % 2 ) sig_0 = "0" + sig_0;
var sig_0 = "";
var sig_1 = tapscript.Signer.taproot.sign( keypairs[ 1 ][ 1 ], txdata, 0, { extension: tapleaf }).hex;
var sighash = tapscript.Signer.taproot.hash( txdata, 0, { extension: tapleaf }).hex;
// var sig_2 = tapscript.Signer.taproot.sign( keypairs[ 2 ][ 1 ], txdata, 0, { extension: tapleaf }).hex;
var sig_2 = "";
// console.log( 44, "pubkey:", tapscript.Address.toScriptPubKey( keypairs[ 1 ][ 0 ] )[ 1 ], "sighash:", sighash, "sig:", sig_1 );
txdata.vin[ index ].witness = [ sig_2, sig_1, sig_0, script, cblock ];
});
console.log( tapscript.Tx.encode( txdata ).hex );
}
$( '.auto_generate' ).onchange = () => {
autogen_value = $( '.auto_generate' ).value;
if ( autogen_value.includes( "---" ) ) $( '.auto_go' ).disabled = true;
else $( '.auto_go' ).removeAttribute( "disabled" );
}
var autogen_num = null;
var autogo = false;
$( '.auto_go' ).onclick = () => {
if ( $( '.auto_go' ).innerText === "Go" ) {
if ( $( '.auto_go' ).disabled ) return;
autogen_value = $( '.auto_generate' ).value;
var num = 500;
if ( autogen_value === "1 second" ) num = 1_000;
if ( autogen_value === "3 seconds" ) num = 3_000;
if ( autogen_value === "5 seconds" ) num = 5_000;
if ( autogen_value === "10 seconds" ) num = 10_000;
if ( autogen_value === "30 seconds" ) num = 30_000;
if ( autogen_value === "1 minute" ) num = 60_000;
if ( autogen_value === "2 minutes" ) num = 120_000;
if ( autogen_value === "5 minutes" ) num = 300_000;
if ( autogen_value === "10 minutes" ) num = 600_000;
autogen_num = num;
autogo = true;
autoLoop();
$( '.auto_go' ).innerText = "Stop";
} else {
autogo = false;
$( '.auto_go' ).innerText = "Go";
}
}
var autoLoop = async () => {
makeBlock();
var keep_going = autogo;
await testnet_generator.waitSomeTime( autogen_num );
if ( !autogo ) keep_going = false;
if ( keep_going ) autoLoop();
}
var showPage = page => {
$( '.block_explorer' ).classList.add( "hidden" );
$( '.tx_explorer' ).classList.add( "hidden" );
$( '.addy_explorer' ).classList.add( "hidden" );
$( '.stuff_thats_not_an_explorer' ).classList.add( "hidden" );
$( `.${page}` ).classList.remove( "hidden" );
}
(async()=>{
$( '.auto_generate' ).value = "--- Auto generate ---";
$( '.auto_go' ).disabled = true;
$( '.tx_broadcaster' ).value = "";
window.scrollTo( 0, 0 );
})();
</ script >
</ body >
</ html >