"Invalid Schnorr signature" with Taproot script spend

Wait 5 sec.

I'm attempting to sweep funds from an address generated with the following descriptor:tr(ff13a3311d3e14239bcbb9dfd5d304f4b57c32c0b35d313cb5255f93d7b2dc68,and_v(v:pk(b064bd37b71b7c39355f866e022287097757b05bb1790ecffc5de5fbf07cff69),sha256(a02e93b3dce9cb6031055905d94b04516819ef8fdae4c498777350469e6352dd)))#pygtzretI've set the funds up on a local regtest using nigiri to test this situation and am using rust-bitcoin to construct the transaction. As far as I can tell, I am constructing the witness for the input correctly. However, when checking the output transaction using testmempoolaccept, I continually get mandatory-script-verify-flag-failed (Invalid Schnorr signature). The key I'm using appears to be right. I saw the comment under this answer suggesting it was the serialization of my signature even though I'm using the default. However, using what now seems to be at bitcoin::taproot::Signature doesn't solve my issue.I'm sure I'm doing something subtly wrong here. So any nudge in the correct direction would be greatly appreciated. My program in its entirety is below:use std::str::FromStr;use anyhow::{Result, anyhow, bail};use bitcoin::absolute::LockTime;use bitcoin::consensus::Encodable;use bitcoin::hashes::{Hash, sha256};use bitcoin::hex::prelude::*;use bitcoin::key::Keypair;use bitcoin::secp256k1::{Message, Secp256k1};use bitcoin::sighash::{Prevouts, SighashCache};use bitcoin::transaction::Version;use bitcoin::{ Address, Amount, Network, OutPoint, TapSighashType, Transaction, TxIn, TxOut, Witness, XOnlyPublicKey, taproot,};use miniscript::Descriptor;const DESCRIPTOR: &str = "tr(ff13a3311d3e14239bcbb9dfd5d304f4b57c32c0b35d313cb5255f93d7b2dc68,and_v(v:pk(b064bd37b71b7c39355f866e022287097757b05bb1790ecffc5de5fbf07cff69),sha256(a02e93b3dce9cb6031055905d94b04516819ef8fdae4c498777350469e6352dd)))#pygtzret";const PREIMAGE: &str = "366d2eff102fd290e7be0226f3dd746f4657bce85f08eeef55d0a7777d1a313f";const PRIVATE_KEY: &str = "0301e0480b374b32851a9462db29dc19fe830a7f7d7a88b81612b9d42099c0ae";fn main() -> Result { let Descriptor::Tr(taproot) = Descriptor::::from_str(DESCRIPTOR)? else { bail!("not a taproot descriptor"); }; assert_eq!( taproot.address(Network::Regtest).to_string(), "bcrt1prg82xfnv265zvl0mt6q2rr39mpmskrf2nyum4hqnmdwen3kzsn3slts4sh" ); let total = Amount::from_str("0.0025 BTC")?; let previous_output = OutPoint::from_str("44afabb1b631d2cd265910dc6befef7e3962df486ad4a4a12f83668c58c9c22c:0")?; let txin = TxIn { previous_output, ..Default::default() }; let to_address = Address::from_str("bcrt1q9th3z6mknxp60avl9xq8vac05q4xgvygx7r7jz")? .require_network(Network::Regtest)?; let txout = TxOut { value: Amount::ZERO, script_pubkey: to_address.script_pubkey(), }; let outputs = vec![txout]; let mut tx = Transaction { version: Version::TWO, lock_time: LockTime::ZERO, input: vec![txin], output: outputs.clone(), }; // Basic 5 sats/vbyte fee let fee = Amount::from_sat(tx.vsize() as u64 * 5); tx.output[0].value = total - fee; let preimage: [u8; 32] = FromHex::from_hex(PREIMAGE)?; let preimage_hash = sha256::Hash::hash(&preimage); assert!( preimage_hash.to_string() == "a02e93b3dce9cb6031055905d94b04516819ef8fdae4c498777350469e6352dd" ); let secp = Secp256k1::new(); let keypair = Keypair::from_seckey_str(&secp, PRIVATE_KEY)?; let public_key = XOnlyPublicKey::from(keypair.public_key()); assert_eq!( public_key.to_string(), "b064bd37b71b7c39355f866e022287097757b05bb1790ecffc5de5fbf07cff69" ); let spend_info = taproot.spend_info(); let leaf = spend_info .leaves() .next() .ok_or(anyhow!("taptree leaf missing"))?; let mut sighash_cache = SighashCache::new(&tx); let prevouts = Prevouts::All(&outputs); let sighash = sighash_cache.taproot_script_spend_signature_hash( 0, &prevouts, leaf.leaf_hash(), TapSighashType::Default, )?; let message = Message::from_digest(*sighash.as_ref()); let signature = taproot::Signature { signature: secp.sign_schnorr(&message, &keypair), sighash_type: TapSighashType::Default, }; let mut witness = Witness::new(); witness.push(preimage); witness.push(signature.serialize()); witness.push(leaf.script()); witness.push(leaf.into_control_block().serialize()); tx.input[0].witness = witness; let mut bytes = Vec::new(); tx.consensus_encode(&mut bytes)?; println!("{}", bytes.to_lower_hex_string()); Ok(())}