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 ;
var input_whose_script_uses_a_negative_number_for_CLTV = - 1 ;
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 ;
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 ) => {
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 ();
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 ) ) ) {
input_whose_script_uses_a_negative_number_for_CLTV = index ;
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 ;
}
if ( input < sequence ) {
input_whose_script_failed_CSV = index ;
return ;
}
if ( input < 0 || ( input === 0 && testnet_generator . isZeroNegative ( input ) ) ) {
input_whose_script_uses_a_negative_number_for_CLTV = index ;
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` ;
if ( input_whose_script_uses_a_negative_number_for_CLTV >= 0 ) return `that tx was invalid because input ${ input_whose_script_uses_a_negative_number_for_CLTV } has a script containing OP_CHECKLOCKTIMEVERIFY and tried to pass a negative number to it` ;
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" ,
"OP_CHECKLOCKTIMEVERIFY" ,
"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 )],
});
txdata . locktime = 3 ;
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 >