@@ -13,11 +13,11 @@ use bitcoin::{ Amount, TxOut, WPubkeyHash,
1313 blockdata:: witness:: Witness ,
1414 Script , ScriptBuf , XOnlyPublicKey , PublicKey ,
1515 sighash:: { SighashCache , TapSighashType , Prevouts , TapSighash } ,
16- taproot:: { LeafVersion , TapLeafHash , TapNodeHash , TapTree , ScriptLeaves , TaprootMerkleBranch , TaprootBuilder , TaprootSpendInfo , ControlBlock } ,
16+ taproot:: { LeafVersion , NodeInfo , TapLeafHash , TapNodeHash , TapTree , ScriptLeaves , TaprootMerkleBranch , TaprootBuilder , TaprootSpendInfo , ControlBlock } ,
1717 transaction:: { Transaction , Sequence }
1818} ;
1919
20- use bitcoin:: p2qrh:: { P2qrhScriptBuf , P2qrhBuilder , P2qrhSpendInfo , P2qrhControlBlock } ;
20+ use bitcoin:: p2qrh:: { P2qrhScriptBuf , P2qrhBuilder , P2qrhSpendInfo , P2qrhControlBlock , QuantumRootHash , P2QRH_LEAF_VERSION } ;
2121
2222use data_structures:: { SpendDetails , UtxoReturn , TaptreeReturn } ;
2323
@@ -86,24 +86,44 @@ pub fn create_p2qrh_multi_leaf_taptree() -> TaptreeReturn {
8686 let p2qrh_builder: P2qrhBuilder = P2qrhBuilder :: with_huffman_tree ( huffman_entries) . unwrap ( ) ;
8787
8888 let p2qrh_spend_info: P2qrhSpendInfo = p2qrh_builder. clone ( ) . finalize ( ) . unwrap ( ) ;
89- let quantum_root: TapNodeHash = p2qrh_spend_info. quantum_root . unwrap ( ) ;
90- info ! ( "keypair_of_interest: \n \t secret_bytes: {} \n \t pubkey: {} \n \t quantum_root: {} " ,
89+ let quantum_root: QuantumRootHash = p2qrh_spend_info. quantum_root . unwrap ( ) ;
90+ info ! ( "keypair_of_interest: \n \t secret_bytes: {} \n \t pubkey: {}" ,
9191 hex:: encode( keypair_of_interest. unwrap( ) . 0 . secret_bytes( ) ) , // secret_bytes returns big endian
9292 hex:: encode( keypair_of_interest. unwrap( ) . 1 . serialize( ) ) , // serialize returns little endian
93- quantum_root ) ;
94-
93+ ) ;
94+
9595 let tap_tree: TapTree = p2qrh_builder. clone ( ) . into_inner ( ) . try_into_taptree ( ) . unwrap ( ) ;
9696 let mut script_leaves: ScriptLeaves = tap_tree. script_leaves ( ) ;
9797 let script_leaf = script_leaves
9898 . find ( |leaf| leaf. script ( ) == script_buf_of_interest. as_ref ( ) . unwrap ( ) . as_script ( ) )
9999 . expect ( "Script leaf not found" ) ;
100+
101+ let merkle_root_node_info: NodeInfo = p2qrh_builder. clone ( ) . into_inner ( ) . try_into_node_info ( ) . unwrap ( ) ;
102+ let merkle_root: TapNodeHash = merkle_root_node_info. node_hash ( ) ;
103+
104+ let leaf_hash: TapLeafHash = TapLeafHash :: from_script ( script_leaf. script ( ) , LeafVersion :: from_consensus ( P2QRH_LEAF_VERSION ) . unwrap ( ) ) ;
105+
106+ // Convert leaf hash to big-endian for display (like Bitcoin Core)
107+ let mut leaf_hash_bytes = leaf_hash. as_raw_hash ( ) . to_byte_array ( ) . to_vec ( ) ;
108+ leaf_hash_bytes. reverse ( ) ;
109+
110+ info ! ( "leaf_hash: {}, merkle_root: {}, quantum_root: {}" ,
111+ hex:: encode( leaf_hash_bytes) ,
112+ merkle_root,
113+ quantum_root) ;
114+
100115 let leaf_script = script_leaf. script ( ) ;
101116 let merkle_branch: & TaprootMerkleBranch = script_leaf. merkle_branch ( ) ;
117+
102118 info ! ( "Leaf script: {}, merkle branch: {:?}" , leaf_script, merkle_branch) ;
103119
104120 let control_block: P2qrhControlBlock = P2qrhControlBlock {
105121 merkle_branch : merkle_branch. clone ( ) ,
106122 } ;
123+
124+ // Not a requirement but useful to demonstrate what Bitcoin Core does as the verifier when spending from a p2qrh UTXO
125+ control_block. verify_script_in_quantum_root_path ( leaf_script, quantum_root) ;
126+
107127 let control_block_hex: String = hex:: encode ( control_block. serialize ( ) ) ;
108128
109129 return TaptreeReturn {
@@ -132,6 +152,7 @@ pub fn create_p2tr_multi_leaf_taptree(p2tr_internal_pubkey_hex: String) -> Taptr
132152 // When spending via script path, the verifier needs to know whether the output key has an even or odd Y-coordinate to properly reconstruct & verify the internal key.
133153 // The internal key can be recovered from the output key using the parity bit and the merkle root.
134154 let output_key_parity: Parity = p2tr_spend_info. output_key_parity ( ) ;
155+ let output_key: XOnlyPublicKey = p2tr_spend_info. output_key ( ) . into ( ) ;
135156
136157 info ! ( "keypair_of_interest: \n \t secret_bytes: {} \n \t pubkey: {} \n \t merkle_root: {}" ,
137158 hex:: encode( keypair_of_interest. unwrap( ) . 0 . secret_bytes( ) ) , // secret_bytes returns big endian
@@ -145,14 +166,19 @@ pub fn create_p2tr_multi_leaf_taptree(p2tr_internal_pubkey_hex: String) -> Taptr
145166 . expect ( "Script leaf not found" ) ;
146167 let leaf_script = script_leaf. script ( ) . to_hex_string ( ) ;
147168 let merkle_branch: & TaprootMerkleBranch = script_leaf. merkle_branch ( ) ;
169+ debug ! ( "Leaf script: {}, merkle branch: {:?}" , leaf_script, merkle_branch) ;
170+
148171 let control_block: ControlBlock = ControlBlock {
149172 leaf_version : LeafVersion :: TapScript ,
150173 output_key_parity : output_key_parity,
151174 internal_key : internal_xonly_pubkey,
152175 merkle_branch : merkle_branch. clone ( ) ,
153176 } ;
154177 let control_block_hex: String = hex:: encode ( control_block. serialize ( ) ) ;
155- debug ! ( "Leaf script: {}, merkle branch: {:?}" , leaf_script, merkle_branch) ;
178+
179+ // Not a requirement but useful to demonstrate what Bitcoin Core does as the verifier when spending from a p2tr UTXO
180+ let verify: bool = verify_taproot_commitment ( control_block_hex. clone ( ) , output_key, script_leaf. script ( ) ) ;
181+ info ! ( "verify_taproot_commitment: {}" , verify) ;
156182
157183 return TaptreeReturn {
158184 leaf_script_priv_key_hex : hex:: encode ( keypair_of_interest. unwrap ( ) . 0 . secret_bytes ( ) ) ,
@@ -443,3 +469,20 @@ pub fn verify_schnorr_signature(mut signature: Signature, message: Message, pubk
443469 }
444470 is_valid
445471}
472+
473+ /* 1. Re-constructs merkle_root from merkle_path (found in control_block) and provided script.
474+ 2. Determines the parity of the output key via the control byte (found in the control block).
475+ - the parity bit indicates whether the output key has an even or odd Y-coordinate
476+ 3. Computes the tap tweak hash using the internal key and reconstructed merkle root.
477+ - tap_tweak_hash = tagged_hash("TapTweak", internal_key || merkle_root)
478+ 4. Verifies that the provided output key can be derived from the internal key using the tweak.
479+ - tap_tweak_hash = tagged_hash("TapTweak", internal_key || merkle_root)
480+ 5. This proves the script is committed to in the taptree described by the output key.
481+ */
482+ pub fn verify_taproot_commitment ( control_block_hex : String , output_key : XOnlyPublicKey , script : & Script ) -> bool {
483+
484+ let control_block_bytes = hex:: decode ( control_block_hex) . unwrap ( ) ;
485+ let control_block: ControlBlock = ControlBlock :: decode ( & control_block_bytes) . unwrap ( ) ;
486+
487+ return control_block. verify_taproot_commitment ( & SECP , output_key, script) ;
488+ }
0 commit comments