Last active
December 10, 2024 12:47
-
-
Save supertestnet/ad0ce4f92d1eecfc607b0b46afed7228 to your computer and use it in GitHub Desktop.
CTV Hash Generator in Javascript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//dependencies: | |
//https://unpkg.com/@cmdcode/[email protected] | |
var 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; | |
}); | |
} | |
var hexToBytes = hex => Uint8Array.from( hex.match( /.{1,2}/g ).map( byte => parseInt( byte, 16 ) ) ); | |
var reverseHexString = s => s.match(/[a-fA-F0-9]{2}/g).reverse().join(''); | |
var getStandardTemplateHash = async ( rawtx, inputnum ) => { | |
var tx = tapscript.Tx.decode( rawtx ); | |
var ctv_hash_preimage = ""; | |
//add the tx version number to the ctv preimage | |
var version = tx.version.toString( 16 ); | |
version = version.padStart( 8, "0" ); | |
version = reverseHexString( version ); | |
ctv_hash_preimage = ctv_hash_preimage + version; | |
//add the tx locktime to the ctv preimage | |
var locktime = tx.locktime.toString( 16 ); | |
locktime = locktime.padStart( 8, "0" ); | |
var locktime = reverseHexString( locktime ); | |
ctv_hash_preimage = ctv_hash_preimage + locktime; | |
//go through each input and concatenate the scriptSigs, if any; then hash them and add the hash to the ctv preimage | |
var vin = tx.vin || []; | |
var scriptsigs = []; | |
if ( vin.length ) { | |
vin.forEach( ( input, index ) => { | |
var length = ( input.scriptSig.length / 2 ).toString( 16 ); | |
if ( length.length % 2 ) length = "0" + length; | |
length = reverseHexString( length ); | |
//I'm not sure why I have to prefix fd to a 2-byte scriptsig length, but that made it match the test cases I was testing against, | |
//which are available here: https://github.com/jamesob/simple-ctv-vault/blob/master/ctvhash-test-vectors.json | |
if ( length.length === 4 ) length = "fd" + length; | |
//I'm also not sure why ff turns into fdff00, but that's what it does in the test vectors | |
if ( length === "ff" ) length = "fdff00"; | |
scriptsigs.push( length + input.scriptSig ); | |
}); | |
} | |
var scriptsigs_has_nonempty_elements = false; | |
scriptsigs.every( scriptsig => { | |
if ( scriptsig !== "00" ) { | |
scriptsigs_has_nonempty_elements = true; | |
return; | |
} | |
return true; | |
}); | |
if ( !scriptsigs_has_nonempty_elements ) scriptsigs = []; | |
var scriptsigs_bytes = []; | |
scriptsigs.forEach( scriptsig => scriptsigs_bytes.push( hexToBytes( scriptsig ) ) ); | |
var all_scriptsigs_together = []; | |
scriptsigs_bytes.forEach( scriptsig => all_scriptsigs_together = [ ...all_scriptsigs_together, ...scriptsig ] ); | |
scriptsigs = Uint8Array.from( all_scriptsigs_together ); | |
if ( scriptsigs.length ) var hash = await sha256( scriptsigs ); | |
else var hash = ""; | |
ctv_hash_preimage = ctv_hash_preimage + hash; | |
//add the number of inputs to the ctv preimage | |
var num_of_inputs = reverseHexString( tx.vin.length.toString( 16 ).padStart( 8, "0" ) ); | |
ctv_hash_preimage = ctv_hash_preimage + num_of_inputs; | |
//concatenate the sequence numbers of every input, hash them, and add the hash to the ctv preimage | |
var sequences = ""; | |
tx.vin.forEach( input => sequences = sequences + reverseHexString( input.sequence ) ); | |
sequences_hash = await sha256( hexToBytes( sequences ) ); | |
ctv_hash_preimage = ctv_hash_preimage + sequences_hash; | |
//add the number of outputs to the ctv preimage | |
var num_of_outputs = reverseHexString( tx.vout.length.toString( 16 ).padStart( 8, "0" ) ); | |
ctv_hash_preimage = ctv_hash_preimage + num_of_outputs; | |
//concatenate the outputs, hash them, and add the hash to the ctv preimage | |
var outputs_preimage = ""; | |
tx.vout.forEach( output => { | |
var value = output.value.toString( 16 ); | |
value = value.padStart( 16, "0" ); | |
value = reverseHexString( value ); | |
outputs_preimage = outputs_preimage + value; | |
var length = ( output.scriptPubKey.length / 2 ).toString( 16 ); | |
if ( length % 2 ) length = "0" + length; | |
outputs_preimage = outputs_preimage + length; | |
outputs_preimage = outputs_preimage + output.scriptPubKey; | |
}); | |
var outputs_hash = await sha256( hexToBytes( outputs_preimage ) ); | |
ctv_hash_preimage = ctv_hash_preimage + outputs_hash; | |
//add the index number of the input being spent to the ctv preimage | |
var inputnum = reverseHexString( inputnum.toString( 16 ).padStart( 8, "0" ) ); | |
ctv_hash_preimage = ctv_hash_preimage + inputnum; | |
//hash the ctv preimage | |
var ctv_hash = await sha256( hexToBytes( ctv_hash_preimage ) ); | |
//return the ctv hash | |
return ctv_hash; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
🙌🏻 good work