@@ -131,20 +131,23 @@ pub fn derive_pubkey_from_input<C: secp256k1::Verification>(
131131/// - `psbt`: The PSBT containing the transaction and inputs
132132/// - `input_index`: The index of the input to verify
133133/// - `public_key`: The compressed public key to verify the signature for
134+ /// - `cache`: Mutable reference to a SighashCache for computing sighash (can be reused for bulk verification)
134135///
135136/// # Returns
136137/// - `Ok(true)` if a valid Schnorr signature exists for the public key
137138/// - `Ok(false)` if no signature exists or verification fails
138139/// - `Err(String)` if required data is missing or computation fails
139- pub fn verify_taproot_script_signature < C : secp256k1:: Verification > (
140+ pub fn verify_taproot_script_signature <
141+ C : secp256k1:: Verification ,
142+ T : std:: borrow:: Borrow < miniscript:: bitcoin:: Transaction > ,
143+ > (
140144 secp : & secp256k1:: Secp256k1 < C > ,
141145 psbt : & miniscript:: bitcoin:: psbt:: Psbt ,
142146 input_index : usize ,
143147 public_key : miniscript:: bitcoin:: CompressedPublicKey ,
148+ cache : & mut miniscript:: bitcoin:: sighash:: SighashCache < T > ,
144149) -> Result < bool , String > {
145- use miniscript:: bitcoin:: {
146- hashes:: Hash , sighash:: Prevouts , sighash:: SighashCache , TapLeafHash , XOnlyPublicKey ,
147- } ;
150+ use miniscript:: bitcoin:: { hashes:: Hash , sighash:: Prevouts , TapLeafHash , XOnlyPublicKey } ;
148151
149152 let input = & psbt. inputs [ input_index] ;
150153
@@ -160,11 +163,8 @@ pub fn verify_taproot_script_signature<C: secp256k1::Verification>(
160163 for ( ( sig_pubkey, leaf_hash) , signature) in & input. tap_script_sigs {
161164 if sig_pubkey == & x_only_key {
162165 // Found a signature for this public key, now verify it
163- let mut cache = SighashCache :: new ( & psbt. unsigned_tx ) ;
164-
165166 // Compute taproot script spend sighash
166- let prevouts = super :: p2tr_musig2_input:: collect_prevouts ( psbt)
167- . map_err ( |e| format ! ( "Failed to collect prevouts: {}" , e) ) ?;
167+ let prevouts = collect_prevouts ( psbt) ?;
168168
169169 // Find the script for this leaf hash
170170 // tap_scripts is keyed by ControlBlock, so we need to find the matching entry
@@ -206,6 +206,97 @@ pub fn verify_taproot_script_signature<C: secp256k1::Verification>(
206206 Ok ( false )
207207}
208208
209+ /// Collect all prevouts (funding outputs) from PSBT inputs
210+ ///
211+ /// This helper extracts the TxOut for each input from either witness_utxo or non_witness_utxo.
212+ /// Required for computing sighashes in taproot transactions.
213+ ///
214+ /// # Arguments
215+ /// - `psbt`: The PSBT containing the inputs
216+ ///
217+ /// # Returns
218+ /// - `Ok(Vec<TxOut>)` with all prevouts
219+ /// - `Err(String)` if any input is missing UTXO data
220+ pub fn collect_prevouts (
221+ psbt : & miniscript:: bitcoin:: psbt:: Psbt ,
222+ ) -> Result < Vec < miniscript:: bitcoin:: TxOut > , String > {
223+ let tx = & psbt. unsigned_tx ;
224+ psbt. inputs
225+ . iter ( )
226+ . enumerate ( )
227+ . map ( |( i, input) | {
228+ if let Some ( witness_utxo) = & input. witness_utxo {
229+ Ok ( witness_utxo. clone ( ) )
230+ } else if let Some ( non_witness_utxo) = & input. non_witness_utxo {
231+ let output_index = tx. input [ i] . previous_output . vout as usize ;
232+ non_witness_utxo
233+ . output
234+ . get ( output_index)
235+ . cloned ( )
236+ . ok_or_else ( || format ! ( "Output index {} out of bounds" , output_index) )
237+ } else {
238+ Err ( format ! ( "Missing UTXO data for input {}" , i) )
239+ }
240+ } )
241+ . collect ( )
242+ }
243+
244+ /// Verifies a Taproot key path signature for a given x-only public key in a PSBT input
245+ ///
246+ /// # Arguments
247+ /// - `secp`: Secp256k1 context for signature verification
248+ /// - `psbt`: The PSBT containing the transaction and inputs
249+ /// - `input_index`: The index of the input to verify
250+ /// - `x_only_key`: The x-only public key to verify the signature for
251+ /// - `cache`: Mutable reference to a SighashCache for computing sighash (can be reused for bulk verification)
252+ ///
253+ /// # Returns
254+ /// - `Ok(true)` if a valid Schnorr signature exists for the public key
255+ /// - `Ok(false)` if no signature exists or verification fails
256+ /// - `Err(String)` if required data is missing or computation fails
257+ pub fn verify_taproot_key_signature <
258+ C : secp256k1:: Verification ,
259+ T : std:: borrow:: Borrow < miniscript:: bitcoin:: Transaction > ,
260+ > (
261+ secp : & secp256k1:: Secp256k1 < C > ,
262+ psbt : & miniscript:: bitcoin:: psbt:: Psbt ,
263+ input_index : usize ,
264+ x_only_key : miniscript:: bitcoin:: XOnlyPublicKey ,
265+ cache : & mut miniscript:: bitcoin:: sighash:: SighashCache < T > ,
266+ ) -> Result < bool , String > {
267+ use miniscript:: bitcoin:: { hashes:: Hash , sighash:: Prevouts } ;
268+
269+ let input = & psbt. inputs [ input_index] ;
270+
271+ // Check if there's a taproot key path signature
272+ let sig = match & input. tap_key_sig {
273+ Some ( sig) => sig,
274+ None => return Ok ( false ) ,
275+ } ;
276+
277+ // Verify that the tap_internal_key matches the provided x_only_key
278+ match & input. tap_internal_key {
279+ Some ( tap_internal_key) if tap_internal_key == & x_only_key => { }
280+ Some ( _) => return Ok ( false ) , // Key mismatch
281+ None => return Ok ( false ) , // No tap_internal_key
282+ }
283+
284+ // Collect prevouts for taproot sighash
285+ let prevouts = collect_prevouts ( psbt) ?;
286+
287+ // Compute taproot key spend sighash
288+ let sighash = cache
289+ . taproot_key_spend_signature_hash ( input_index, & Prevouts :: All ( & prevouts) , sig. sighash_type )
290+ . map_err ( |e| format ! ( "Failed to compute taproot sighash: {}" , e) ) ?;
291+
292+ // Verify Schnorr signature
293+ let message = secp256k1:: Message :: from_digest ( sighash. to_byte_array ( ) ) ;
294+ match secp. verify_schnorr ( & sig. signature , & message, & x_only_key) {
295+ Ok ( ( ) ) => Ok ( true ) ,
296+ Err ( _) => Ok ( false ) ,
297+ }
298+ }
299+
209300/// Verifies an ECDSA signature for a given public key in a PSBT input (legacy/SegWit)
210301///
211302/// # Arguments
0 commit comments